From 4b198cb8c3e80d3b1af969391a16595920564df9 Mon Sep 17 00:00:00 2001 From: markchien Date: Mon, 8 Jun 2020 11:30:18 +0800 Subject: [PATCH 1/2] Pass entitlement configuration to Settings for entitlement check Tethering resource configuration is move from framwork to tethering module. Since tethering resource would not be accessible from outside of tethering module, EntitlementManager would tell Settings the entitlement configuration via intent extra when run entitlement check. Bug: 146918263 Test: atest TetheringTests Change-Id: I6f23553bb1da5f0b767f920b32a86fafb9e00b9e Merged-In: I6f23553bb1da5f0b767f920b32a86fafb9e00b9e --- .../src/android/net/TetheringConstants.java | 38 +++++++++++-- .../tethering/EntitlementManager.java | 33 +++++++----- .../tethering/TetheringConfiguration.java | 10 ++-- .../tethering/EntitlementManagerTest.java | 53 ++++++++++++++++++- .../tethering/TetheringConfigurationTest.java | 10 +++- 5 files changed, 122 insertions(+), 22 deletions(-) diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java index fd6f171487a93..f14def6a3a02a 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java @@ -37,8 +37,8 @@ public final class TetheringConstants { private TetheringConstants() { } /** - * Extra used for communicating with the TetherService. Includes the type of tethering to - * enable if any. + * Extra used for communicating with the TetherService and TetherProvisioningActivity. + * Includes the type of tethering to enable if any. */ public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; /** @@ -56,8 +56,38 @@ public final class TetheringConstants { */ public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; /** - * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver} - * which will receive provisioning results. Can be left empty. + * Extra used for communicating with the TetherService and TetherProvisioningActivity. + * Contains the {@link ResultReceiver} which will receive provisioning results. + * Can not be empty. */ public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; + + /** + * Extra used for communicating with the TetherService and TetherProvisioningActivity. + * Contains the subId of current active cellular upstream. + * @hide + */ + public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID"; + + /** + * Extra used for telling TetherProvisioningActivity the entitlement package name and class + * name to start UI entitlement check. + * @hide + */ + public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME = + "android.net.extra.TETHER_UI_PROVISIONING_APP_NAME"; + + /** + * Extra used for telling TetherService the intent action to start silent entitlement check. + * @hide + */ + public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION = + "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION"; + + /** + * Extra used for TetherService to receive the response of provisioning check. + * @hide + */ + public static final String EXTRA_TETHER_PROVISIONING_RESPONSE = + "android.net.extra.TETHER_PROVISIONING_RESPONSE"; } diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index 3c6e8d88ed13a..9dace709d734d 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -19,6 +19,10 @@ package com.android.networkstack.tethering; import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; +import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; +import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; +import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; +import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_INVALID; @@ -69,7 +73,6 @@ public class EntitlementManager { protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; private static final String ACTION_PROVISIONING_ALARM = "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM"; - private static final String EXTRA_SUBID = "subId"; private final ComponentName mSilentProvisioningService; private static final int MS_PER_HOUR = 60 * 60 * 1000; @@ -197,9 +200,9 @@ public class EntitlementManager { // till upstream change to cellular. if (mUsingCellularAsUpstream) { if (showProvisioningUi) { - runUiTetherProvisioning(downstreamType, config.activeDataSubId); + runUiTetherProvisioning(downstreamType, config); } else { - runSilentTetherProvisioning(downstreamType, config.activeDataSubId); + runSilentTetherProvisioning(downstreamType, config); } mNeedReRunProvisioningUi = false; } else { @@ -262,9 +265,9 @@ public class EntitlementManager { if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) { if (mNeedReRunProvisioningUi) { mNeedReRunProvisioningUi = false; - runUiTetherProvisioning(downstream, config.activeDataSubId); + runUiTetherProvisioning(downstream, config); } else { - runSilentTetherProvisioning(downstream, config.activeDataSubId); + runSilentTetherProvisioning(downstream, config); } } } @@ -361,7 +364,7 @@ public class EntitlementManager { * @param subId default data subscription ID. */ @VisibleForTesting - protected void runSilentTetherProvisioning(int type, int subId) { + protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) { if (DBG) mLog.i("runSilentTetherProvisioning: " + type); // For silent provisioning, settings would stop tethering when entitlement fail. ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null); @@ -369,17 +372,20 @@ public class EntitlementManager { Intent intent = new Intent(); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); intent.putExtra(EXTRA_RUN_PROVISION, true); + intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi); + intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_SUBID, subId); + intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); intent.setComponent(mSilentProvisioningService); // Only admin user can change tethering and SilentTetherProvisioning don't need to // show UI, it is fine to always start setting's background service as system user. mContext.startService(intent); + return intent; } - private void runUiTetherProvisioning(int type, int subId) { + private void runUiTetherProvisioning(int type, final TetheringConfiguration config) { ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null); - runUiTetherProvisioning(type, subId, receiver); + runUiTetherProvisioning(type, config, receiver); } /** @@ -389,17 +395,20 @@ public class EntitlementManager { * @param receiver to receive entitlement check result. */ @VisibleForTesting - protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { + protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config, + ResultReceiver receiver) { if (DBG) mLog.i("runUiTetherProvisioning: " + type); Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); + intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); - intent.putExtra(EXTRA_SUBID, subId); + intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Only launch entitlement UI for system user. Entitlement UI should not appear for other // user because only admin user is allowed to change tethering. mContext.startActivity(intent); + return intent; } // Not needed to check if this don't run on the handler thread because it's private. @@ -631,7 +640,7 @@ public class EntitlementManager { receiver.send(cacheValue, null); } else { ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver); - runUiTetherProvisioning(downstream, config.activeDataSubId, proxy); + runUiTetherProvisioning(downstream, config, proxy); } } } diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 1e94de12ce110..18b2b7804fb0e 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -105,6 +105,7 @@ public class TetheringConfiguration { public final String[] provisioningApp; public final String provisioningAppNoUi; public final int provisioningCheckPeriod; + public final String provisioningResponse; public final int activeDataSubId; @@ -141,10 +142,13 @@ public class TetheringConfiguration { enableLegacyDhcpServer = getEnableLegacyDhcpServer(res); provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app); - provisioningAppNoUi = getProvisioningAppNoUi(res); + provisioningAppNoUi = getResourceString(res, + R.string.config_mobile_hotspot_provision_app_no_ui); provisioningCheckPeriod = getResourceInteger(res, R.integer.config_mobile_hotspot_provision_check_period, 0 /* No periodic re-check */); + provisioningResponse = getResourceString(res, + R.string.config_mobile_hotspot_provision_response); mOffloadPollInterval = getResourceInteger(res, R.integer.config_tether_offload_poll_interval, @@ -341,9 +345,9 @@ public class TetheringConfiguration { return copy(LEGACY_DHCP_DEFAULT_RANGE); } - private static String getProvisioningAppNoUi(Resources res) { + private static String getResourceString(Resources res, final int resId) { try { - return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui); + return res.getString(resId); } catch (Resources.NotFoundException e) { return ""; } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java index 72fa916b9e425..354e75356e9f4 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java @@ -16,8 +16,16 @@ package com.android.networkstack.tethering; +import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; +import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; +import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; +import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; +import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; +import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; +import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; +import static android.net.TetheringManager.TETHERING_INVALID; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; @@ -44,6 +52,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.Intent; import android.content.res.Resources; import android.net.util.SharedLog; import android.os.Bundle; @@ -53,6 +62,7 @@ import android.os.ResultReceiver; import android.os.SystemProperties; import android.os.test.TestLooper; import android.provider.DeviceConfig; +import android.provider.Settings; import android.telephony.CarrierConfigManager; import androidx.test.filters.SmallTest; @@ -76,6 +86,7 @@ public final class EntitlementManagerTest { private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; + private static final String PROVISIONING_APP_RESPONSE = "app_response"; @Mock private CarrierConfigManager mCarrierConfigManager; @Mock private Context mContext; @@ -122,15 +133,51 @@ public final class EntitlementManagerTest { } @Override - protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) { + protected Intent runUiTetherProvisioning(int type, + final TetheringConfiguration config, final ResultReceiver receiver) { + Intent intent = super.runUiTetherProvisioning(type, config, receiver); + assertUiTetherProvisioningIntent(type, config, receiver, intent); uiProvisionCount++; receiver.send(fakeEntitlementResult, null); + return intent; + } + + private void assertUiTetherProvisioningIntent(int type, final TetheringConfiguration config, + final ResultReceiver receiver, final Intent intent) { + assertEquals(Settings.ACTION_TETHER_PROVISIONING_UI, intent.getAction()); + assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); + final String[] appName = intent.getStringArrayExtra( + EXTRA_TETHER_UI_PROVISIONING_APP_NAME); + assertEquals(PROVISIONING_APP_NAME.length, appName.length); + for (int i = 0; i < PROVISIONING_APP_NAME.length; i++) { + assertEquals(PROVISIONING_APP_NAME[i], appName[i]); + } + assertEquals(receiver, intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK)); + assertEquals(config.activeDataSubId, + intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); } @Override - protected void runSilentTetherProvisioning(int type, int subId) { + protected Intent runSilentTetherProvisioning(int type, + final TetheringConfiguration config) { + Intent intent = super.runSilentTetherProvisioning(type, config); + assertSilentTetherProvisioning(type, config, intent); silentProvisionCount++; addDownstreamMapping(type, fakeEntitlementResult); + return intent; + } + + private void assertSilentTetherProvisioning(int type, final TetheringConfiguration config, + final Intent intent) { + assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID)); + assertEquals(true, intent.getBooleanExtra(EXTRA_RUN_PROVISION, false)); + assertEquals(PROVISIONING_NO_UI_APP_NAME, + intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION)); + assertEquals(PROVISIONING_APP_RESPONSE, + intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE)); + assertTrue(intent.hasExtra(EXTRA_PROVISION_CALLBACK)); + assertEquals(config.activeDataSubId, + intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); } } @@ -187,6 +234,8 @@ public final class EntitlementManagerTest { .thenReturn(PROVISIONING_APP_NAME); when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) .thenReturn(PROVISIONING_NO_UI_APP_NAME); + when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn( + PROVISIONING_APP_RESPONSE); // Act like the CarrierConfigManager is present and ready unless told otherwise. when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) .thenReturn(mCarrierConfigManager); diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index 3f4cfe7afb47b..a9ac4e2851f3a 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -61,6 +61,8 @@ public class TetheringConfigurationTest { private final SharedLog mLog = new SharedLog("TetheringConfigurationTest"); private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; + private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; + private static final String PROVISIONING_APP_RESPONSE = "app_response"; @Mock private Context mContext; @Mock private TelephonyManager mTelephonyManager; @Mock private Resources mResources; @@ -388,6 +390,8 @@ public class TetheringConfigurationTest { new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId); assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]); assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]); + assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME); + assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE); } private void setUpResourceForSubId() { @@ -403,6 +407,10 @@ public class TetheringConfigurationTest { new int[0]); when(mResourcesForSubId.getStringArray( R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME); + when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) + .thenReturn(PROVISIONING_NO_UI_APP_NAME); + when(mResourcesForSubId.getString( + R.string.config_mobile_hotspot_provision_response)).thenReturn( + PROVISIONING_APP_RESPONSE); } - } From b39a7b35cb15b47ef92f2aeebc5c36131b4f929d Mon Sep 17 00:00:00 2001 From: markchien Date: Wed, 3 Jun 2020 12:27:37 +0800 Subject: [PATCH 2/2] Protect invalid entitlement app configuration There is a protection in Settings that Settings would gray out if tethering requires entitlement check but the entitlement app is invalid. Tethering resource is moved from framework to tethering module, so Settings can not fetch entitlement app name anymore. In this change, tethering module would check whether entitltement app package name is exsited if entitlement check is needed. Tethering would be not supported (Settings tethering option would be hidded) if entitlement app is not installed. After moving the protection into tethering module, TetherUtil#isProvisioningNeeded is no longer needed. Because The only use case is Settings wants to gray out tethering setting when entitltement check is needed but entitlement app is invalid. Bug: 146918263 Test: atest TetheringCoverageTests Change-Id: I9a5ff5dbc1db3f3be7fcd7146862a16b373507e6 Merged-In: I9a5ff5dbc1db3f3be7fcd7146862a16b373507e6 --- .../com/android/settingslib/TetherUtil.java | 33 ------------------- .../android/settingslib/TetherUtilTest.java | 7 ---- .../networkstack/tethering/Tethering.java | 28 +++++++++++++--- .../tethering/TetheringDependencies.java | 9 +++++ .../networkstack/tethering/TetheringTest.java | 30 +++++++++++++++++ 5 files changed, 63 insertions(+), 44 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java index c3993e9063b26..dc9384aa7c5dd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java +++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java @@ -19,42 +19,9 @@ import static android.os.UserManager.DISALLOW_CONFIG_TETHERING; import android.content.Context; import android.net.ConnectivityManager; -import android.os.SystemProperties; import android.os.UserHandle; -import android.telephony.CarrierConfigManager; - -import androidx.annotation.VisibleForTesting; public class TetherUtil { - - @VisibleForTesting - static boolean isEntitlementCheckRequired(Context context) { - final CarrierConfigManager configManager = (CarrierConfigManager) context - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager == null || configManager.getConfig() == null) { - // return service default - return true; - } - return configManager.getConfig().getBoolean(CarrierConfigManager - .KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL); - } - - public static boolean isProvisioningNeeded(Context context) { - // Keep in sync with other usage of config_mobile_hotspot_provision_app. - // ConnectivityManager#enforceTetherChangePermission - String[] provisionApp = context.getResources().getStringArray( - com.android.internal.R.array.config_mobile_hotspot_provision_app); - if (SystemProperties.getBoolean("net.tethering.noprovisioning", false) - || provisionApp == null) { - return false; - } - // Check carrier config for entitlement checks - if (isEntitlementCheckRequired(context) == false) { - return false; - } - return (provisionApp.length == 2); - } - public static boolean isTetherAvailable(Context context) { final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class); final boolean tetherConfigDisallowed = RestrictedLockUtilsInternal diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java index 0ca779162ef20..df08809b5601f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java @@ -59,13 +59,6 @@ public class TetherUtilTest { .when(mContext).getSystemService(Context.USER_SERVICE); } - @Test - public void isEntitlementCheckRequired_noConfigManager_returnTrue() { - doReturn(null).when(mContext).getSystemService(Context.CARRIER_CONFIG_SERVICE); - - assertThat(TetherUtil.isEntitlementCheckRequired(mContext)).isTrue(); - } - @Test public void isTetherAvailable_supported_configDisallowed_hasUserRestriction_returnTrue() { setupIsTetherAvailable(true, true, true); diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java index 71ab1767643b0..8d720e7539f35 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -18,6 +18,7 @@ package com.android.networkstack.tethering; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.NETWORK_STACK; +import static android.content.pm.PackageManager.GET_ACTIVITIES; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; @@ -71,6 +72,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; import android.net.EthernetManager; @@ -110,7 +112,6 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceSpecificException; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -811,11 +812,30 @@ public class Tethering { } } + private boolean isProvisioningNeededButUnavailable() { + return isTetherProvisioningRequired() && !doesEntitlementPackageExist(); + } + boolean isTetherProvisioningRequired() { final TetheringConfiguration cfg = mConfig; return mEntitlementMgr.isTetherProvisioningRequired(cfg); } + private boolean doesEntitlementPackageExist() { + // provisioningApp must contain package and class name. + if (mConfig.provisioningApp.length != 2) { + return false; + } + + final PackageManager pm = mContext.getPackageManager(); + try { + pm.getPackageInfo(mConfig.provisioningApp[0], GET_ACTIVITIES); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + return true; + } + // TODO: Figure out how to update for local hotspot mode interfaces. private void sendTetherStateChangedBroadcast() { if (!isTetheringSupported()) return; @@ -2175,14 +2195,14 @@ public class Tethering { // gservices could set the secure setting to 1 though to enable it on a build where it // had previously been turned off. boolean isTetheringSupported() { - final int defaultVal = - SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1; + final int defaultVal = mDeps.isTetheringDenied() ? 0 : 1; final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; final boolean tetherEnabledInSettings = tetherSupported && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); - return tetherEnabledInSettings && hasTetherableConfiguration(); + return tetherEnabledInSettings && hasTetherableConfiguration() + && !isProvisioningNeededButUnavailable(); } void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) { diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java index 8f4b964323c61..131a5fbf2abe3 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java @@ -26,6 +26,8 @@ import android.net.util.SharedLog; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.SystemProperties; +import android.text.TextUtils; import androidx.annotation.NonNull; @@ -147,4 +149,11 @@ public abstract class TetheringDependencies { * Get a reference to BluetoothAdapter to be used by tethering. */ public abstract BluetoothAdapter getBluetoothAdapter(); + + /** + * Get SystemProperties which indicate whether tethering is denied. + */ + public boolean isTetheringDenied() { + return TextUtils.equals(SystemProperties.get("ro.tether.denied"), "true"); + } } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index 2b2dfae5c930c..d37aad26d1fd4 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -16,6 +16,7 @@ package com.android.networkstack.tethering; +import static android.content.pm.PackageManager.GET_ACTIVITIES; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM; @@ -81,6 +82,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; @@ -204,6 +206,7 @@ public class TetheringTest { @Mock private EthernetManager mEm; @Mock private TetheringNotificationUpdater mNotificationUpdater; @Mock private BpfCoordinator mBpfCoordinator; + @Mock private PackageManager mPackageManager; private final MockIpServerDependencies mIpServerDependencies = spy(new MockIpServerDependencies()); @@ -263,6 +266,11 @@ public class TetheringTest { return super.getSystemService(name); } + @Override + public PackageManager getPackageManager() { + return mPackageManager; + } + @Override public String getSystemServiceName(Class serviceClass) { if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE; @@ -425,6 +433,11 @@ public class TetheringTest { public TetheringNotificationUpdater getNotificationUpdater(Context ctx, Looper looper) { return mNotificationUpdater; } + + @Override + public boolean isTetheringDenied() { + return false; + } } private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4, @@ -1951,6 +1964,23 @@ public class TetheringTest { assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_ETH_IFNAME)); } + @Test + public void testProvisioningNeededButUnavailable() throws Exception { + assertTrue(mTethering.isTetheringSupported()); + verify(mPackageManager, never()).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES); + + setupForRequiredProvisioning(); + assertTrue(mTethering.isTetheringSupported()); + verify(mPackageManager).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES); + reset(mPackageManager); + + doThrow(PackageManager.NameNotFoundException.class).when(mPackageManager).getPackageInfo( + PROVISIONING_APP_NAME[0], GET_ACTIVITIES); + setupForRequiredProvisioning(); + assertFalse(mTethering.isTetheringSupported()); + verify(mPackageManager).getPackageInfo(PROVISIONING_APP_NAME[0], GET_ACTIVITIES); + } + // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. }