Merge "Fix two problems related to data connectivity in the multi-SIM header" into qt-dev

This commit is contained in:
TreeHugger Robot
2019-05-08 18:31:39 +00:00
committed by Android (Google) Code Review
6 changed files with 419 additions and 7 deletions

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.network;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
/** Helper class to listen for changes in the enabled state of mobile data. */
public class MobileDataEnabledListener extends ContentObserver {
private Context mContext;
private Client mClient;
private int mSubId;
public interface Client {
void onMobileDataEnabledChange();
}
public MobileDataEnabledListener(Context context, Client client) {
super(new Handler());
mContext = context;
mClient = client;
mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
/** Starts listening to changes in the enabled state for data on the given subscription id. */
public void start(int subId) {
mSubId = subId;
Uri uri;
if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA);
} else {
uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + mSubId);
}
mContext.getContentResolver().registerContentObserver(uri, true /*notifyForDescendants*/,
this);
}
public int getSubId() {
return mSubId;
}
public MobileDataEnabledListener stop() {
mContext.getContentResolver().unregisterContentObserver(this);
return this;
}
@Override
public void onChange(boolean selfChange) {
mClient.onMobileDataEnabledChange();
}
}

View File

@@ -21,6 +21,9 @@ import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -35,6 +38,7 @@ import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.network.telephony.DataConnectivityListener;
import com.android.settings.network.telephony.MobileNetworkActivity;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -46,14 +50,18 @@ import java.util.Map;
* available if there are 2 or more subscriptions.
*/
public class SubscriptionsPreferenceController extends AbstractPreferenceController implements
LifecycleObserver, SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
LifecycleObserver, SubscriptionsChangeListener.SubscriptionsChangeListenerClient,
MobileDataEnabledListener.Client, DataConnectivityListener.Client {
private static final String TAG = "SubscriptionsPrefCntrlr";
private UpdateListener mUpdateListener;
private String mPreferenceGroupKey;
private PreferenceGroup mPreferenceGroup;
private SubscriptionManager mManager;
private ConnectivityManager mConnectivityManager;
private SubscriptionsChangeListener mSubscriptionsListener;
private MobileDataEnabledListener mDataEnabledListener;
private DataConnectivityListener mConnectivityListener;
// Map of subscription id to Preference
private Map<Integer, Preference> mSubscriptionPreferences;
@@ -89,20 +97,27 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
mPreferenceGroupKey = preferenceGroupKey;
mStartOrder = startOrder;
mManager = context.getSystemService(SubscriptionManager.class);
mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
mSubscriptionPreferences = new ArrayMap<>();
mSubscriptionsListener = new SubscriptionsChangeListener(context, this);
mDataEnabledListener = new MobileDataEnabledListener(context, this);
mConnectivityListener = new DataConnectivityListener(context, this);
lifecycle.addObserver(this);
}
@OnLifecycleEvent(ON_RESUME)
public void onResume() {
mSubscriptionsListener.start();
mDataEnabledListener.start(SubscriptionManager.getDefaultDataSubscriptionId());
mConnectivityListener.start();
update();
}
@OnLifecycleEvent(ON_PAUSE)
public void onPause() {
mSubscriptionsListener.stop();
mDataEnabledListener.stop();
mConnectivityListener.stop();
}
@Override
@@ -158,6 +173,19 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
mUpdateListener.onChildrenUpdated();
}
private boolean activeNetworkIsCellular() {
final Network activeNetwork = mConnectivityManager.getActiveNetwork();
if (activeNetwork == null) {
return false;
}
final NetworkCapabilities networkCapabilities = mConnectivityManager.getNetworkCapabilities(
activeNetwork);
if (networkCapabilities == null) {
return false;
}
return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
}
/**
* The summary can have either 1 or 2 lines depending on which services (calls, SMS, data) this
* subscription is the default for.
@@ -187,10 +215,10 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
if (subId == dataDefaultSubId) {
final TelephonyManager telMgrForSub = mContext.getSystemService(
TelephonyManager.class).createForSubscriptionId(subId);
final int dataState = telMgrForSub.getDataState();
if (dataState == TelephonyManager.DATA_CONNECTED) {
boolean dataEnabled = telMgrForSub.isDataEnabled();
if (dataEnabled && activeNetworkIsCellular()) {
line2 = mContext.getString(R.string.mobile_data_active);
} else if (!telMgrForSub.isDataEnabled()) {
} else if (!dataEnabled) {
line2 = mContext.getString(R.string.mobile_data_off);
} else {
line2 = mContext.getString(R.string.default_for_mobile_data);
@@ -231,6 +259,22 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
@Override
public void onSubscriptionsChanged() {
// See if we need to change which sub id we're using to listen for enabled/disabled changes.
int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
if (defaultDataSubId != mDataEnabledListener.getSubId()) {
mDataEnabledListener.stop();
mDataEnabledListener.start(defaultDataSubId);
}
update();
}
@Override
public void onMobileDataEnabledChange() {
update();
}
@Override
public void onDataConnectivityChange() {
update();
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.network.telephony;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
/** A helper class to listen to a few different kinds of connectivity changes that could be relevant
* to changes in which network is active, and whether the active network has internet data
* connectivity. */
public class DataConnectivityListener extends ConnectivityManager.NetworkCallback {
private Context mContext;
private ConnectivityManager mConnectivityManager;
private final NetworkRequest mNetworkRequest;
private Client mClient;
public interface Client {
void onDataConnectivityChange();
}
public DataConnectivityListener(Context context, Client client) {
mContext = context;
mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
mClient = client;
mNetworkRequest = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build();
}
public void start() {
mConnectivityManager.registerNetworkCallback(mNetworkRequest, this,
mContext.getMainThreadHandler());
}
public void stop() {
mConnectivityManager.unregisterNetworkCallback(this);
}
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
final Network activeNetwork = mConnectivityManager.getActiveNetwork();
if (activeNetwork != null && activeNetwork.equals(network)) {
mClient.onDataConnectivityChange();
}
}
@Override
public void onLosing(Network network, int maxMsToLive) {
mClient.onDataConnectivityChange();
}
@Override
public void onLost(Network network) {
mClient.onDataConnectivityChange();
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.network;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Handler;
import com.android.settings.network.telephony.DataConnectivityListener;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class DataConnectivityListenerTest {
@Mock
private DataConnectivityListener.Client mClient;
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
private Network mActiveNetwork;
private Context mContext;
private DataConnectivityListener mListener;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
when(mConnectivityManager.getActiveNetwork()).thenReturn(mActiveNetwork);
mListener = new DataConnectivityListener(mContext, mClient);
}
@Test
public void noStart_doesNotRegister() {
verify(mConnectivityManager, never()).registerNetworkCallback(any(NetworkRequest.class),
any(ConnectivityManager.NetworkCallback.class), any(Handler.class));
}
@Test
public void start_doesRegister() {
mListener.start();
verify(mConnectivityManager).registerNetworkCallback(any(NetworkRequest.class),
eq(mListener), any(Handler.class));
}
@Test
public void onCapabilitiesChanged_notActiveNetwork_noCallback() {
Network changedNetwork = mock(Network.class);
mListener.onCapabilitiesChanged(changedNetwork, mock(NetworkCapabilities.class));
verify(mClient, never()).onDataConnectivityChange();
}
@Test
public void onCapabilitiesChanged_activeNetwork_onDataConnectivityChangeFires() {
mListener.onCapabilitiesChanged(mActiveNetwork, mock(NetworkCapabilities.class));
verify(mClient).onDataConnectivityChange();
}
@Test
public void onLosing_notActiveNetwork_onDataConnectivityChangeFires() {
Network changedNetwork = mock(Network.class);
mListener.onLosing(changedNetwork, 500);
verify(mClient).onDataConnectivityChange();
}
@Test
public void onLosing_activeNetwork_onDataConnectivityChangeFires() {
mListener.onLosing(mActiveNetwork, 500);
verify(mClient).onDataConnectivityChange();
}
@Test
public void onLost_notActiveNetwork_onDataConnectivityChangeFires() {
Network changedNetwork = mock(Network.class);
mListener.onLost(changedNetwork);
verify(mClient).onDataConnectivityChange();
}
@Test
public void onLost_activeNetwork_onDataConnectivityChangeFires() {
mListener.onLost(mActiveNetwork);
verify(mClient).onDataConnectivityChange();
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.network;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.Uri;
import android.provider.Settings;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class MobileDataEnabledListenerTest {
private static final int SUB_ID_ONE = 111;
private static final int SUB_ID_TWO = 222;
@Mock
private MobileDataEnabledListener.Client mClient;
private Context mContext;
private MobileDataEnabledListener mListener;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mListener = new MobileDataEnabledListener(mContext, mClient);
}
@Test
public void onMobileDataEnabledChange_firesCorrectly() {
mListener.start(SUB_ID_ONE);
final Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + SUB_ID_ONE);
mContext.getContentResolver().notifyChange(uri, null);
verify(mClient).onMobileDataEnabledChange();
}
@Test
public void onMobileDataEnabledChange_doesNotFireAfterStop() {
mListener.start(SUB_ID_ONE);
mListener.stop();
final Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + SUB_ID_ONE);
mContext.getContentResolver().notifyChange(uri, null);
verify(mClient, never()).onMobileDataEnabledChange();
}
@Test
public void onMobileDataEnabledChange_changedToDifferentId_firesCorrectly() {
mListener.start(SUB_ID_ONE);
mListener.stop();
mListener.start(SUB_ID_TWO);
final Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + SUB_ID_TWO);
mContext.getContentResolver().notifyChange(uri, null);
verify(mClient).onMobileDataEnabledChange();
}
}

View File

@@ -33,6 +33,9 @@ import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -74,7 +77,13 @@ public class SubscriptionsPreferenceControllerTest {
@Mock
private SubscriptionManager mSubscriptionManager;
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private Network mActiveNetwork;
@Mock
private NetworkCapabilities mCapabilities;
private Context mContext;
private LifecycleOwner mLifecycleOwner;
@@ -90,7 +99,10 @@ public class SubscriptionsPreferenceControllerTest {
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
when(mConnectivityManager.getActiveNetwork()).thenReturn(mActiveNetwork);
when(mConnectivityManager.getNetworkCapabilities(mActiveNetwork)).thenReturn(mCapabilities);
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
when(mScreen.findPreference(eq(KEY))).thenReturn(mPreferenceCategory);
when(mPreferenceCategory.getContext()).thenReturn(mContext);
@@ -308,7 +320,8 @@ public class SubscriptionsPreferenceControllerTest {
ShadowSubscriptionManager.setDefaultDataSubscriptionId(11);
ShadowSubscriptionManager.setDefaultSmsSubscriptionId(11);
ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(11);
when(mTelephonyManager.getDataState()).thenReturn(TelephonyManager.DATA_CONNECTED);
when(mTelephonyManager.isDataEnabled()).thenReturn(true);
when(mCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).thenReturn(true);
assertThat(mController.getSummary(11)).isEqualTo(
mContext.getString(R.string.default_for_calls_and_sms) + System.lineSeparator()
@@ -318,6 +331,27 @@ public class SubscriptionsPreferenceControllerTest {
mContext.getString(R.string.subscription_available));
}
@Test
public void getSummary_twoSubsOneDefaultForEverythingDataNotActive() {
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
final SubscriptionInfo sub2 = mock(SubscriptionInfo.class);
when(sub1.getSubscriptionId()).thenReturn(11);
when(sub2.getSubscriptionId()).thenReturn(22);
SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(sub1, sub2));
ShadowSubscriptionManager.setDefaultDataSubscriptionId(11);
ShadowSubscriptionManager.setDefaultSmsSubscriptionId(11);
ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(11);
when(mTelephonyManager.isDataEnabled()).thenReturn(true);
assertThat(mController.getSummary(11)).isEqualTo(
mContext.getString(R.string.default_for_calls_and_sms) + System.lineSeparator()
+ mContext.getString(R.string.default_for_mobile_data));
assertThat(mController.getSummary(22)).isEqualTo(
mContext.getString(R.string.subscription_available));
}
@Test
public void getSummary_twoSubsOneDefaultForEverythingDataDisabled() {
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
@@ -329,7 +363,6 @@ public class SubscriptionsPreferenceControllerTest {
ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(11);
ShadowSubscriptionManager.setDefaultSmsSubscriptionId(11);
ShadowSubscriptionManager.setDefaultDataSubscriptionId(11);
when(mTelephonyManager.getDataState()).thenReturn(TelephonyManager.DATA_DISCONNECTED);
when(mTelephonyManager.isDataEnabled()).thenReturn(false);
assertThat(mController.getSummary(11)).isEqualTo(
@@ -351,7 +384,6 @@ public class SubscriptionsPreferenceControllerTest {
ShadowSubscriptionManager.setDefaultDataSubscriptionId(11);
ShadowSubscriptionManager.setDefaultSmsSubscriptionId(22);
ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(11);
when(mTelephonyManager.getDataState()).thenReturn(TelephonyManager.DATA_DISCONNECTED);
when(mTelephonyManager.isDataEnabled()).thenReturn(true);
assertThat(mController.getSummary(11)).isEqualTo(