Loading packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml +10 −0 Original line number Diff line number Diff line Loading @@ -42,4 +42,14 @@ --> </string-array> <string-array translatable="false" name="config_legacy_networktype_restore_timers"> <item>2,60000</item><!-- mobile_mms --> <item>3,60000</item><!-- mobile_supl --> <item>4,60000</item><!-- mobile_dun --> <item>5,60000</item><!-- mobile_hipri --> <item>10,60000</item><!-- mobile_fota --> <item>11,60000</item><!-- mobile_ims --> <item>12,60000</item><!-- mobile_cbs --> </string-array> </resources> No newline at end of file packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml +1 −1 Original line number Diff line number Diff line Loading @@ -17,11 +17,11 @@ <overlayable name="ServiceConnectivityResourcesConfig"> <policy type="product|system|vendor"> <!-- Configuration values for ConnectivityService --> <item type="array" name="config_legacy_networktype_restore_timers"/> <item type="string" name="config_networkCaptivePortalServerUrl"/> <item type="integer" name="config_networkTransitionTimeout"/> <item type="array" name="config_wakeonlan_supported_interfaces"/> </policy> </overlayable> </resources> services/core/java/com/android/server/ConnectivityService.java +107 −68 Original line number Diff line number Diff line Loading @@ -17,6 +17,10 @@ package com.android.server; import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE; import static android.content.pm.PackageManager.FEATURE_BLUETOOTH; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.FEATURE_WIFI; import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK; import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK; Loading @@ -28,9 +32,23 @@ import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; import static android.net.ConnectivityManager.TYPE_BLUETOOTH; import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_CBS; import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY; import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; import static android.net.ConnectivityManager.TYPE_MOBILE_IA; import static android.net.ConnectivityManager.TYPE_MOBILE_IMS; import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL; import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_PROXY; import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI_P2P; import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.ConnectivityManager.isNetworkTypeValid; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS; Loading Loading @@ -112,7 +130,6 @@ import android.net.Network; import android.net.NetworkAgent; import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkConfig; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkMonitorManager; Loading Loading @@ -626,11 +643,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private UserManager mUserManager; private NetworkConfig[] mNetConfigs; private int mNetworksDefined; // the set of network types that can only be enabled by system/sig apps private List mProtectedNetworks; private List<Integer> mProtectedNetworks; private Set<String> mWolSupportedInterfaces; Loading Loading @@ -720,18 +734,63 @@ public class ConnectivityService extends IConnectivityManager.Stub * They are therefore not thread-safe with respect to each other. * - getNetworkForType() can be called at any time on binder threads. It is synchronized * on mTypeLists to be thread-safe with respect to a concurrent remove call. * - getRestoreTimerForType(type) is also synchronized on mTypeLists. * - dump is thread-safe with respect to concurrent add and remove calls. */ private final ArrayList<NetworkAgentInfo> mTypeLists[]; @NonNull private final ConnectivityService mService; // Restore timers for requestNetworkForFeature (network type -> timer in ms). Types without // an entry have no timer (equivalent to -1). Lazily loaded. @NonNull private ArrayMap<Integer, Integer> mRestoreTimers = new ArrayMap<>(); LegacyTypeTracker(@NonNull ConnectivityService service) { mService = service; mTypeLists = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1]; } public void addSupportedType(int type) { public void loadSupportedTypes(@NonNull Context ctx, @NonNull TelephonyManager tm) { final PackageManager pm = ctx.getPackageManager(); if (pm.hasSystemFeature(FEATURE_WIFI)) { addSupportedType(TYPE_WIFI); } if (pm.hasSystemFeature(FEATURE_WIFI_DIRECT)) { addSupportedType(TYPE_WIFI_P2P); } if (tm.isDataCapable()) { // Telephony does not have granular support for these types: they are either all // supported, or none is supported addSupportedType(TYPE_MOBILE); addSupportedType(TYPE_MOBILE_MMS); addSupportedType(TYPE_MOBILE_SUPL); addSupportedType(TYPE_MOBILE_DUN); addSupportedType(TYPE_MOBILE_HIPRI); addSupportedType(TYPE_MOBILE_FOTA); addSupportedType(TYPE_MOBILE_IMS); addSupportedType(TYPE_MOBILE_CBS); addSupportedType(TYPE_MOBILE_IA); addSupportedType(TYPE_MOBILE_EMERGENCY); } if (pm.hasSystemFeature(FEATURE_BLUETOOTH)) { addSupportedType(TYPE_BLUETOOTH); } if (pm.hasSystemFeature(FEATURE_WATCH)) { // TYPE_PROXY is only used on Wear addSupportedType(TYPE_PROXY); } // Ethernet is often not specified in the configs, although many devices can use it via // USB host adapters. Add it as long as the ethernet service is here. if (ctx.getSystemService(Context.ETHERNET_SERVICE) != null) { addSupportedType(TYPE_ETHERNET); } // Always add TYPE_VPN as a supported type addSupportedType(TYPE_VPN); } private void addSupportedType(int type) { if (mTypeLists[type] != null) { throw new IllegalStateException( "legacy list for type " + type + "already initialized"); Loading @@ -752,6 +811,35 @@ public class ConnectivityService extends IConnectivityManager.Stub return null; } public int getRestoreTimerForType(int type) { synchronized (mTypeLists) { if (mRestoreTimers == null) { mRestoreTimers = loadRestoreTimers(); } return mRestoreTimers.getOrDefault(type, -1); } } private ArrayMap<Integer, Integer> loadRestoreTimers() { final String[] configs = mService.mResources.get().getStringArray( com.android.connectivity.resources.R.array .config_legacy_networktype_restore_timers); final ArrayMap<Integer, Integer> ret = new ArrayMap<>(configs.length); for (final String config : configs) { final String[] splits = TextUtils.split(config, ","); if (splits.length != 2) { logwtf("Invalid restore timer token count: " + config); continue; } try { ret.put(Integer.parseInt(splits[0]), Integer.parseInt(splits[1])); } catch (NumberFormatException e) { logwtf("Invalid restore timer number format: " + config, e); } } return ret; } private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type, boolean isDefaultNetwork) { if (DBG) { Loading Loading @@ -1174,64 +1262,12 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1]; // TODO: What is the "correct" way to do determine if this is a wifi only device? boolean wifiOnly = mSystemProperties.getBoolean("ro.radio.noril", false); log("wifiOnly=" + wifiOnly); String[] naStrings = context.getResources().getStringArray( com.android.internal.R.array.networkAttributes); for (String naString : naStrings) { try { NetworkConfig n = new NetworkConfig(naString); if (VDBG) log("naString=" + naString + " config=" + n); if (n.type > ConnectivityManager.MAX_NETWORK_TYPE) { loge("Error in networkAttributes - ignoring attempt to define type " + n.type); continue; } if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(n.type)) { log("networkAttributes - ignoring mobile as this dev is wifiOnly " + n.type); continue; } if (mNetConfigs[n.type] != null) { loge("Error in networkAttributes - ignoring attempt to redefine type " + n.type); continue; } mLegacyTypeTracker.addSupportedType(n.type); mNetConfigs[n.type] = n; mNetworksDefined++; } catch(Exception e) { // ignore it - leave the entry null } } // Forcibly add TYPE_VPN as a supported type, if it has not already been added via config. if (mNetConfigs[TYPE_VPN] == null) { // mNetConfigs is used only for "restore time", which isn't applicable to VPNs, so we // don't need to add TYPE_VPN to mNetConfigs. mLegacyTypeTracker.addSupportedType(TYPE_VPN); mNetworksDefined++; // used only in the log() statement below. } // Do the same for Ethernet, since it's often not specified in the configs, although many // devices can use it via USB host adapters. if (mNetConfigs[TYPE_ETHERNET] == null && mContext.getSystemService(Context.ETHERNET_SERVICE) != null) { mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET); mNetworksDefined++; } if (VDBG) log("mNetworksDefined=" + mNetworksDefined); mProtectedNetworks = new ArrayList<Integer>(); mLegacyTypeTracker.loadSupportedTypes(mContext, mTelephonyManager); mProtectedNetworks = new ArrayList<>(); int[] protectedNetworks = context.getResources().getIntArray( com.android.internal.R.array.config_protectedNetworks); for (int p : protectedNetworks) { if ((mNetConfigs[p] != null) && (mProtectedNetworks.contains(p) == false)) { if (mLegacyTypeTracker.isTypeSupported(p) && !mProtectedNetworks.contains(p)) { mProtectedNetworks.add(p); } else { if (DBG) loge("Ignoring protectedNetwork " + p); Loading Loading @@ -2640,9 +2676,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // if the system property isn't set, use the value for the apn type int ret = RESTORE_DEFAULT_NETWORK_DELAY; if ((networkType <= ConnectivityManager.MAX_NETWORK_TYPE) && (mNetConfigs[networkType] != null)) { ret = mNetConfigs[networkType].restoreTime; if (mLegacyTypeTracker.isTypeSupported(networkType)) { ret = mLegacyTypeTracker.getRestoreTimerForType(networkType); } return ret; } Loading Loading @@ -4855,6 +4890,10 @@ public class ConnectivityService extends IConnectivityManager.Stub Log.wtf(TAG, s); } private static void logwtf(String s, Throwable t) { Log.wtf(TAG, s, t); } private static void loge(String s) { Log.e(TAG, s); } Loading Loading @@ -8917,13 +8956,13 @@ public class ConnectivityService extends IConnectivityManager.Stub private int transportTypeToLegacyType(int type) { switch (type) { case NetworkCapabilities.TRANSPORT_CELLULAR: return ConnectivityManager.TYPE_MOBILE; return TYPE_MOBILE; case NetworkCapabilities.TRANSPORT_WIFI: return ConnectivityManager.TYPE_WIFI; return TYPE_WIFI; case NetworkCapabilities.TRANSPORT_BLUETOOTH: return ConnectivityManager.TYPE_BLUETOOTH; return TYPE_BLUETOOTH; case NetworkCapabilities.TRANSPORT_ETHERNET: return ConnectivityManager.TYPE_ETHERNET; return TYPE_ETHERNET; default: loge("Unexpected transport in transportTypeToLegacyType: " + type); } Loading tests/net/java/com/android/server/ConnectivityServiceTest.java +8 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import static android.content.Intent.ACTION_USER_ADDED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.ACTION_USER_UNLOCKED; import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PackageManager.FEATURE_WIFI; import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.content.pm.PackageManager.PERMISSION_DENIED; Loading @@ -42,6 +44,7 @@ import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL; import static android.net.ConnectivityManager.TYPE_PROXY; import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS; Loading Loading @@ -1505,6 +1508,9 @@ public class ConnectivityServiceTest { Looper.prepare(); } mockDefaultPackages(); mockHasSystemFeature(FEATURE_WIFI, true); mockHasSystemFeature(FEATURE_WIFI_DIRECT, true); doReturn(true).when(mTelephonyManager).isDataCapable(); FakeSettingsProvider.clearSettingsProvider(); mServiceContext = new MockContext(InstrumentationRegistry.getContext(), Loading Loading @@ -1829,7 +1835,8 @@ public class ConnectivityServiceTest { assertTrue(mCm.isNetworkSupported(TYPE_WIFI)); assertTrue(mCm.isNetworkSupported(TYPE_MOBILE)); assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_MMS)); assertFalse(mCm.isNetworkSupported(TYPE_MOBILE_FOTA)); assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_FOTA)); assertFalse(mCm.isNetworkSupported(TYPE_PROXY)); // Check that TYPE_ETHERNET is supported. Unlike the asserts above, which only validate our // mocks, this assert exercises the ConnectivityService code path that ensures that Loading tests/net/java/com/android/server/LegacyTypeTrackerTest.kt +92 −35 Original line number Diff line number Diff line Loading @@ -21,13 +21,29 @@ package com.android.server import android.content.Context import android.content.pm.PackageManager import android.content.pm.PackageManager.FEATURE_WIFI import android.content.pm.PackageManager.FEATURE_WIFI_DIRECT import android.net.ConnectivityManager.TYPE_ETHERNET import android.net.ConnectivityManager.TYPE_MOBILE import android.net.ConnectivityManager.TYPE_MOBILE_CBS import android.net.ConnectivityManager.TYPE_MOBILE_DUN import android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY import android.net.ConnectivityManager.TYPE_MOBILE_FOTA import android.net.ConnectivityManager.TYPE_MOBILE_HIPRI import android.net.ConnectivityManager.TYPE_MOBILE_IA import android.net.ConnectivityManager.TYPE_MOBILE_IMS import android.net.ConnectivityManager.TYPE_MOBILE_MMS import android.net.ConnectivityManager.TYPE_MOBILE_SUPL import android.net.ConnectivityManager.TYPE_VPN import android.net.ConnectivityManager.TYPE_WIFI import android.net.ConnectivityManager.TYPE_WIFI_P2P import android.net.ConnectivityManager.TYPE_WIMAX import android.net.EthernetManager import android.net.NetworkInfo.DetailedState.CONNECTED import android.net.NetworkInfo.DetailedState.DISCONNECTED import android.telephony.TelephonyManager import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.server.ConnectivityService.LegacyTypeTracker Loading @@ -36,7 +52,6 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertNull import org.junit.Assert.assertSame import org.junit.Assert.assertTrue import org.junit.Assert.fail import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any Loading @@ -52,88 +67,130 @@ const val UNSUPPORTED_TYPE = TYPE_WIMAX @RunWith(AndroidJUnit4::class) @SmallTest class LegacyTypeTrackerTest { private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL) private val supportedTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_MOBILE, TYPE_MOBILE_SUPL, TYPE_MOBILE_MMS, TYPE_MOBILE_SUPL, TYPE_MOBILE_DUN, TYPE_MOBILE_HIPRI, TYPE_MOBILE_FOTA, TYPE_MOBILE_IMS, TYPE_MOBILE_CBS, TYPE_MOBILE_IA, TYPE_MOBILE_EMERGENCY, TYPE_VPN) private val mMockService = mock(ConnectivityService::class.java).apply { doReturn(false).`when`(this).isDefaultNetwork(any()) } private val mTracker = LegacyTypeTracker(mMockService).apply { supportedTypes.forEach { addSupportedType(it) private val mPm = mock(PackageManager::class.java) private val mContext = mock(Context::class.java).apply { doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI) doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT) doReturn(mPm).`when`(this).packageManager doReturn(mock(EthernetManager::class.java)).`when`(this).getSystemService( Context.ETHERNET_SERVICE) } private val mTm = mock(TelephonyManager::class.java).apply { doReturn(true).`when`(this).isDataCapable } private fun makeTracker() = LegacyTypeTracker(mMockService).apply { loadSupportedTypes(mContext, mTm) } @Test fun testSupportedTypes() { try { mTracker.addSupportedType(supportedTypes[0]) fail("Expected IllegalStateException") } catch (expected: IllegalStateException) {} val tracker = makeTracker() supportedTypes.forEach { assertTrue(mTracker.isTypeSupported(it)) assertTrue(tracker.isTypeSupported(it)) } assertFalse(tracker.isTypeSupported(UNSUPPORTED_TYPE)) } @Test fun testSupportedTypes_NoEthernet() { doReturn(null).`when`(mContext).getSystemService(Context.ETHERNET_SERVICE) assertFalse(makeTracker().isTypeSupported(TYPE_ETHERNET)) } @Test fun testSupportedTypes_NoTelephony() { doReturn(false).`when`(mTm).isDataCapable val tracker = makeTracker() val nonMobileTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_VPN) nonMobileTypes.forEach { assertTrue(tracker.isTypeSupported(it)) } supportedTypes.toSet().minus(nonMobileTypes).forEach { assertFalse(tracker.isTypeSupported(it)) } } @Test fun testSupportedTypes_NoWifiDirect() { doReturn(false).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT) val tracker = makeTracker() assertFalse(tracker.isTypeSupported(TYPE_WIFI_P2P)) supportedTypes.toSet().minus(TYPE_WIFI_P2P).forEach { assertTrue(tracker.isTypeSupported(it)) } assertFalse(mTracker.isTypeSupported(UNSUPPORTED_TYPE)) } @Test fun testSupl() { val tracker = makeTracker() val mobileNai = mock(NetworkAgentInfo::class.java) mTracker.add(TYPE_MOBILE, mobileNai) tracker.add(TYPE_MOBILE, mobileNai) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE) reset(mMockService) mTracker.add(TYPE_MOBILE_SUPL, mobileNai) tracker.add(TYPE_MOBILE_SUPL, mobileNai) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL) reset(mMockService) mTracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */) tracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL) reset(mMockService) mTracker.add(TYPE_MOBILE_SUPL, mobileNai) tracker.add(TYPE_MOBILE_SUPL, mobileNai) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL) reset(mMockService) mTracker.remove(mobileNai, false) tracker.remove(mobileNai, false) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE) } @Test fun testAddNetwork() { val tracker = makeTracker() val mobileNai = mock(NetworkAgentInfo::class.java) val wifiNai = mock(NetworkAgentInfo::class.java) mTracker.add(TYPE_MOBILE, mobileNai) mTracker.add(TYPE_WIFI, wifiNai) assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai) assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai) tracker.add(TYPE_MOBILE, mobileNai) tracker.add(TYPE_WIFI, wifiNai) assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai) assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) // Make sure adding a second NAI does not change the results. val secondMobileNai = mock(NetworkAgentInfo::class.java) mTracker.add(TYPE_MOBILE, secondMobileNai) assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai) assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai) tracker.add(TYPE_MOBILE, secondMobileNai) assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai) assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) // Make sure removing a network that wasn't added for this type is a no-op. mTracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */) assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai) assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai) tracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */) assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai) assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) // Remove the top network for mobile and make sure the second one becomes the network // of record for this type. mTracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */) assertSame(mTracker.getNetworkForType(TYPE_MOBILE), secondMobileNai) assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai) tracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */) assertSame(tracker.getNetworkForType(TYPE_MOBILE), secondMobileNai) assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) // Make sure adding a network for an unsupported type does not register it. mTracker.add(UNSUPPORTED_TYPE, mobileNai) assertNull(mTracker.getNetworkForType(UNSUPPORTED_TYPE)) tracker.add(UNSUPPORTED_TYPE, mobileNai) assertNull(tracker.getNetworkForType(UNSUPPORTED_TYPE)) } @Test fun testBroadcastOnDisconnect() { val tracker = makeTracker() val mobileNai1 = mock(NetworkAgentInfo::class.java) val mobileNai2 = mock(NetworkAgentInfo::class.java) doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1) mTracker.add(TYPE_MOBILE, mobileNai1) tracker.add(TYPE_MOBILE, mobileNai1) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE) reset(mMockService) doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2) mTracker.add(TYPE_MOBILE, mobileNai2) tracker.add(TYPE_MOBILE, mobileNai2) verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt()) mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */) tracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, DISCONNECTED, TYPE_MOBILE) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai2, CONNECTED, TYPE_MOBILE) } Loading Loading
packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml +10 −0 Original line number Diff line number Diff line Loading @@ -42,4 +42,14 @@ --> </string-array> <string-array translatable="false" name="config_legacy_networktype_restore_timers"> <item>2,60000</item><!-- mobile_mms --> <item>3,60000</item><!-- mobile_supl --> <item>4,60000</item><!-- mobile_dun --> <item>5,60000</item><!-- mobile_hipri --> <item>10,60000</item><!-- mobile_fota --> <item>11,60000</item><!-- mobile_ims --> <item>12,60000</item><!-- mobile_cbs --> </string-array> </resources> No newline at end of file
packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml +1 −1 Original line number Diff line number Diff line Loading @@ -17,11 +17,11 @@ <overlayable name="ServiceConnectivityResourcesConfig"> <policy type="product|system|vendor"> <!-- Configuration values for ConnectivityService --> <item type="array" name="config_legacy_networktype_restore_timers"/> <item type="string" name="config_networkCaptivePortalServerUrl"/> <item type="integer" name="config_networkTransitionTimeout"/> <item type="array" name="config_wakeonlan_supported_interfaces"/> </policy> </overlayable> </resources>
services/core/java/com/android/server/ConnectivityService.java +107 −68 Original line number Diff line number Diff line Loading @@ -17,6 +17,10 @@ package com.android.server; import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE; import static android.content.pm.PackageManager.FEATURE_BLUETOOTH; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.FEATURE_WIFI; import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK; import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK; Loading @@ -28,9 +32,23 @@ import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; import static android.net.ConnectivityManager.TYPE_BLUETOOTH; import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_CBS; import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY; import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; import static android.net.ConnectivityManager.TYPE_MOBILE_IA; import static android.net.ConnectivityManager.TYPE_MOBILE_IMS; import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL; import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_PROXY; import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI_P2P; import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.ConnectivityManager.isNetworkTypeValid; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS; Loading Loading @@ -112,7 +130,6 @@ import android.net.Network; import android.net.NetworkAgent; import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkConfig; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkMonitorManager; Loading Loading @@ -626,11 +643,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private UserManager mUserManager; private NetworkConfig[] mNetConfigs; private int mNetworksDefined; // the set of network types that can only be enabled by system/sig apps private List mProtectedNetworks; private List<Integer> mProtectedNetworks; private Set<String> mWolSupportedInterfaces; Loading Loading @@ -720,18 +734,63 @@ public class ConnectivityService extends IConnectivityManager.Stub * They are therefore not thread-safe with respect to each other. * - getNetworkForType() can be called at any time on binder threads. It is synchronized * on mTypeLists to be thread-safe with respect to a concurrent remove call. * - getRestoreTimerForType(type) is also synchronized on mTypeLists. * - dump is thread-safe with respect to concurrent add and remove calls. */ private final ArrayList<NetworkAgentInfo> mTypeLists[]; @NonNull private final ConnectivityService mService; // Restore timers for requestNetworkForFeature (network type -> timer in ms). Types without // an entry have no timer (equivalent to -1). Lazily loaded. @NonNull private ArrayMap<Integer, Integer> mRestoreTimers = new ArrayMap<>(); LegacyTypeTracker(@NonNull ConnectivityService service) { mService = service; mTypeLists = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1]; } public void addSupportedType(int type) { public void loadSupportedTypes(@NonNull Context ctx, @NonNull TelephonyManager tm) { final PackageManager pm = ctx.getPackageManager(); if (pm.hasSystemFeature(FEATURE_WIFI)) { addSupportedType(TYPE_WIFI); } if (pm.hasSystemFeature(FEATURE_WIFI_DIRECT)) { addSupportedType(TYPE_WIFI_P2P); } if (tm.isDataCapable()) { // Telephony does not have granular support for these types: they are either all // supported, or none is supported addSupportedType(TYPE_MOBILE); addSupportedType(TYPE_MOBILE_MMS); addSupportedType(TYPE_MOBILE_SUPL); addSupportedType(TYPE_MOBILE_DUN); addSupportedType(TYPE_MOBILE_HIPRI); addSupportedType(TYPE_MOBILE_FOTA); addSupportedType(TYPE_MOBILE_IMS); addSupportedType(TYPE_MOBILE_CBS); addSupportedType(TYPE_MOBILE_IA); addSupportedType(TYPE_MOBILE_EMERGENCY); } if (pm.hasSystemFeature(FEATURE_BLUETOOTH)) { addSupportedType(TYPE_BLUETOOTH); } if (pm.hasSystemFeature(FEATURE_WATCH)) { // TYPE_PROXY is only used on Wear addSupportedType(TYPE_PROXY); } // Ethernet is often not specified in the configs, although many devices can use it via // USB host adapters. Add it as long as the ethernet service is here. if (ctx.getSystemService(Context.ETHERNET_SERVICE) != null) { addSupportedType(TYPE_ETHERNET); } // Always add TYPE_VPN as a supported type addSupportedType(TYPE_VPN); } private void addSupportedType(int type) { if (mTypeLists[type] != null) { throw new IllegalStateException( "legacy list for type " + type + "already initialized"); Loading @@ -752,6 +811,35 @@ public class ConnectivityService extends IConnectivityManager.Stub return null; } public int getRestoreTimerForType(int type) { synchronized (mTypeLists) { if (mRestoreTimers == null) { mRestoreTimers = loadRestoreTimers(); } return mRestoreTimers.getOrDefault(type, -1); } } private ArrayMap<Integer, Integer> loadRestoreTimers() { final String[] configs = mService.mResources.get().getStringArray( com.android.connectivity.resources.R.array .config_legacy_networktype_restore_timers); final ArrayMap<Integer, Integer> ret = new ArrayMap<>(configs.length); for (final String config : configs) { final String[] splits = TextUtils.split(config, ","); if (splits.length != 2) { logwtf("Invalid restore timer token count: " + config); continue; } try { ret.put(Integer.parseInt(splits[0]), Integer.parseInt(splits[1])); } catch (NumberFormatException e) { logwtf("Invalid restore timer number format: " + config, e); } } return ret; } private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type, boolean isDefaultNetwork) { if (DBG) { Loading Loading @@ -1174,64 +1262,12 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1]; // TODO: What is the "correct" way to do determine if this is a wifi only device? boolean wifiOnly = mSystemProperties.getBoolean("ro.radio.noril", false); log("wifiOnly=" + wifiOnly); String[] naStrings = context.getResources().getStringArray( com.android.internal.R.array.networkAttributes); for (String naString : naStrings) { try { NetworkConfig n = new NetworkConfig(naString); if (VDBG) log("naString=" + naString + " config=" + n); if (n.type > ConnectivityManager.MAX_NETWORK_TYPE) { loge("Error in networkAttributes - ignoring attempt to define type " + n.type); continue; } if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(n.type)) { log("networkAttributes - ignoring mobile as this dev is wifiOnly " + n.type); continue; } if (mNetConfigs[n.type] != null) { loge("Error in networkAttributes - ignoring attempt to redefine type " + n.type); continue; } mLegacyTypeTracker.addSupportedType(n.type); mNetConfigs[n.type] = n; mNetworksDefined++; } catch(Exception e) { // ignore it - leave the entry null } } // Forcibly add TYPE_VPN as a supported type, if it has not already been added via config. if (mNetConfigs[TYPE_VPN] == null) { // mNetConfigs is used only for "restore time", which isn't applicable to VPNs, so we // don't need to add TYPE_VPN to mNetConfigs. mLegacyTypeTracker.addSupportedType(TYPE_VPN); mNetworksDefined++; // used only in the log() statement below. } // Do the same for Ethernet, since it's often not specified in the configs, although many // devices can use it via USB host adapters. if (mNetConfigs[TYPE_ETHERNET] == null && mContext.getSystemService(Context.ETHERNET_SERVICE) != null) { mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET); mNetworksDefined++; } if (VDBG) log("mNetworksDefined=" + mNetworksDefined); mProtectedNetworks = new ArrayList<Integer>(); mLegacyTypeTracker.loadSupportedTypes(mContext, mTelephonyManager); mProtectedNetworks = new ArrayList<>(); int[] protectedNetworks = context.getResources().getIntArray( com.android.internal.R.array.config_protectedNetworks); for (int p : protectedNetworks) { if ((mNetConfigs[p] != null) && (mProtectedNetworks.contains(p) == false)) { if (mLegacyTypeTracker.isTypeSupported(p) && !mProtectedNetworks.contains(p)) { mProtectedNetworks.add(p); } else { if (DBG) loge("Ignoring protectedNetwork " + p); Loading Loading @@ -2640,9 +2676,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // if the system property isn't set, use the value for the apn type int ret = RESTORE_DEFAULT_NETWORK_DELAY; if ((networkType <= ConnectivityManager.MAX_NETWORK_TYPE) && (mNetConfigs[networkType] != null)) { ret = mNetConfigs[networkType].restoreTime; if (mLegacyTypeTracker.isTypeSupported(networkType)) { ret = mLegacyTypeTracker.getRestoreTimerForType(networkType); } return ret; } Loading Loading @@ -4855,6 +4890,10 @@ public class ConnectivityService extends IConnectivityManager.Stub Log.wtf(TAG, s); } private static void logwtf(String s, Throwable t) { Log.wtf(TAG, s, t); } private static void loge(String s) { Log.e(TAG, s); } Loading Loading @@ -8917,13 +8956,13 @@ public class ConnectivityService extends IConnectivityManager.Stub private int transportTypeToLegacyType(int type) { switch (type) { case NetworkCapabilities.TRANSPORT_CELLULAR: return ConnectivityManager.TYPE_MOBILE; return TYPE_MOBILE; case NetworkCapabilities.TRANSPORT_WIFI: return ConnectivityManager.TYPE_WIFI; return TYPE_WIFI; case NetworkCapabilities.TRANSPORT_BLUETOOTH: return ConnectivityManager.TYPE_BLUETOOTH; return TYPE_BLUETOOTH; case NetworkCapabilities.TRANSPORT_ETHERNET: return ConnectivityManager.TYPE_ETHERNET; return TYPE_ETHERNET; default: loge("Unexpected transport in transportTypeToLegacyType: " + type); } Loading
tests/net/java/com/android/server/ConnectivityServiceTest.java +8 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import static android.content.Intent.ACTION_USER_ADDED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.ACTION_USER_UNLOCKED; import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PackageManager.FEATURE_WIFI; import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.content.pm.PackageManager.PERMISSION_DENIED; Loading @@ -42,6 +44,7 @@ import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL; import static android.net.ConnectivityManager.TYPE_PROXY; import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS; Loading Loading @@ -1505,6 +1508,9 @@ public class ConnectivityServiceTest { Looper.prepare(); } mockDefaultPackages(); mockHasSystemFeature(FEATURE_WIFI, true); mockHasSystemFeature(FEATURE_WIFI_DIRECT, true); doReturn(true).when(mTelephonyManager).isDataCapable(); FakeSettingsProvider.clearSettingsProvider(); mServiceContext = new MockContext(InstrumentationRegistry.getContext(), Loading Loading @@ -1829,7 +1835,8 @@ public class ConnectivityServiceTest { assertTrue(mCm.isNetworkSupported(TYPE_WIFI)); assertTrue(mCm.isNetworkSupported(TYPE_MOBILE)); assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_MMS)); assertFalse(mCm.isNetworkSupported(TYPE_MOBILE_FOTA)); assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_FOTA)); assertFalse(mCm.isNetworkSupported(TYPE_PROXY)); // Check that TYPE_ETHERNET is supported. Unlike the asserts above, which only validate our // mocks, this assert exercises the ConnectivityService code path that ensures that Loading
tests/net/java/com/android/server/LegacyTypeTrackerTest.kt +92 −35 Original line number Diff line number Diff line Loading @@ -21,13 +21,29 @@ package com.android.server import android.content.Context import android.content.pm.PackageManager import android.content.pm.PackageManager.FEATURE_WIFI import android.content.pm.PackageManager.FEATURE_WIFI_DIRECT import android.net.ConnectivityManager.TYPE_ETHERNET import android.net.ConnectivityManager.TYPE_MOBILE import android.net.ConnectivityManager.TYPE_MOBILE_CBS import android.net.ConnectivityManager.TYPE_MOBILE_DUN import android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY import android.net.ConnectivityManager.TYPE_MOBILE_FOTA import android.net.ConnectivityManager.TYPE_MOBILE_HIPRI import android.net.ConnectivityManager.TYPE_MOBILE_IA import android.net.ConnectivityManager.TYPE_MOBILE_IMS import android.net.ConnectivityManager.TYPE_MOBILE_MMS import android.net.ConnectivityManager.TYPE_MOBILE_SUPL import android.net.ConnectivityManager.TYPE_VPN import android.net.ConnectivityManager.TYPE_WIFI import android.net.ConnectivityManager.TYPE_WIFI_P2P import android.net.ConnectivityManager.TYPE_WIMAX import android.net.EthernetManager import android.net.NetworkInfo.DetailedState.CONNECTED import android.net.NetworkInfo.DetailedState.DISCONNECTED import android.telephony.TelephonyManager import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.server.ConnectivityService.LegacyTypeTracker Loading @@ -36,7 +52,6 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertNull import org.junit.Assert.assertSame import org.junit.Assert.assertTrue import org.junit.Assert.fail import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any Loading @@ -52,88 +67,130 @@ const val UNSUPPORTED_TYPE = TYPE_WIMAX @RunWith(AndroidJUnit4::class) @SmallTest class LegacyTypeTrackerTest { private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL) private val supportedTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_MOBILE, TYPE_MOBILE_SUPL, TYPE_MOBILE_MMS, TYPE_MOBILE_SUPL, TYPE_MOBILE_DUN, TYPE_MOBILE_HIPRI, TYPE_MOBILE_FOTA, TYPE_MOBILE_IMS, TYPE_MOBILE_CBS, TYPE_MOBILE_IA, TYPE_MOBILE_EMERGENCY, TYPE_VPN) private val mMockService = mock(ConnectivityService::class.java).apply { doReturn(false).`when`(this).isDefaultNetwork(any()) } private val mTracker = LegacyTypeTracker(mMockService).apply { supportedTypes.forEach { addSupportedType(it) private val mPm = mock(PackageManager::class.java) private val mContext = mock(Context::class.java).apply { doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI) doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT) doReturn(mPm).`when`(this).packageManager doReturn(mock(EthernetManager::class.java)).`when`(this).getSystemService( Context.ETHERNET_SERVICE) } private val mTm = mock(TelephonyManager::class.java).apply { doReturn(true).`when`(this).isDataCapable } private fun makeTracker() = LegacyTypeTracker(mMockService).apply { loadSupportedTypes(mContext, mTm) } @Test fun testSupportedTypes() { try { mTracker.addSupportedType(supportedTypes[0]) fail("Expected IllegalStateException") } catch (expected: IllegalStateException) {} val tracker = makeTracker() supportedTypes.forEach { assertTrue(mTracker.isTypeSupported(it)) assertTrue(tracker.isTypeSupported(it)) } assertFalse(tracker.isTypeSupported(UNSUPPORTED_TYPE)) } @Test fun testSupportedTypes_NoEthernet() { doReturn(null).`when`(mContext).getSystemService(Context.ETHERNET_SERVICE) assertFalse(makeTracker().isTypeSupported(TYPE_ETHERNET)) } @Test fun testSupportedTypes_NoTelephony() { doReturn(false).`when`(mTm).isDataCapable val tracker = makeTracker() val nonMobileTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_VPN) nonMobileTypes.forEach { assertTrue(tracker.isTypeSupported(it)) } supportedTypes.toSet().minus(nonMobileTypes).forEach { assertFalse(tracker.isTypeSupported(it)) } } @Test fun testSupportedTypes_NoWifiDirect() { doReturn(false).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT) val tracker = makeTracker() assertFalse(tracker.isTypeSupported(TYPE_WIFI_P2P)) supportedTypes.toSet().minus(TYPE_WIFI_P2P).forEach { assertTrue(tracker.isTypeSupported(it)) } assertFalse(mTracker.isTypeSupported(UNSUPPORTED_TYPE)) } @Test fun testSupl() { val tracker = makeTracker() val mobileNai = mock(NetworkAgentInfo::class.java) mTracker.add(TYPE_MOBILE, mobileNai) tracker.add(TYPE_MOBILE, mobileNai) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE) reset(mMockService) mTracker.add(TYPE_MOBILE_SUPL, mobileNai) tracker.add(TYPE_MOBILE_SUPL, mobileNai) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL) reset(mMockService) mTracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */) tracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL) reset(mMockService) mTracker.add(TYPE_MOBILE_SUPL, mobileNai) tracker.add(TYPE_MOBILE_SUPL, mobileNai) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL) reset(mMockService) mTracker.remove(mobileNai, false) tracker.remove(mobileNai, false) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE) } @Test fun testAddNetwork() { val tracker = makeTracker() val mobileNai = mock(NetworkAgentInfo::class.java) val wifiNai = mock(NetworkAgentInfo::class.java) mTracker.add(TYPE_MOBILE, mobileNai) mTracker.add(TYPE_WIFI, wifiNai) assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai) assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai) tracker.add(TYPE_MOBILE, mobileNai) tracker.add(TYPE_WIFI, wifiNai) assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai) assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) // Make sure adding a second NAI does not change the results. val secondMobileNai = mock(NetworkAgentInfo::class.java) mTracker.add(TYPE_MOBILE, secondMobileNai) assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai) assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai) tracker.add(TYPE_MOBILE, secondMobileNai) assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai) assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) // Make sure removing a network that wasn't added for this type is a no-op. mTracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */) assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai) assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai) tracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */) assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai) assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) // Remove the top network for mobile and make sure the second one becomes the network // of record for this type. mTracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */) assertSame(mTracker.getNetworkForType(TYPE_MOBILE), secondMobileNai) assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai) tracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */) assertSame(tracker.getNetworkForType(TYPE_MOBILE), secondMobileNai) assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) // Make sure adding a network for an unsupported type does not register it. mTracker.add(UNSUPPORTED_TYPE, mobileNai) assertNull(mTracker.getNetworkForType(UNSUPPORTED_TYPE)) tracker.add(UNSUPPORTED_TYPE, mobileNai) assertNull(tracker.getNetworkForType(UNSUPPORTED_TYPE)) } @Test fun testBroadcastOnDisconnect() { val tracker = makeTracker() val mobileNai1 = mock(NetworkAgentInfo::class.java) val mobileNai2 = mock(NetworkAgentInfo::class.java) doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1) mTracker.add(TYPE_MOBILE, mobileNai1) tracker.add(TYPE_MOBILE, mobileNai1) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE) reset(mMockService) doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2) mTracker.add(TYPE_MOBILE, mobileNai2) tracker.add(TYPE_MOBILE, mobileNai2) verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt()) mTracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */) tracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, DISCONNECTED, TYPE_MOBILE) verify(mMockService).sendLegacyNetworkBroadcast(mobileNai2, CONNECTED, TYPE_MOBILE) } Loading