Loading AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ permissions added would cause crashes on startup unless they are also added to the privileged permissions whitelist for that package. --> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> Loading res/values/config.xml +3 −0 Original line number Diff line number Diff line Loading @@ -43,4 +43,7 @@ <!-- Customized default DNS Servers address. --> <string-array name="config_default_dns_servers" translatable="false"> </string-array> <!-- Set to true if NetworkMonitor needs to load the resource by neighbor mcc when device doesn't have a SIM card inserted. --> <bool name="config_no_sim_card_uses_neighbor_mcc">false</bool> </resources> res/values/overlayable.xml +1 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ <item type="string" name="config_captive_portal_http_url"/> <item type="string" name="config_captive_portal_https_url"/> <item type="array" name="config_captive_portal_fallback_urls"/> <item type="bool" name="config_no_sim_card_uses_neighbor_mcc"/> <!-- Configuration value for DhcpResults --> <item type="array" name="config_default_dns_servers"/> </policy> Loading src/com/android/server/connectivity/NetworkMonitor.java +97 −2 Original line number Diff line number Diff line Loading @@ -76,6 +76,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.DnsResolver; Loading @@ -101,11 +103,19 @@ import android.net.wifi.WifiManager; import android.os.Build; import android.os.Bundle; import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.telephony.AccessNetworkConstants; import android.telephony.CellIdentityNr; import android.telephony.CellInfo; import android.telephony.CellInfoGsm; import android.telephony.CellInfoLte; import android.telephony.CellInfoNr; import android.telephony.CellInfoTdscdma; import android.telephony.CellInfoWcdma; import android.telephony.CellSignalStrength; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; Loading @@ -116,6 +126,7 @@ import android.util.Log; import android.util.Pair; import androidx.annotation.ArrayRes; import androidx.annotation.BoolRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; Loading @@ -141,8 +152,10 @@ import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.StringJoiner; import java.util.UUID; Loading Loading @@ -1318,8 +1331,78 @@ public class NetworkMonitor extends StateMachine { CAPTIVE_PORTAL_USE_HTTPS, 1) == 1; } @Nullable private String getMccFromCellInfo(final CellInfo cell) { if (cell instanceof CellInfoGsm) { return ((CellInfoGsm) cell).getCellIdentity().getMccString(); } else if (cell instanceof CellInfoLte) { return ((CellInfoLte) cell).getCellIdentity().getMccString(); } else if (cell instanceof CellInfoWcdma) { return ((CellInfoWcdma) cell).getCellIdentity().getMccString(); } else if (cell instanceof CellInfoTdscdma) { return ((CellInfoTdscdma) cell).getCellIdentity().getMccString(); } else if (cell instanceof CellInfoNr) { return ((CellIdentityNr) ((CellInfoNr) cell).getCellIdentity()).getMccString(); } else { return null; } } /** * Return location mcc. */ @VisibleForTesting @Nullable protected String getLocationMcc() { // Adding this check is because the new permission won't be granted by mainline update, // the new permission only be granted by OTA for current design. Tracking: b/145774617. if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_DENIED) { log("getLocationMcc : NetworkStack does not hold ACCESS_FINE_LOCATION"); return null; } try { final List<CellInfo> cells = mTelephonyManager.getAllCellInfo(); final Map<String, Integer> countryCodeMap = new HashMap<>(); int maxCount = 0; for (final CellInfo cell : cells) { final String mcc = getMccFromCellInfo(cell); if (mcc != null) { final int count = countryCodeMap.getOrDefault(mcc, 0) + 1; countryCodeMap.put(mcc, count); } } // Return the MCC which occurs most. if (countryCodeMap.size() <= 0) return null; return Collections.max(countryCodeMap.entrySet(), (e1, e2) -> e1.getValue().compareTo(e2.getValue())).getKey(); } catch (SecurityException e) { log("Permission is not granted:" + e); return null; } } @VisibleForTesting protected Context getContextByMccIfNoSimCardOrDefault() { final boolean useNeighborResource = getResBooleanConfig(mContext, R.bool.config_no_sim_card_uses_neighbor_mcc); if (!useNeighborResource || TelephonyManager.SIM_STATE_READY == mTelephonyManager.getSimState()) { return mContext; } final String mcc = getLocationMcc(); if (TextUtils.isEmpty(mcc)) { return mContext; } final Configuration config = mContext.getResources().getConfiguration(); config.mcc = Integer.parseInt(mcc); return mContext.createConfigurationContext(config); } private String getCaptivePortalServerHttpsUrl() { return getSettingFromResource(mContext, R.string.config_captive_portal_https_url, final Context targetContext = getContextByMccIfNoSimCardOrDefault(); return getSettingFromResource(targetContext, R.string.config_captive_portal_https_url, R.string.default_captive_portal_https_url, CAPTIVE_PORTAL_HTTPS_URL); } Loading Loading @@ -1350,6 +1433,17 @@ public class NetworkMonitor extends StateMachine { } } @VisibleForTesting protected boolean getResBooleanConfig(@NonNull final Context context, @BoolRes int configResource) { final Resources res = context.getResources(); try { return res.getBoolean(configResource); } catch (Resources.NotFoundException e) { return false; } } /** * Get the captive portal server HTTP URL that is configured on the device. * Loading @@ -1358,7 +1452,8 @@ public class NetworkMonitor extends StateMachine { * on one URL that can be used, while NetworkMonitor may implement more complex logic. */ public String getCaptivePortalServerHttpUrl() { return getSettingFromResource(mContext, R.string.config_captive_portal_http_url, final Context targetContext = getContextByMccIfNoSimCardOrDefault(); return getSettingFromResource(targetContext, R.string.config_captive_portal_http_url, R.string.default_captive_portal_http_url, CAPTIVE_PORTAL_HTTP_URL); } Loading tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +52 −0 Original line number Diff line number Diff line Loading @@ -64,7 +64,10 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.DnsResolver; Loading @@ -87,6 +90,11 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; import android.telephony.CellIdentityGsm; import android.telephony.CellIdentityLte; import android.telephony.CellInfo; import android.telephony.CellInfoGsm; import android.telephony.CellInfoLte; import android.telephony.CellSignalStrength; import android.telephony.TelephonyManager; import android.util.ArrayMap; Loading Loading @@ -133,6 +141,7 @@ public class NetworkMonitorTest { private static final String LOCATION_HEADER = "location"; private @Mock Context mContext; private @Mock Configuration mConfiguration; private @Mock Resources mResources; private @Mock IpConnectivityLog mLogger; private @Mock SharedLog mValidationLogger; Loading Loading @@ -484,6 +493,10 @@ public class NetworkMonitorTest { assertTrue("NetworkMonitor did not quit after " + HANDLER_TIMEOUT_MS + "ms", mQuitCv.block(HANDLER_TIMEOUT_MS)); } protected Context getContext() { return mContext; } } private WrappedNetworkMonitor makeMonitor(NetworkCapabilities nc) { Loading Loading @@ -512,6 +525,45 @@ public class NetworkMonitorTest { HandlerUtilsKt.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS); } @Test public void testGetLocationMcc() throws Exception { final WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); doReturn(PackageManager.PERMISSION_DENIED).when(mContext).checkPermission( eq(android.Manifest.permission.ACCESS_FINE_LOCATION), anyInt(), anyInt()); assertNull(wnm.getLocationMcc()); doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkPermission( eq(android.Manifest.permission.ACCESS_FINE_LOCATION), anyInt(), anyInt()); doReturn(new ContextWrapper(mContext)).when(mContext).createConfigurationContext(any()); // Prepare CellInfo and check if the vote mechanism is working or not. final CellInfoGsm cellInfoGsm1 = new CellInfoGsm(); final CellInfoGsm cellInfoGsm2 = new CellInfoGsm(); final CellInfoLte cellInfoLte = new CellInfoLte(); final CellIdentityGsm cellIdentityGsm = new CellIdentityGsm(0, 0, 0, 0, "460", "01", "", ""); final CellIdentityLte cellIdentityLte = new CellIdentityLte(0, 0, 0, 0, 0, "466", "01", "", ""); cellInfoGsm1.setCellIdentity(cellIdentityGsm); cellInfoGsm2.setCellIdentity(cellIdentityGsm); cellInfoLte.setCellIdentity(cellIdentityLte); final List<CellInfo> cellList = new ArrayList<CellInfo>(); cellList.add(cellInfoGsm1); cellList.add(cellInfoGsm2); cellList.add(cellInfoLte); doReturn(cellList).when(mTelephony).getAllCellInfo(); // The count of 460 is 2 and the count of 466 is 1, so the getLocationMcc() should return // 460. assertEquals("460", wnm.getLocationMcc()); // getContextByMccIfNoSimCardOrDefault() shouldn't return mContext when using neighbor mcc // is enabled and the sim is not ready. doReturn(true).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc); doReturn(TelephonyManager.SIM_STATE_ABSENT).when(mTelephony).getSimState(); doReturn(mConfiguration).when(mResources).getConfiguration(); assertEquals(460, wnm.getContextByMccIfNoSimCardOrDefault().getResources().getConfiguration().mcc); doReturn(false).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc); assertEquals(wnm.getContext(), wnm.getContextByMccIfNoSimCardOrDefault()); } @Test public void testGetIntSetting() throws Exception { WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); Loading Loading
AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ permissions added would cause crashes on startup unless they are also added to the privileged permissions whitelist for that package. --> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> Loading
res/values/config.xml +3 −0 Original line number Diff line number Diff line Loading @@ -43,4 +43,7 @@ <!-- Customized default DNS Servers address. --> <string-array name="config_default_dns_servers" translatable="false"> </string-array> <!-- Set to true if NetworkMonitor needs to load the resource by neighbor mcc when device doesn't have a SIM card inserted. --> <bool name="config_no_sim_card_uses_neighbor_mcc">false</bool> </resources>
res/values/overlayable.xml +1 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ <item type="string" name="config_captive_portal_http_url"/> <item type="string" name="config_captive_portal_https_url"/> <item type="array" name="config_captive_portal_fallback_urls"/> <item type="bool" name="config_no_sim_card_uses_neighbor_mcc"/> <!-- Configuration value for DhcpResults --> <item type="array" name="config_default_dns_servers"/> </policy> Loading
src/com/android/server/connectivity/NetworkMonitor.java +97 −2 Original line number Diff line number Diff line Loading @@ -76,6 +76,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.DnsResolver; Loading @@ -101,11 +103,19 @@ import android.net.wifi.WifiManager; import android.os.Build; import android.os.Bundle; import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.telephony.AccessNetworkConstants; import android.telephony.CellIdentityNr; import android.telephony.CellInfo; import android.telephony.CellInfoGsm; import android.telephony.CellInfoLte; import android.telephony.CellInfoNr; import android.telephony.CellInfoTdscdma; import android.telephony.CellInfoWcdma; import android.telephony.CellSignalStrength; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; Loading @@ -116,6 +126,7 @@ import android.util.Log; import android.util.Pair; import androidx.annotation.ArrayRes; import androidx.annotation.BoolRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; Loading @@ -141,8 +152,10 @@ import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.StringJoiner; import java.util.UUID; Loading Loading @@ -1318,8 +1331,78 @@ public class NetworkMonitor extends StateMachine { CAPTIVE_PORTAL_USE_HTTPS, 1) == 1; } @Nullable private String getMccFromCellInfo(final CellInfo cell) { if (cell instanceof CellInfoGsm) { return ((CellInfoGsm) cell).getCellIdentity().getMccString(); } else if (cell instanceof CellInfoLte) { return ((CellInfoLte) cell).getCellIdentity().getMccString(); } else if (cell instanceof CellInfoWcdma) { return ((CellInfoWcdma) cell).getCellIdentity().getMccString(); } else if (cell instanceof CellInfoTdscdma) { return ((CellInfoTdscdma) cell).getCellIdentity().getMccString(); } else if (cell instanceof CellInfoNr) { return ((CellIdentityNr) ((CellInfoNr) cell).getCellIdentity()).getMccString(); } else { return null; } } /** * Return location mcc. */ @VisibleForTesting @Nullable protected String getLocationMcc() { // Adding this check is because the new permission won't be granted by mainline update, // the new permission only be granted by OTA for current design. Tracking: b/145774617. if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_DENIED) { log("getLocationMcc : NetworkStack does not hold ACCESS_FINE_LOCATION"); return null; } try { final List<CellInfo> cells = mTelephonyManager.getAllCellInfo(); final Map<String, Integer> countryCodeMap = new HashMap<>(); int maxCount = 0; for (final CellInfo cell : cells) { final String mcc = getMccFromCellInfo(cell); if (mcc != null) { final int count = countryCodeMap.getOrDefault(mcc, 0) + 1; countryCodeMap.put(mcc, count); } } // Return the MCC which occurs most. if (countryCodeMap.size() <= 0) return null; return Collections.max(countryCodeMap.entrySet(), (e1, e2) -> e1.getValue().compareTo(e2.getValue())).getKey(); } catch (SecurityException e) { log("Permission is not granted:" + e); return null; } } @VisibleForTesting protected Context getContextByMccIfNoSimCardOrDefault() { final boolean useNeighborResource = getResBooleanConfig(mContext, R.bool.config_no_sim_card_uses_neighbor_mcc); if (!useNeighborResource || TelephonyManager.SIM_STATE_READY == mTelephonyManager.getSimState()) { return mContext; } final String mcc = getLocationMcc(); if (TextUtils.isEmpty(mcc)) { return mContext; } final Configuration config = mContext.getResources().getConfiguration(); config.mcc = Integer.parseInt(mcc); return mContext.createConfigurationContext(config); } private String getCaptivePortalServerHttpsUrl() { return getSettingFromResource(mContext, R.string.config_captive_portal_https_url, final Context targetContext = getContextByMccIfNoSimCardOrDefault(); return getSettingFromResource(targetContext, R.string.config_captive_portal_https_url, R.string.default_captive_portal_https_url, CAPTIVE_PORTAL_HTTPS_URL); } Loading Loading @@ -1350,6 +1433,17 @@ public class NetworkMonitor extends StateMachine { } } @VisibleForTesting protected boolean getResBooleanConfig(@NonNull final Context context, @BoolRes int configResource) { final Resources res = context.getResources(); try { return res.getBoolean(configResource); } catch (Resources.NotFoundException e) { return false; } } /** * Get the captive portal server HTTP URL that is configured on the device. * Loading @@ -1358,7 +1452,8 @@ public class NetworkMonitor extends StateMachine { * on one URL that can be used, while NetworkMonitor may implement more complex logic. */ public String getCaptivePortalServerHttpUrl() { return getSettingFromResource(mContext, R.string.config_captive_portal_http_url, final Context targetContext = getContextByMccIfNoSimCardOrDefault(); return getSettingFromResource(targetContext, R.string.config_captive_portal_http_url, R.string.default_captive_portal_http_url, CAPTIVE_PORTAL_HTTP_URL); } Loading
tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +52 −0 Original line number Diff line number Diff line Loading @@ -64,7 +64,10 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.DnsResolver; Loading @@ -87,6 +90,11 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; import android.telephony.CellIdentityGsm; import android.telephony.CellIdentityLte; import android.telephony.CellInfo; import android.telephony.CellInfoGsm; import android.telephony.CellInfoLte; import android.telephony.CellSignalStrength; import android.telephony.TelephonyManager; import android.util.ArrayMap; Loading Loading @@ -133,6 +141,7 @@ public class NetworkMonitorTest { private static final String LOCATION_HEADER = "location"; private @Mock Context mContext; private @Mock Configuration mConfiguration; private @Mock Resources mResources; private @Mock IpConnectivityLog mLogger; private @Mock SharedLog mValidationLogger; Loading Loading @@ -484,6 +493,10 @@ public class NetworkMonitorTest { assertTrue("NetworkMonitor did not quit after " + HANDLER_TIMEOUT_MS + "ms", mQuitCv.block(HANDLER_TIMEOUT_MS)); } protected Context getContext() { return mContext; } } private WrappedNetworkMonitor makeMonitor(NetworkCapabilities nc) { Loading Loading @@ -512,6 +525,45 @@ public class NetworkMonitorTest { HandlerUtilsKt.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS); } @Test public void testGetLocationMcc() throws Exception { final WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); doReturn(PackageManager.PERMISSION_DENIED).when(mContext).checkPermission( eq(android.Manifest.permission.ACCESS_FINE_LOCATION), anyInt(), anyInt()); assertNull(wnm.getLocationMcc()); doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkPermission( eq(android.Manifest.permission.ACCESS_FINE_LOCATION), anyInt(), anyInt()); doReturn(new ContextWrapper(mContext)).when(mContext).createConfigurationContext(any()); // Prepare CellInfo and check if the vote mechanism is working or not. final CellInfoGsm cellInfoGsm1 = new CellInfoGsm(); final CellInfoGsm cellInfoGsm2 = new CellInfoGsm(); final CellInfoLte cellInfoLte = new CellInfoLte(); final CellIdentityGsm cellIdentityGsm = new CellIdentityGsm(0, 0, 0, 0, "460", "01", "", ""); final CellIdentityLte cellIdentityLte = new CellIdentityLte(0, 0, 0, 0, 0, "466", "01", "", ""); cellInfoGsm1.setCellIdentity(cellIdentityGsm); cellInfoGsm2.setCellIdentity(cellIdentityGsm); cellInfoLte.setCellIdentity(cellIdentityLte); final List<CellInfo> cellList = new ArrayList<CellInfo>(); cellList.add(cellInfoGsm1); cellList.add(cellInfoGsm2); cellList.add(cellInfoLte); doReturn(cellList).when(mTelephony).getAllCellInfo(); // The count of 460 is 2 and the count of 466 is 1, so the getLocationMcc() should return // 460. assertEquals("460", wnm.getLocationMcc()); // getContextByMccIfNoSimCardOrDefault() shouldn't return mContext when using neighbor mcc // is enabled and the sim is not ready. doReturn(true).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc); doReturn(TelephonyManager.SIM_STATE_ABSENT).when(mTelephony).getSimState(); doReturn(mConfiguration).when(mResources).getConfiguration(); assertEquals(460, wnm.getContextByMccIfNoSimCardOrDefault().getResources().getConfiguration().mcc); doReturn(false).when(mResources).getBoolean(R.bool.config_no_sim_card_uses_neighbor_mcc); assertEquals(wnm.getContext(), wnm.getContextByMccIfNoSimCardOrDefault()); } @Test public void testGetIntSetting() throws Exception { WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); Loading