Slice of Provider Model: add slice structure

Implement a slice which can show/update carrier networks.
Before and after: go/b173971144screenshot

Bug: 173971144
Test: atest NetworkProviderWorkerTest  (PASS)
atest ProviderModelSliceTest  (PASS)

Change-Id: I3f0dab364c88723ef3185a2ff040b1fbd1b099f4
This commit is contained in:
SongFerngWang
2020-11-17 17:07:19 +08:00
parent a97b4faa67
commit 8cad0197bd
8 changed files with 1062 additions and 6 deletions

View File

@@ -103,8 +103,10 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment
implements Indexable, WifiPickerTracker.WifiPickerTrackerCallback, implements Indexable, WifiPickerTracker.WifiPickerTrackerCallback,
WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener { WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener {
private static final String TAG = "NetworkProviderSettings"; public static final String ACTION_NETWORK_PROVIDER_SETTINGS =
"android.settings.NETWORK_PROVIDER_SETTINGS";
private static final String TAG = "NetworkProviderSettings";
// IDs of context menu // IDs of context menu
static final int MENU_ID_CONNECT = Menu.FIRST + 1; static final int MENU_ID_CONNECT = Menu.FIRST + 1;
@VisibleForTesting @VisibleForTesting

View File

@@ -0,0 +1,204 @@
/*
* Copyright (C) 2020 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 android.app.slice.Slice.EXTRA_TOGGLE_STATE;
import static com.android.settings.slices.CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.telephony.SubscriptionManager;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;
import com.android.settings.R;
import com.android.settings.SubSettings;
import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settings.network.telephony.NetworkProviderWorker;
import com.android.settings.slices.CustomSliceable;
import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settings.slices.SliceBuilderUtils;
import com.android.settings.wifi.slice.WifiSlice;
import com.android.settings.wifi.slice.WifiSliceItem;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
import java.util.stream.Collectors;
/**
* {@link CustomSliceable} for Wi-Fi and mobile data connection, used by generic clients.
*/
// ToDo If the provider model become default design in the future, the code needs to refactor
// the whole structure and use new "data object", and then split provider model out of old design.
public class ProviderModelSlice extends WifiSlice {
private static final String TAG = "ProviderModelSlice";
private final ProviderModelSliceHelper mHelper;
public ProviderModelSlice(Context context) {
super(context);
mHelper = getHelper();
}
@Override
public Uri getUri() {
return PROVIDER_MODEL_SLICE_URI;
}
private static void log(String s) {
Log.d(TAG, s);
}
protected boolean isApRowCollapsed() {
return false;
}
@Override
public Slice getSlice() {
// The provider model slice step:
// First section: Add a Wi-Fi item which state is connected.
// Second section: Add a carrier item.
// Third section: Add the Wi-Fi items which are not connected.
// Fourth section: If device has connection problem, this row show the message for user.
if (mHelper.isAirplaneModeEnabled()) {
log("Airplane mode is enabled.");
// ToDo Next CL will add the Airplane mode Message.
return mHelper.createListBuilder(getUri()).build();
}
int maxListSize = 0;
List<WifiSliceItem> wifiList = null;
final NetworkProviderWorker worker = getWorker();
if (worker != null) {
// get Wi-Fi list.
wifiList = worker.getResults();
maxListSize = worker.getApRowCount();
} else {
log("network provider worker is null.");
}
final boolean hasCarrier = mHelper.hasCarrier();
log("hasCarrier: " + hasCarrier);
final ListBuilder listBuilder = mHelper.createListBuilder(getUri());
// First section: Add a Wi-Fi item which state is connected.
final WifiSliceItem connectedWifiItem = mHelper.getConnectedWifiItem(wifiList);
if (connectedWifiItem != null) {
log("get Wi-Fi item witch is connected");
listBuilder.addRow(getWifiSliceItemRow(connectedWifiItem));
maxListSize--;
}
// Second section: Add a carrier item.
if (hasCarrier) {
listBuilder.addRow(mHelper.createCarrierRow());
maxListSize--;
}
// Third section: Add the Wi-Fi items which are not connected.
if (wifiList != null) {
log("get Wi-Fi items which are not connected");
final List<WifiSliceItem> disconnectedWifiList = wifiList.stream()
.filter(wifiSliceItem -> wifiSliceItem.getConnectedState()
!= WifiEntry.CONNECTED_STATE_CONNECTED)
.limit(maxListSize)
.collect(Collectors.toList());
for (WifiSliceItem item : disconnectedWifiList) {
listBuilder.addRow(getWifiSliceItemRow(item));
}
}
// Fourth section: If device has connection problem, this row show the message for user.
// 1) show non_carrier_network_unavailable:
// - while no wifi item
// 2) show all_network_unavailable:
// - while no wifi item + no carrier
// - while no wifi item + no data capability
if (worker == null || wifiList == null) {
log("wifiList is null");
int resId = R.string.non_carrier_network_unavailable;
if (!hasCarrier || mHelper.isNoCarrierData()) {
log("No carrier item or no carrier data.");
resId = R.string.all_network_unavailable;
}
if (!hasCarrier) {
// If there is no item in ProviderModelItem, slice needs a header.
listBuilder.setHeader(mHelper.createHeader());
}
listBuilder.addGridRow(mHelper.createMessageGridRow(resId));
}
return listBuilder.build();
}
/**
* Update the current carrier's mobile data status.
*/
@Override
public void onNotifyChange(Intent intent) {
final SubscriptionManager subscriptionManager = mHelper.getSubscriptionManager();
if (subscriptionManager == null) {
return;
}
final boolean newState = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
mHelper.isMobileDataEnabled());
final int defaultSubId = subscriptionManager.getDefaultDataSubscriptionId();
log("defaultSubId:" + defaultSubId);
if (!SubscriptionManager.isUsableSubscriptionId(defaultSubId)) {
return; // No subscription - do nothing.
}
MobileNetworkUtils.setMobileDataEnabled(mContext, defaultSubId, newState,
false /* disableOtherSubscriptions */);
}
@Override
public Intent getIntent() {
final String screenTitle = mContext.getText(R.string.provider_internet_settings).toString();
return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
NetworkProviderSettings.class.getName(), "" /* key */, screenTitle,
SettingsEnums.SLICE)
.setClassName(mContext.getPackageName(), SubSettings.class.getName())
.setData(getUri());
}
@Override
public Class getBackgroundWorkerClass() {
return NetworkProviderWorker.class;
}
@VisibleForTesting
ProviderModelSliceHelper getHelper() {
return new ProviderModelSliceHelper(mContext, this);
}
@VisibleForTesting
NetworkProviderWorker getWorker() {
return SliceBackgroundWorker.getInstance(getUri());
}
}

View File

@@ -0,0 +1,234 @@
/*
* Copyright (C) 2020 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.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.settings.network.MobileDataContentObserver;
import com.android.settings.network.MobileDataEnabledListener;
import com.android.settings.network.SubscriptionsChangeListener;
import com.android.settings.wifi.slice.WifiScanWorker;
import java.util.Collections;
import java.util.concurrent.Executor;
/**
* BackgroundWorker for Provider Model slice.
*/
public class NetworkProviderWorker extends WifiScanWorker implements
SignalStrengthListener.Callback, MobileDataEnabledListener.Client,
DataConnectivityListener.Client,
SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
private static final String TAG = "NetworkProviderWorker";
private static final int PROVIDER_MODEL_DEFAULT_EXPANDED_ROW_COUNT = 4;
private DataContentObserver mMobileDataObserver;
private SignalStrengthListener mSignalStrengthListener;
private SubscriptionsChangeListener mSubscriptionsListener;
private MobileDataEnabledListener mDataEnabledListener;
private DataConnectivityListener mConnectivityListener;
private final Context mContext;
@VisibleForTesting
final PhoneStateListener mPhoneStateListener;
private final SubscriptionManager mSubscriptionManager;
private final TelephonyManager mTelephonyManager;
public NetworkProviderWorker(Context context, Uri uri) {
super(context, uri);
// Mobile data worker
final Handler handler = new Handler(Looper.getMainLooper());
mMobileDataObserver = new DataContentObserver(handler, this);
mContext = context;
mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
mPhoneStateListener = new NetworkProviderPhoneStateListener(handler::post);
mSubscriptionsListener = new SubscriptionsChangeListener(context, this);
mDataEnabledListener = new MobileDataEnabledListener(context, this);
mConnectivityListener = new DataConnectivityListener(context, this);
mSignalStrengthListener = new SignalStrengthListener(context, this);
}
@Override
protected void onSlicePinned() {
mMobileDataObserver.register(mContext,
getDefaultSubscriptionId(mSubscriptionManager));
mSubscriptionsListener.start();
mDataEnabledListener.start(SubscriptionManager.getDefaultDataSubscriptionId());
mConnectivityListener.start();
mSignalStrengthListener.resume();
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE
| PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE
| PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED);
super.onSlicePinned();
}
@Override
protected void onSliceUnpinned() {
mMobileDataObserver.unregister(mContext);
mSubscriptionsListener.stop();
mDataEnabledListener.stop();
mConnectivityListener.stop();
mSignalStrengthListener.pause();
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
super.onSliceUnpinned();
}
@Override
public void close() {
mMobileDataObserver = null;
super.close();
}
@Override
public int getApRowCount() {
return PROVIDER_MODEL_DEFAULT_EXPANDED_ROW_COUNT;
}
/**
* To update the Slice.
*/
public void updateSlice() {
notifySliceChange();
}
@Override
public void onSubscriptionsChanged() {
int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
Log.d(TAG, "onSubscriptionsChanged: defaultDataSubId:" + defaultDataSubId);
mSignalStrengthListener.updateSubscriptionIds(
SubscriptionManager.isUsableSubscriptionId(defaultDataSubId)
? Collections.singleton(defaultDataSubId) : Collections.emptySet());
if (defaultDataSubId != mDataEnabledListener.getSubId()) {
mDataEnabledListener.stop();
mDataEnabledListener.start(defaultDataSubId);
}
updateSlice();
}
@Override
public void onSignalStrengthChanged() {
Log.d(TAG, "onSignalStrengthChanged");
updateSlice();
}
@Override
public void onAirplaneModeChanged(boolean airplaneModeEnabled) {
Log.d(TAG, "onAirplaneModeChanged");
updateSlice();
}
@Override
public void onMobileDataEnabledChange() {
Log.d(TAG, "onMobileDataEnabledChange");
updateSlice();
}
@Override
public void onDataConnectivityChange() {
Log.d(TAG, "onDataConnectivityChange");
updateSlice();
}
/**
* Listen to update of mobile data change.
*/
public class DataContentObserver extends ContentObserver {
private final NetworkProviderWorker mNetworkProviderWorker;
public DataContentObserver(Handler handler, NetworkProviderWorker backgroundWorker) {
super(handler);
mNetworkProviderWorker = backgroundWorker;
}
@Override
public void onChange(boolean selfChange) {
mNetworkProviderWorker.updateSlice();
}
/**
* To register the observer for mobile data changed.
* @param context the Context object.
* @param subId the default data subscription id.
*/
public void register(Context context, int subId) {
final Uri uri = MobileDataContentObserver.getObservableUri(context, subId);
context.getContentResolver().registerContentObserver(uri, false, this);
}
/**
* To unregister the observer for mobile data changed.
* @param context the Context object.
*/
public void unregister(Context context) {
context.getContentResolver().unregisterContentObserver(this);
}
}
class NetworkProviderPhoneStateListener extends PhoneStateListener {
NetworkProviderPhoneStateListener(Executor executor) {
super(executor);
}
@Override
public void onServiceStateChanged(ServiceState state) {
Log.d(TAG, "onServiceStateChanged voiceState=" + state.getState()
+ " dataState=" + state.getDataRegistrationState());
updateSlice();
}
@Override
public void onActiveDataSubscriptionIdChanged(int subId) {
Log.d(TAG, "onActiveDataSubscriptionIdChanged: subId=" + subId);
updateSlice();
}
@Override
public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
Log.d(TAG, "onDisplayInfoChanged: telephonyDisplayInfo=" + telephonyDisplayInfo);
updateSlice();
}
}
protected static int getDefaultSubscriptionId(SubscriptionManager subscriptionManager) {
final SubscriptionInfo defaultSubscription = subscriptionManager.getActiveSubscriptionInfo(
subscriptionManager.getDefaultDataSubscriptionId());
if (defaultSubscription == null) {
return SubscriptionManager.INVALID_SUBSCRIPTION_ID; // No default subscription
}
return defaultSubscription.getSubscriptionId();
}
}

View File

@@ -16,6 +16,8 @@
package com.android.settings.panel; package com.android.settings.panel;
import static com.android.settings.network.NetworkProviderSettings.ACTION_NETWORK_PROVIDER_SETTINGS;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -51,17 +53,19 @@ public class InternetConnectivityPanel implements PanelContent {
@Override @Override
public CharSequence getTitle() { public CharSequence getTitle() {
return mContext.getText(R.string.internet_connectivity_panel_title); return mContext.getText(Utils.isProviderModelEnabled(mContext)
? R.string.provider_internet_settings : R.string.internet_connectivity_panel_title);
} }
@Override @Override
public List<Uri> getSlices() { public List<Uri> getSlices() {
final List<Uri> uris = new ArrayList<>(); final List<Uri> uris = new ArrayList<>();
uris.add(CustomSliceRegistry.WIFI_SLICE_URI);
uris.add(CustomSliceRegistry.MOBILE_DATA_SLICE_URI);
if (Utils.isProviderModelEnabled(mContext)) { if (Utils.isProviderModelEnabled(mContext)) {
uris.add(CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI);
uris.add(CustomSliceRegistry.AIRPLANE_SAFE_NETWORKS_SLICE_URI); uris.add(CustomSliceRegistry.AIRPLANE_SAFE_NETWORKS_SLICE_URI);
} else { } else {
uris.add(CustomSliceRegistry.WIFI_SLICE_URI);
uris.add(CustomSliceRegistry.MOBILE_DATA_SLICE_URI);
uris.add(AirplaneModePreferenceController.SLICE_URI); uris.add(AirplaneModePreferenceController.SLICE_URI);
} }
return uris; return uris;
@@ -69,7 +73,8 @@ public class InternetConnectivityPanel implements PanelContent {
@Override @Override
public Intent getSeeMoreIntent() { public Intent getSeeMoreIntent() {
return new Intent(Settings.ACTION_WIRELESS_SETTINGS) return new Intent(Utils.isProviderModelEnabled(mContext)
? ACTION_NETWORK_PROVIDER_SETTINGS : Settings.ACTION_WIRELESS_SETTINGS)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} }

View File

@@ -40,6 +40,7 @@ import com.android.settings.location.LocationSlice;
import com.android.settings.media.MediaOutputIndicatorSlice; import com.android.settings.media.MediaOutputIndicatorSlice;
import com.android.settings.media.RemoteMediaSlice; import com.android.settings.media.RemoteMediaSlice;
import com.android.settings.network.AirplaneSafeNetworksSlice; import com.android.settings.network.AirplaneSafeNetworksSlice;
import com.android.settings.network.ProviderModelSlice;
import com.android.settings.network.telephony.MobileDataSlice; import com.android.settings.network.telephony.MobileDataSlice;
import com.android.settings.notification.zen.ZenModeButtonPreferenceController; import com.android.settings.notification.zen.ZenModeButtonPreferenceController;
import com.android.settings.wifi.calling.WifiCallingSliceHelper; import com.android.settings.wifi.calling.WifiCallingSliceHelper;
@@ -167,6 +168,17 @@ public class CustomSliceRegistry {
.appendEncodedPath(SettingsSlicesContract.PATH_SETTING_ACTION) .appendEncodedPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("mobile_data") .appendPath("mobile_data")
.build(); .build();
/**
* Full {@link Uri} for the Provider Model Slice.
*/
public static final Uri PROVIDER_MODEL_SLICE_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendEncodedPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("provider_model")
.build();
/** /**
* Full {@link Uri} for the Alarm volume Slice. * Full {@link Uri} for the Alarm volume Slice.
*/ */
@@ -176,6 +188,7 @@ public class CustomSliceRegistry {
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("alarm_volume") .appendPath("alarm_volume")
.build(); .build();
/** /**
* Full {@link Uri} for the Call Volume Slice. * Full {@link Uri} for the Call Volume Slice.
*/ */
@@ -319,6 +332,7 @@ public class CustomSliceRegistry {
sUriToSlice.put(LOW_STORAGE_SLICE_URI, LowStorageSlice.class); sUriToSlice.put(LOW_STORAGE_SLICE_URI, LowStorageSlice.class);
sUriToSlice.put(MEDIA_OUTPUT_INDICATOR_SLICE_URI, MediaOutputIndicatorSlice.class); sUriToSlice.put(MEDIA_OUTPUT_INDICATOR_SLICE_URI, MediaOutputIndicatorSlice.class);
sUriToSlice.put(MOBILE_DATA_SLICE_URI, MobileDataSlice.class); sUriToSlice.put(MOBILE_DATA_SLICE_URI, MobileDataSlice.class);
sUriToSlice.put(PROVIDER_MODEL_SLICE_URI, ProviderModelSlice.class);
sUriToSlice.put(WIFI_SLICE_URI, WifiSlice.class); sUriToSlice.put(WIFI_SLICE_URI, WifiSlice.class);
sUriToSlice.put(DARK_THEME_SLICE_URI, DarkThemeSlice.class); sUriToSlice.put(DARK_THEME_SLICE_URI, DarkThemeSlice.class);
sUriToSlice.put(REMOTE_MEDIA_SLICE_URI, RemoteMediaSlice.class); sUriToSlice.put(REMOTE_MEDIA_SLICE_URI, RemoteMediaSlice.class);

View File

@@ -61,7 +61,7 @@ public class WifiScanWorker extends SliceBackgroundWorker<WifiSliceItem> impleme
@VisibleForTesting @VisibleForTesting
final LifecycleRegistry mLifecycleRegistry; final LifecycleRegistry mLifecycleRegistry;
@VisibleForTesting @VisibleForTesting
WifiPickerTracker mWifiPickerTracker; protected WifiPickerTracker mWifiPickerTracker;
// Worker thread used for WifiPickerTracker work // Worker thread used for WifiPickerTracker work
private final HandlerThread mWorkerThread; private final HandlerThread mWorkerThread;

View File

@@ -0,0 +1,333 @@
/*
* Copyright (C) 2020 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 com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import androidx.slice.Slice;
import androidx.slice.SliceProvider;
import androidx.slice.builders.GridRowBuilder;
import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.SliceAction;
import androidx.slice.widget.SliceLiveData;
import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.Utils;
import com.android.settings.network.telephony.NetworkProviderWorker;
import com.android.settings.testutils.ResourcesUtils;
import com.android.settings.wifi.slice.WifiSliceItem;
import com.android.wifitrackerlib.WifiEntry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class ProviderModelSliceTest {
private static final Uri PROVIDER_MODEL_SLICE_URI =
Uri.parse("content://com.android.settings.slices/action/provider_model");
private static final int MOCK_SLICE_LEVEL = 3;
private Context mContext;
private MockProviderModelSlice mMockProviderModelSlice;
List<WifiSliceItem> mWifiList = new ArrayList<>();
private ListBuilder mListBuilder;
private MockNetworkProviderWorker mMockNetworkProviderWorker;
@Mock
private SubscriptionManager mSubscriptionManager;
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private WifiManager mWifiManager;
@Mock
private ProviderModelSliceHelper mProviderModelSliceHelper;
@Mock
private WifiSliceItem mMockWifiSliceItem1;
@Mock
private WifiSliceItem mMockWifiSliceItem2;
@Mock
private WifiSliceItem mMockWifiSliceItem3;
@Mock
ListBuilder.RowBuilder mMockCarrierRowBuild;
@Mock
ListBuilder.HeaderBuilder mMockHeader;
@Mock
GridRowBuilder mMockGridRowBuilderNonCarrierNetworkUnavailable;
@Mock
GridRowBuilder mMockGridRowBuilderAllNetworkUnavailable;
@Before
@UiThreadTest
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
// Set-up specs for SliceMetadata.
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
mMockNetworkProviderWorker = new MockNetworkProviderWorker(mContext,
PROVIDER_MODEL_SLICE_URI);
mMockProviderModelSlice = new MockProviderModelSlice(mContext, mMockNetworkProviderWorker);
mListBuilder = spy(new ListBuilder(mContext, PROVIDER_MODEL_SLICE_URI,
ListBuilder.INFINITY).setAccentColor(-1));
when(mProviderModelSliceHelper.createListBuilder(PROVIDER_MODEL_SLICE_URI)).thenReturn(
mListBuilder);
mWifiList = new ArrayList<>();
mMockNetworkProviderWorker.updateSelfResults(mWifiList);
mockBuilder();
}
@Test
@UiThreadTest
public void getSlice_noWorkerAndNoCarrier_getOneHeaderOneGridRowWithAllNetworkUnavailable() {
mWifiList.clear();
mMockProviderModelSlice = new MockProviderModelSlice(mContext, null);
mockHelperCondition(false, false, false, null);
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(1)).setHeader(mMockHeader);
verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable);
}
@Test
@UiThreadTest
public void getSlice_noWifiAndNoCarrier_getOneHeaderOneGridRowWithAllNetworkUnavailable() {
mWifiList.clear();
mMockNetworkProviderWorker.updateSelfResults(null);
mockHelperCondition(false, false, false, null);
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(1)).setHeader(mMockHeader);
verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable);
}
@Test
@UiThreadTest
public void getSlice_noWifiAndHasCarrierNoData_oneCarrierOneGridRowWithAllNetworkUnavailable() {
mWifiList.clear();
mMockNetworkProviderWorker.updateSelfResults(null);
mockHelperCondition(false, true, true, null);
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild);
verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable);
}
@Test
@UiThreadTest
public void getSlice_noWifiAndNoCarrier_oneCarrierOneGridRowWithNonCarrierNetworkUnavailable() {
mWifiList.clear();
mMockProviderModelSlice = new MockProviderModelSlice(mContext, null);
mockHelperCondition(false, true, false, null);
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild);
verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderNonCarrierNetworkUnavailable);
}
@Test
@UiThreadTest
public void getSlice_haveTwoWifiAndOneCarrier_getCarrierAndTwoWiFi() {
mWifiList.clear();
mockWifiItemCondition(mMockWifiSliceItem1, "wifi1", "wifi1",
WifiEntry.CONNECTED_STATE_CONNECTED, "wifi1_key", true);
mWifiList.add(mMockWifiSliceItem1);
mockWifiItemCondition(mMockWifiSliceItem2, "wifi2", "wifi2",
WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi2_key", true);
mWifiList.add(mMockWifiSliceItem2);
mMockNetworkProviderWorker.updateSelfResults(mWifiList);
mockHelperCondition(false, true, false, mWifiList.get(0));
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild);
verify(mListBuilder, times(3)).addRow(any(ListBuilder.RowBuilder.class));
}
@Test
@UiThreadTest
public void getSlice_haveOneConnectedWifiAndTwoDisconnectedWifiAndNoCarrier_getTwoRow() {
mWifiList.clear();
mockWifiItemCondition(mMockWifiSliceItem1, "wifi1", "wifi1",
WifiEntry.CONNECTED_STATE_CONNECTED, "wifi1_key", true);
mWifiList.add(mMockWifiSliceItem1);
mockWifiItemCondition(mMockWifiSliceItem2, "wifi2", "wifi2",
WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi2_key", true);
mWifiList.add(mMockWifiSliceItem2);
mockWifiItemCondition(mMockWifiSliceItem3, "wifi3", "wifi3",
WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi3_key", true);
mWifiList.add(mMockWifiSliceItem3);
mMockNetworkProviderWorker.updateSelfResults(mWifiList);
mockHelperCondition(false, false, false, mWifiList.get(0));
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(3)).addRow(any(ListBuilder.RowBuilder.class));
}
@Test
@UiThreadTest
public void getSlice_haveTwoDisconnectedWifiAndNoCarrier_getTwoRow() {
mWifiList.clear();
mockWifiItemCondition(mMockWifiSliceItem1, "wifi1", "wifi1",
WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi1_key", true);
mWifiList.add(mMockWifiSliceItem1);
mockWifiItemCondition(mMockWifiSliceItem2, "wifi2", "wifi2",
WifiEntry.CONNECTED_STATE_DISCONNECTED, "wifi2_key", true);
mWifiList.add(mMockWifiSliceItem2);
mMockNetworkProviderWorker.updateSelfResults(mWifiList);
mockHelperCondition(false, false, false, null);
final Slice slice = mMockProviderModelSlice.getSlice();
assertThat(slice).isNotNull();
verify(mListBuilder, times(2)).addRow(any(ListBuilder.RowBuilder.class));
}
@Test
public void providerModelSlice_hasCorrectUri() {
assertThat(mMockProviderModelSlice.getUri()).isEqualTo(PROVIDER_MODEL_SLICE_URI);
}
private void mockHelperCondition(boolean airplaneMode, boolean hasCarrier,
boolean isNoCarrierData, WifiSliceItem connectedWifiItem) {
when(mProviderModelSliceHelper.isAirplaneModeEnabled()).thenReturn(airplaneMode);
when(mProviderModelSliceHelper.hasCarrier()).thenReturn(hasCarrier);
when(mProviderModelSliceHelper.isNoCarrierData()).thenReturn(isNoCarrierData);
when(mProviderModelSliceHelper.getConnectedWifiItem(any())).thenReturn(connectedWifiItem);
}
private void mockWifiItemCondition(WifiSliceItem mockWifiItem, String title, String summary,
int connectedState, String key, boolean shouldEditBeforeConnect) {
when(mockWifiItem.getTitle()).thenReturn(title);
when(mockWifiItem.getSummary()).thenReturn(summary);
when(mockWifiItem.getConnectedState()).thenReturn(connectedState);
when(mockWifiItem.getLevel()).thenReturn(MOCK_SLICE_LEVEL);
when(mockWifiItem.getKey()).thenReturn(key);
when(mockWifiItem.shouldEditBeforeConnect()).thenReturn(shouldEditBeforeConnect);
}
private void mockBuilder() {
SliceAction mockSliceAction = getPrimarySliceAction();
when(mMockHeader.getTitle()).thenReturn("mockHeader");
when(mMockHeader.getPrimaryAction()).thenReturn(mockSliceAction);
when(mProviderModelSliceHelper.createHeader()).thenReturn(mMockHeader);
int resId = ResourcesUtils.getResourcesId(mContext, "string",
"non_carrier_network_unavailable");
when(mProviderModelSliceHelper.createMessageGridRow(resId)).thenReturn(
mMockGridRowBuilderNonCarrierNetworkUnavailable);
resId = ResourcesUtils.getResourcesId(mContext, "string",
"all_network_unavailable");
when(mProviderModelSliceHelper.createMessageGridRow(resId)).thenReturn(
mMockGridRowBuilderAllNetworkUnavailable);
when(mMockCarrierRowBuild.getTitle()).thenReturn("mockRow");
when(mMockCarrierRowBuild.getPrimaryAction()).thenReturn(mockSliceAction);
when(mProviderModelSliceHelper.createCarrierRow()).thenReturn(mMockCarrierRowBuild);
}
private SliceAction getPrimarySliceAction() {
return SliceAction.createDeeplink(
getPrimaryAction(),
Utils.createIconWithDrawable(new ColorDrawable(Color.TRANSPARENT)),
ListBuilder.ICON_IMAGE,
ResourcesUtils.getResourcesString(mContext, "summary_placeholder"));
}
private PendingIntent getPrimaryAction() {
final Intent intent = new Intent("android.settings.NETWORK_PROVIDER_SETTINGS")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return PendingIntent.getActivity(mContext, 0 /* requestCode */,
intent, PendingIntent.FLAG_IMMUTABLE /* flags */);
}
public class MockNetworkProviderWorker extends NetworkProviderWorker {
MockNetworkProviderWorker(Context context, Uri uri) {
super(context, uri);
}
public void updateSelfResults(List<WifiSliceItem> results) {
this.updateResults(results);
}
}
public class MockProviderModelSlice extends ProviderModelSlice {
private MockNetworkProviderWorker mNetworkProviderWorker;
MockProviderModelSlice(Context context, MockNetworkProviderWorker networkProviderWorker) {
super(context);
mNetworkProviderWorker = networkProviderWorker;
}
@Override
ProviderModelSliceHelper getHelper() {
return mProviderModelSliceHelper;
}
@Override
NetworkProviderWorker getWorker() {
return mNetworkProviderWorker;
}
}
}

View File

@@ -0,0 +1,264 @@
/*
* Copyright (C) 2020 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 static com.android.settings.slices.CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import androidx.lifecycle.Lifecycle;
import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.wifitrackerlib.WifiEntry;
import com.android.wifitrackerlib.WifiPickerTracker;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
public class NetworkProviderWorkerTest {
private Context mContext;
private MockNetworkProviderWorker mMockNetworkProviderWorker;
@Mock
WifiPickerTracker mMockWifiPickerTracker;
@Mock
private SubscriptionManager mSubscriptionManager;
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
private TelephonyManager mTelephonyManager;
@Before
@UiThreadTest
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
mMockNetworkProviderWorker = new MockNetworkProviderWorker(mContext,
PROVIDER_MODEL_SLICE_URI);
mMockNetworkProviderWorker.setWifiPickerTracker(mMockWifiPickerTracker);
}
@Test
@UiThreadTest
public void onConstructor_shouldBeInCreatedState() {
assertThat(mMockNetworkProviderWorker.getLifecycle().getCurrentState())
.isEqualTo(Lifecycle.State.CREATED);
}
@Test
@UiThreadTest
public void onSlicePinned_shouldBeInResumedState() {
mMockNetworkProviderWorker.onSlicePinned();
assertThat(mMockNetworkProviderWorker.getLifecycle().getCurrentState())
.isEqualTo(Lifecycle.State.RESUMED);
}
@Test
@UiThreadTest
public void onSliceUnpinned_shouldBeInCreatedState() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.onSliceUnpinned();
assertThat(mMockNetworkProviderWorker.getLifecycle().getCurrentState())
.isEqualTo(Lifecycle.State.CREATED);
}
@Test
@UiThreadTest
public void close_shouldBeInDestroyedState() {
mMockNetworkProviderWorker.close();
assertThat(mMockNetworkProviderWorker.getLifecycle().getCurrentState())
.isEqualTo(Lifecycle.State.DESTROYED);
}
@Test
@UiThreadTest
public void getWifiEntry_connectedWifiKey_shouldGetConnectedWifi() {
final String key = "key";
final WifiEntry connectedWifiEntry = mock(WifiEntry.class);
when(connectedWifiEntry.getKey()).thenReturn(key);
when(mMockWifiPickerTracker.getConnectedWifiEntry()).thenReturn(connectedWifiEntry);
assertThat(mMockNetworkProviderWorker.getWifiEntry(key)).isEqualTo(connectedWifiEntry);
}
@Test
@UiThreadTest
public void getWifiEntry_reachableWifiKey_shouldGetReachableWifi() {
final String key = "key";
final WifiEntry reachableWifiEntry = mock(WifiEntry.class);
when(reachableWifiEntry.getKey()).thenReturn(key);
when(mMockWifiPickerTracker.getWifiEntries()).thenReturn(Arrays.asList(reachableWifiEntry));
assertThat(mMockNetworkProviderWorker.getWifiEntry(key)).isEqualTo(reachableWifiEntry);
}
@Test
@UiThreadTest
public void onSubscriptionsChanged_notifySubscriptionChanged_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.onSubscriptionsChanged();
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onAirplaneModeChanged_airplaneModeOn_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.onAirplaneModeChanged(false);
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onAirplaneModeChanged_airplaneModeOff_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.onAirplaneModeChanged(true);
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onSignalStrengthChanged_notifySignalStrengthChanged_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.onSignalStrengthChanged();
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onMobileDataEnabledChange_notifyMobileDataEnabledChanged_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.onMobileDataEnabledChange();
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onDataConnectivityChange_notifyDataConnectivityChanged_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.onDataConnectivityChange();
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onServiceStateChanged_notifyPhoneStateListener_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.mPhoneStateListener.onServiceStateChanged(new ServiceState());
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onActiveDataSubscriptionIdChanged_notifyPhoneStateListener_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.mPhoneStateListener.onActiveDataSubscriptionIdChanged(1);
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
@Test
@UiThreadTest
public void onDisplayInfoChanged_notifyPhoneStateListener_callUpdateSlice() {
mMockNetworkProviderWorker.onSlicePinned();
mMockNetworkProviderWorker.receiveNotification(false);
mMockNetworkProviderWorker.mPhoneStateListener.onDisplayInfoChanged(
new TelephonyDisplayInfo(14, 0));
assertThat(mMockNetworkProviderWorker.hasNotification()).isTrue();
}
public class MockNetworkProviderWorker extends NetworkProviderWorker {
private boolean mHasNotification = false;
MockNetworkProviderWorker(Context context, Uri uri) {
super(context, uri);
}
public void receiveNotification(boolean inputValue) {
mHasNotification = inputValue;
}
public boolean hasNotification() {
return mHasNotification;
}
@Override
public void updateSlice() {
super.updateSlice();
receiveNotification(true);
}
public void setWifiPickerTracker(WifiPickerTracker wifiPickerTracker) {
mWifiPickerTracker = wifiPickerTracker;
}
}
}