Loading packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +31 −3 Original line number Diff line number Diff line Loading @@ -80,6 +80,7 @@ public class EntitlementManager { // {@link TetheringManager.TETHERING_USB} // {@link TetheringManager.TETHERING_BLUETOOTH} private final BitSet mCurrentDownstreams; private final BitSet mExemptedDownstreams; private final Context mContext; private final SharedLog mLog; private final SparseIntArray mEntitlementCacheValue; Loading @@ -100,6 +101,7 @@ public class EntitlementManager { mContext = ctx; mLog = log.forSubComponent(TAG); mCurrentDownstreams = new BitSet(); mExemptedDownstreams = new BitSet(); mCurrentEntitlementResults = new SparseIntArray(); mEntitlementCacheValue = new SparseIntArray(); mPermissionChangeCallback = callback; Loading Loading @@ -150,13 +152,29 @@ public class EntitlementManager { private boolean isCellularUpstreamPermitted(final TetheringConfiguration config) { if (!isTetherProvisioningRequired(config)) return true; // If provisioning is required and EntitlementManager doesn't know any downstreams, // cellular upstream should not be allowed. if (mCurrentDownstreams.isEmpty()) return false; // If provisioning is required and EntitlementManager doesn't know any downstreams, cellular // upstream should not be enabled. Enable cellular upstream for exempted downstreams only // when there is no non-exempted downstream. if (mCurrentDownstreams.isEmpty()) return !mExemptedDownstreams.isEmpty(); return mCurrentEntitlementResults.indexOfValue(TETHER_ERROR_NO_ERROR) > -1; } /** * Set exempted downstream type. If there is only exempted downstream type active, * corresponding entitlement check will not be run and cellular upstream will be permitted * by default. If a privileged app enables tethering without a provisioning check, and then * another app enables tethering of the same type but does not disable the provisioning check, * then the downstream immediately loses exempt status and a provisioning check is run. * If any non-exempted downstream type is active, the cellular upstream will be gated by the * result of entitlement check from non-exempted downstreams. If entitlement check is still * in progress on non-exempt downstreams, ceullar upstream would default be disabled. When any * non-exempted downstream gets positive entitlement result, ceullar upstream will be enabled. */ public void setExemptedDownstreamType(final int type) { mExemptedDownstreams.set(type, true); } /** * This is called when tethering starts. * Launch provisioning app if upstream is cellular. Loading @@ -170,6 +188,8 @@ public class EntitlementManager { mCurrentDownstreams.set(downstreamType, true); mExemptedDownstreams.set(downstreamType, false); final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); if (!isTetherProvisioningRequired(config)) return; Loading Loading @@ -200,6 +220,7 @@ public class EntitlementManager { // "tethering supported" may change without without tethering being notified properly. // Remove the mapping all the time no matter provisioning is required or not. removeDownstreamMapping(downstreamType); mExemptedDownstreams.set(downstreamType, false); } /** Loading Loading @@ -505,6 +526,13 @@ public class EntitlementManager { if (!mWaiting.block(DUMP_TIMEOUT)) { pw.println("... dump timed out after " + DUMP_TIMEOUT + "ms"); } pw.print("Exempted: ["); for (int type = mExemptedDownstreams.nextSetBit(0); type >= 0; type = mExemptedDownstreams.nextSetBit(type + 1)) { pw.print(typeString(type)); pw.print(", "); } pw.println("]"); } private static String typeString(int type) { Loading packages/Tethering/src/com/android/networkstack/tethering/Tethering.java +6 −2 Original line number Diff line number Diff line Loading @@ -516,8 +516,12 @@ public class Tethering { } mActiveTetheringRequests.put(request.tetheringType, request); if (request.exemptFromEntitlementCheck) { mEntitlementMgr.setExemptedDownstreamType(request.tetheringType); } else { mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType, request.showProvisioningUi); } enableTetheringInternal(request.tetheringType, true /* enabled */, listener); }); } Loading packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java +49 −44 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.networkstack.tethering; import static android.Manifest.permission.ACCESS_NETWORK_STATE; import static android.Manifest.permission.TETHER_PRIVILEGED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION; import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION; Loading Loading @@ -151,7 +153,12 @@ public class TetheringService extends Service { @Override public void startTethering(TetheringRequestParcel request, String callerPkg, String callingAttributionTag, IIntResultListener listener) { if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, request.exemptFromEntitlementCheck /* onlyAllowPrivileged */, listener)) { return; } mTethering.startTethering(request, listener); } Loading Loading @@ -179,7 +186,7 @@ public class TetheringService extends Service { public void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg) { try { if (!mService.hasTetherAccessPermission()) { if (!hasTetherAccessPermission()) { callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); return; } Loading @@ -191,7 +198,7 @@ public class TetheringService extends Service { public void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg) { try { if (!mService.hasTetherAccessPermission()) { if (!hasTetherAccessPermission()) { callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); return; } Loading Loading @@ -226,10 +233,18 @@ public class TetheringService extends Service { mTethering.dump(fd, writer, args); } private boolean checkAndNotifyCommonError(String callerPkg, String callingAttributionTag, IIntResultListener listener) { private boolean checkAndNotifyCommonError(final String callerPkg, final String callingAttributionTag, final IIntResultListener listener) { return checkAndNotifyCommonError(callerPkg, callingAttributionTag, false /* onlyAllowPrivileged */, listener); } private boolean checkAndNotifyCommonError(final String callerPkg, final String callingAttributionTag, final boolean onlyAllowPrivileged, final IIntResultListener listener) { try { if (!mService.hasTetherChangePermission(callerPkg, callingAttributionTag)) { if (!hasTetherChangePermission(callerPkg, callingAttributionTag, onlyAllowPrivileged)) { listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); return true; } Loading @@ -244,9 +259,10 @@ public class TetheringService extends Service { return false; } private boolean checkAndNotifyCommonError(String callerPkg, String callingAttributionTag, ResultReceiver receiver) { if (!mService.hasTetherChangePermission(callerPkg, callingAttributionTag)) { private boolean checkAndNotifyCommonError(final String callerPkg, final String callingAttributionTag, final ResultReceiver receiver) { if (!hasTetherChangePermission(callerPkg, callingAttributionTag, false /* onlyAllowPrivileged */)) { receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null); return true; } Loading @@ -258,6 +274,30 @@ public class TetheringService extends Service { return false; } private boolean hasTetherPrivilegedPermission() { return mService.checkCallingOrSelfPermission(TETHER_PRIVILEGED) == PERMISSION_GRANTED; } private boolean hasTetherChangePermission(final String callerPkg, final String callingAttributionTag, final boolean onlyAllowPrivileged) { if (hasTetherPrivilegedPermission()) return true; if (onlyAllowPrivileged || mTethering.isTetherProvisioningRequired()) return false; int uid = Binder.getCallingUid(); // If callerPkg's uid is not same as Binder.getCallingUid(), // checkAndNoteWriteSettingsOperation will return false and the operation will be // denied. return TetheringService.checkAndNoteWriteSettingsOperation(mService, uid, callerPkg, callingAttributionTag, false /* throwException */); } private boolean hasTetherAccessPermission() { if (hasTetherPrivilegedPermission()) return true; return mService.checkCallingOrSelfPermission( ACCESS_NETWORK_STATE) == PERMISSION_GRANTED; } } // if ro.tether.denied = true we default to no tethering Loading @@ -274,26 +314,6 @@ public class TetheringService extends Service { return tetherEnabledInSettings && mTethering.hasTetherableConfiguration(); } private boolean hasTetherChangePermission(String callerPkg, String callingAttributionTag) { if (checkCallingOrSelfPermission( android.Manifest.permission.TETHER_PRIVILEGED) == PERMISSION_GRANTED) { return true; } if (mTethering.isTetherProvisioningRequired()) return false; int uid = Binder.getCallingUid(); // If callerPkg's uid is not same as Binder.getCallingUid(), // checkAndNoteWriteSettingsOperation will return false and the operation will be denied. if (checkAndNoteWriteSettingsOperation(mContext, uid, callerPkg, callingAttributionTag, false /* throwException */)) { return true; } return false; } /** * Check if the package is a allowed to write settings. This also accounts that such an access * happened. Loading @@ -308,21 +328,6 @@ public class TetheringService extends Service { throwException); } private boolean hasTetherAccessPermission() { if (checkCallingOrSelfPermission( android.Manifest.permission.TETHER_PRIVILEGED) == PERMISSION_GRANTED) { return true; } if (checkCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE) == PERMISSION_GRANTED) { return true; } return false; } /** * An injection method for testing. */ Loading packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +29 −0 Original line number Diff line number Diff line Loading @@ -543,4 +543,33 @@ public final class EntitlementManagerTest { assertEquals(1, mEnMgr.uiProvisionCount); verify(mEntitlementFailedListener, times(1)).onUiEntitlementFailed(TETHERING_WIFI); } @Test public void testsetExemptedDownstreamType() throws Exception { setupForRequiredProvisioning(); // Cellular upstream is not permitted when no entitlement result. assertFalse(mEnMgr.isCellularUpstreamPermitted()); // If there is exempted downstream and no other non-exempted downstreams, cellular is // permitted. mEnMgr.setExemptedDownstreamType(TETHERING_WIFI); assertTrue(mEnMgr.isCellularUpstreamPermitted()); // If second downstream run entitlement check fail, cellular upstream is not permitted. mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.notifyUpstream(true); mLooper.dispatchAll(); mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); mLooper.dispatchAll(); assertFalse(mEnMgr.isCellularUpstreamPermitted()); // When second downstream is down, exempted downstream can use cellular upstream. assertEquals(1, mEnMgr.uiProvisionCount); verify(mEntitlementFailedListener).onUiEntitlementFailed(TETHERING_USB); mEnMgr.stopProvisioningIfNeeded(TETHERING_USB); assertTrue(mEnMgr.isCellularUpstreamPermitted()); mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); assertFalse(mEnMgr.isCellularUpstreamPermitted()); } } packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +73 −7 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; Loading Loading @@ -172,6 +173,8 @@ public class TetheringTest { private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0"; private static final String TEST_NCM_IFNAME = "test_ncm0"; private static final String TETHERING_NAME = "Tethering"; private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; private static final int DHCPSERVER_START_TIMEOUT_MS = 1000; Loading Loading @@ -539,16 +542,16 @@ public class TetheringTest { } private TetheringRequestParcel createTetheringRequestParcel(final int type) { return createTetheringRequestParcel(type, null, null); return createTetheringRequestParcel(type, null, null, false); } private TetheringRequestParcel createTetheringRequestParcel(final int type, final LinkAddress serverAddr, final LinkAddress clientAddr) { final LinkAddress serverAddr, final LinkAddress clientAddr, final boolean exempt) { final TetheringRequestParcel request = new TetheringRequestParcel(); request.tetheringType = type; request.localIPv4Address = serverAddr; request.staticClientAddress = clientAddr; request.exemptFromEntitlementCheck = false; request.exemptFromEntitlementCheck = exempt; request.showProvisioningUi = false; return request; Loading Loading @@ -1659,7 +1662,7 @@ public class TetheringTest { // Enable USB tethering and check that Tethering starts USB. mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, null, null), firstResult); null, null, false), firstResult); mLooper.dispatchAll(); firstResult.assertHasResult(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); Loading @@ -1667,7 +1670,7 @@ public class TetheringTest { // Enable USB tethering again with the same request and expect no change to USB. mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, null, null), secondResult); null, null, false), secondResult); mLooper.dispatchAll(); secondResult.assertHasResult(); verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE); Loading @@ -1676,7 +1679,7 @@ public class TetheringTest { // Enable USB tethering with a different request and expect that USB is stopped and // started. mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, serverLinkAddr, clientLinkAddr), thirdResult); serverLinkAddr, clientLinkAddr, false), thirdResult); mLooper.dispatchAll(); thirdResult.assertHasResult(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE); Loading @@ -1700,7 +1703,7 @@ public class TetheringTest { final ArgumentCaptor<DhcpServingParamsParcel> dhcpParamsCaptor = ArgumentCaptor.forClass(DhcpServingParamsParcel.class); mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, serverLinkAddr, clientLinkAddr), null); serverLinkAddr, clientLinkAddr, false), null); mLooper.dispatchAll(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); Loading Loading @@ -1762,6 +1765,69 @@ public class TetheringTest { mLooper.stopAutoDispatch(); } @Test public void testExemptFromEntitlementCheck() throws Exception { setupForRequiredProvisioning(); final TetheringRequestParcel wifiNotExemptRequest = createTetheringRequestParcel(TETHERING_WIFI, null, null, false); mTethering.startTethering(wifiNotExemptRequest, null); mLooper.dispatchAll(); verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false); verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI); assertFalse(mEntitleMgr.isCellularUpstreamPermitted()); mTethering.stopTethering(TETHERING_WIFI); mLooper.dispatchAll(); verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); reset(mEntitleMgr); setupForRequiredProvisioning(); final TetheringRequestParcel wifiExemptRequest = createTetheringRequestParcel(TETHERING_WIFI, null, null, true); mTethering.startTethering(wifiExemptRequest, null); mLooper.dispatchAll(); verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false); verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI); assertTrue(mEntitleMgr.isCellularUpstreamPermitted()); mTethering.stopTethering(TETHERING_WIFI); mLooper.dispatchAll(); verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); reset(mEntitleMgr); // If one app enables tethering without provisioning check first, then another app enables // tethering of the same type but does not disable the provisioning check. setupForRequiredProvisioning(); mTethering.startTethering(wifiExemptRequest, null); mLooper.dispatchAll(); verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false); verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI); assertTrue(mEntitleMgr.isCellularUpstreamPermitted()); reset(mEntitleMgr); setupForRequiredProvisioning(); mTethering.startTethering(wifiNotExemptRequest, null); mLooper.dispatchAll(); verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false); verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI); assertFalse(mEntitleMgr.isCellularUpstreamPermitted()); mTethering.stopTethering(TETHERING_WIFI); mLooper.dispatchAll(); verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); reset(mEntitleMgr); } private void setupForRequiredProvisioning() { // Produce some acceptable looking provision app setting if requested. when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) .thenReturn(PROVISIONING_APP_NAME); when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) .thenReturn(PROVISIONING_NO_UI_APP_NAME); // Act like the CarrierConfigManager is present and ready unless told otherwise. when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) .thenReturn(mCarrierConfigManager); when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig); mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true); mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true); sendConfigurationChanged(); } // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. } Loading
packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +31 −3 Original line number Diff line number Diff line Loading @@ -80,6 +80,7 @@ public class EntitlementManager { // {@link TetheringManager.TETHERING_USB} // {@link TetheringManager.TETHERING_BLUETOOTH} private final BitSet mCurrentDownstreams; private final BitSet mExemptedDownstreams; private final Context mContext; private final SharedLog mLog; private final SparseIntArray mEntitlementCacheValue; Loading @@ -100,6 +101,7 @@ public class EntitlementManager { mContext = ctx; mLog = log.forSubComponent(TAG); mCurrentDownstreams = new BitSet(); mExemptedDownstreams = new BitSet(); mCurrentEntitlementResults = new SparseIntArray(); mEntitlementCacheValue = new SparseIntArray(); mPermissionChangeCallback = callback; Loading Loading @@ -150,13 +152,29 @@ public class EntitlementManager { private boolean isCellularUpstreamPermitted(final TetheringConfiguration config) { if (!isTetherProvisioningRequired(config)) return true; // If provisioning is required and EntitlementManager doesn't know any downstreams, // cellular upstream should not be allowed. if (mCurrentDownstreams.isEmpty()) return false; // If provisioning is required and EntitlementManager doesn't know any downstreams, cellular // upstream should not be enabled. Enable cellular upstream for exempted downstreams only // when there is no non-exempted downstream. if (mCurrentDownstreams.isEmpty()) return !mExemptedDownstreams.isEmpty(); return mCurrentEntitlementResults.indexOfValue(TETHER_ERROR_NO_ERROR) > -1; } /** * Set exempted downstream type. If there is only exempted downstream type active, * corresponding entitlement check will not be run and cellular upstream will be permitted * by default. If a privileged app enables tethering without a provisioning check, and then * another app enables tethering of the same type but does not disable the provisioning check, * then the downstream immediately loses exempt status and a provisioning check is run. * If any non-exempted downstream type is active, the cellular upstream will be gated by the * result of entitlement check from non-exempted downstreams. If entitlement check is still * in progress on non-exempt downstreams, ceullar upstream would default be disabled. When any * non-exempted downstream gets positive entitlement result, ceullar upstream will be enabled. */ public void setExemptedDownstreamType(final int type) { mExemptedDownstreams.set(type, true); } /** * This is called when tethering starts. * Launch provisioning app if upstream is cellular. Loading @@ -170,6 +188,8 @@ public class EntitlementManager { mCurrentDownstreams.set(downstreamType, true); mExemptedDownstreams.set(downstreamType, false); final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); if (!isTetherProvisioningRequired(config)) return; Loading Loading @@ -200,6 +220,7 @@ public class EntitlementManager { // "tethering supported" may change without without tethering being notified properly. // Remove the mapping all the time no matter provisioning is required or not. removeDownstreamMapping(downstreamType); mExemptedDownstreams.set(downstreamType, false); } /** Loading Loading @@ -505,6 +526,13 @@ public class EntitlementManager { if (!mWaiting.block(DUMP_TIMEOUT)) { pw.println("... dump timed out after " + DUMP_TIMEOUT + "ms"); } pw.print("Exempted: ["); for (int type = mExemptedDownstreams.nextSetBit(0); type >= 0; type = mExemptedDownstreams.nextSetBit(type + 1)) { pw.print(typeString(type)); pw.print(", "); } pw.println("]"); } private static String typeString(int type) { Loading
packages/Tethering/src/com/android/networkstack/tethering/Tethering.java +6 −2 Original line number Diff line number Diff line Loading @@ -516,8 +516,12 @@ public class Tethering { } mActiveTetheringRequests.put(request.tetheringType, request); if (request.exemptFromEntitlementCheck) { mEntitlementMgr.setExemptedDownstreamType(request.tetheringType); } else { mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType, request.showProvisioningUi); } enableTetheringInternal(request.tetheringType, true /* enabled */, listener); }); } Loading
packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java +49 −44 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.networkstack.tethering; import static android.Manifest.permission.ACCESS_NETWORK_STATE; import static android.Manifest.permission.TETHER_PRIVILEGED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION; import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION; Loading Loading @@ -151,7 +153,12 @@ public class TetheringService extends Service { @Override public void startTethering(TetheringRequestParcel request, String callerPkg, String callingAttributionTag, IIntResultListener listener) { if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, request.exemptFromEntitlementCheck /* onlyAllowPrivileged */, listener)) { return; } mTethering.startTethering(request, listener); } Loading Loading @@ -179,7 +186,7 @@ public class TetheringService extends Service { public void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg) { try { if (!mService.hasTetherAccessPermission()) { if (!hasTetherAccessPermission()) { callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); return; } Loading @@ -191,7 +198,7 @@ public class TetheringService extends Service { public void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg) { try { if (!mService.hasTetherAccessPermission()) { if (!hasTetherAccessPermission()) { callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION); return; } Loading Loading @@ -226,10 +233,18 @@ public class TetheringService extends Service { mTethering.dump(fd, writer, args); } private boolean checkAndNotifyCommonError(String callerPkg, String callingAttributionTag, IIntResultListener listener) { private boolean checkAndNotifyCommonError(final String callerPkg, final String callingAttributionTag, final IIntResultListener listener) { return checkAndNotifyCommonError(callerPkg, callingAttributionTag, false /* onlyAllowPrivileged */, listener); } private boolean checkAndNotifyCommonError(final String callerPkg, final String callingAttributionTag, final boolean onlyAllowPrivileged, final IIntResultListener listener) { try { if (!mService.hasTetherChangePermission(callerPkg, callingAttributionTag)) { if (!hasTetherChangePermission(callerPkg, callingAttributionTag, onlyAllowPrivileged)) { listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); return true; } Loading @@ -244,9 +259,10 @@ public class TetheringService extends Service { return false; } private boolean checkAndNotifyCommonError(String callerPkg, String callingAttributionTag, ResultReceiver receiver) { if (!mService.hasTetherChangePermission(callerPkg, callingAttributionTag)) { private boolean checkAndNotifyCommonError(final String callerPkg, final String callingAttributionTag, final ResultReceiver receiver) { if (!hasTetherChangePermission(callerPkg, callingAttributionTag, false /* onlyAllowPrivileged */)) { receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null); return true; } Loading @@ -258,6 +274,30 @@ public class TetheringService extends Service { return false; } private boolean hasTetherPrivilegedPermission() { return mService.checkCallingOrSelfPermission(TETHER_PRIVILEGED) == PERMISSION_GRANTED; } private boolean hasTetherChangePermission(final String callerPkg, final String callingAttributionTag, final boolean onlyAllowPrivileged) { if (hasTetherPrivilegedPermission()) return true; if (onlyAllowPrivileged || mTethering.isTetherProvisioningRequired()) return false; int uid = Binder.getCallingUid(); // If callerPkg's uid is not same as Binder.getCallingUid(), // checkAndNoteWriteSettingsOperation will return false and the operation will be // denied. return TetheringService.checkAndNoteWriteSettingsOperation(mService, uid, callerPkg, callingAttributionTag, false /* throwException */); } private boolean hasTetherAccessPermission() { if (hasTetherPrivilegedPermission()) return true; return mService.checkCallingOrSelfPermission( ACCESS_NETWORK_STATE) == PERMISSION_GRANTED; } } // if ro.tether.denied = true we default to no tethering Loading @@ -274,26 +314,6 @@ public class TetheringService extends Service { return tetherEnabledInSettings && mTethering.hasTetherableConfiguration(); } private boolean hasTetherChangePermission(String callerPkg, String callingAttributionTag) { if (checkCallingOrSelfPermission( android.Manifest.permission.TETHER_PRIVILEGED) == PERMISSION_GRANTED) { return true; } if (mTethering.isTetherProvisioningRequired()) return false; int uid = Binder.getCallingUid(); // If callerPkg's uid is not same as Binder.getCallingUid(), // checkAndNoteWriteSettingsOperation will return false and the operation will be denied. if (checkAndNoteWriteSettingsOperation(mContext, uid, callerPkg, callingAttributionTag, false /* throwException */)) { return true; } return false; } /** * Check if the package is a allowed to write settings. This also accounts that such an access * happened. Loading @@ -308,21 +328,6 @@ public class TetheringService extends Service { throwException); } private boolean hasTetherAccessPermission() { if (checkCallingOrSelfPermission( android.Manifest.permission.TETHER_PRIVILEGED) == PERMISSION_GRANTED) { return true; } if (checkCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE) == PERMISSION_GRANTED) { return true; } return false; } /** * An injection method for testing. */ Loading
packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +29 −0 Original line number Diff line number Diff line Loading @@ -543,4 +543,33 @@ public final class EntitlementManagerTest { assertEquals(1, mEnMgr.uiProvisionCount); verify(mEntitlementFailedListener, times(1)).onUiEntitlementFailed(TETHERING_WIFI); } @Test public void testsetExemptedDownstreamType() throws Exception { setupForRequiredProvisioning(); // Cellular upstream is not permitted when no entitlement result. assertFalse(mEnMgr.isCellularUpstreamPermitted()); // If there is exempted downstream and no other non-exempted downstreams, cellular is // permitted. mEnMgr.setExemptedDownstreamType(TETHERING_WIFI); assertTrue(mEnMgr.isCellularUpstreamPermitted()); // If second downstream run entitlement check fail, cellular upstream is not permitted. mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED; mEnMgr.notifyUpstream(true); mLooper.dispatchAll(); mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); mLooper.dispatchAll(); assertFalse(mEnMgr.isCellularUpstreamPermitted()); // When second downstream is down, exempted downstream can use cellular upstream. assertEquals(1, mEnMgr.uiProvisionCount); verify(mEntitlementFailedListener).onUiEntitlementFailed(TETHERING_USB); mEnMgr.stopProvisioningIfNeeded(TETHERING_USB); assertTrue(mEnMgr.isCellularUpstreamPermitted()); mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); assertFalse(mEnMgr.isCellularUpstreamPermitted()); } }
packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +73 −7 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; Loading Loading @@ -172,6 +173,8 @@ public class TetheringTest { private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0"; private static final String TEST_NCM_IFNAME = "test_ncm0"; private static final String TETHERING_NAME = "Tethering"; private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; private static final int DHCPSERVER_START_TIMEOUT_MS = 1000; Loading Loading @@ -539,16 +542,16 @@ public class TetheringTest { } private TetheringRequestParcel createTetheringRequestParcel(final int type) { return createTetheringRequestParcel(type, null, null); return createTetheringRequestParcel(type, null, null, false); } private TetheringRequestParcel createTetheringRequestParcel(final int type, final LinkAddress serverAddr, final LinkAddress clientAddr) { final LinkAddress serverAddr, final LinkAddress clientAddr, final boolean exempt) { final TetheringRequestParcel request = new TetheringRequestParcel(); request.tetheringType = type; request.localIPv4Address = serverAddr; request.staticClientAddress = clientAddr; request.exemptFromEntitlementCheck = false; request.exemptFromEntitlementCheck = exempt; request.showProvisioningUi = false; return request; Loading Loading @@ -1659,7 +1662,7 @@ public class TetheringTest { // Enable USB tethering and check that Tethering starts USB. mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, null, null), firstResult); null, null, false), firstResult); mLooper.dispatchAll(); firstResult.assertHasResult(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); Loading @@ -1667,7 +1670,7 @@ public class TetheringTest { // Enable USB tethering again with the same request and expect no change to USB. mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, null, null), secondResult); null, null, false), secondResult); mLooper.dispatchAll(); secondResult.assertHasResult(); verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE); Loading @@ -1676,7 +1679,7 @@ public class TetheringTest { // Enable USB tethering with a different request and expect that USB is stopped and // started. mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, serverLinkAddr, clientLinkAddr), thirdResult); serverLinkAddr, clientLinkAddr, false), thirdResult); mLooper.dispatchAll(); thirdResult.assertHasResult(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE); Loading @@ -1700,7 +1703,7 @@ public class TetheringTest { final ArgumentCaptor<DhcpServingParamsParcel> dhcpParamsCaptor = ArgumentCaptor.forClass(DhcpServingParamsParcel.class); mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, serverLinkAddr, clientLinkAddr), null); serverLinkAddr, clientLinkAddr, false), null); mLooper.dispatchAll(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); Loading Loading @@ -1762,6 +1765,69 @@ public class TetheringTest { mLooper.stopAutoDispatch(); } @Test public void testExemptFromEntitlementCheck() throws Exception { setupForRequiredProvisioning(); final TetheringRequestParcel wifiNotExemptRequest = createTetheringRequestParcel(TETHERING_WIFI, null, null, false); mTethering.startTethering(wifiNotExemptRequest, null); mLooper.dispatchAll(); verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false); verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI); assertFalse(mEntitleMgr.isCellularUpstreamPermitted()); mTethering.stopTethering(TETHERING_WIFI); mLooper.dispatchAll(); verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); reset(mEntitleMgr); setupForRequiredProvisioning(); final TetheringRequestParcel wifiExemptRequest = createTetheringRequestParcel(TETHERING_WIFI, null, null, true); mTethering.startTethering(wifiExemptRequest, null); mLooper.dispatchAll(); verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false); verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI); assertTrue(mEntitleMgr.isCellularUpstreamPermitted()); mTethering.stopTethering(TETHERING_WIFI); mLooper.dispatchAll(); verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); reset(mEntitleMgr); // If one app enables tethering without provisioning check first, then another app enables // tethering of the same type but does not disable the provisioning check. setupForRequiredProvisioning(); mTethering.startTethering(wifiExemptRequest, null); mLooper.dispatchAll(); verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false); verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI); assertTrue(mEntitleMgr.isCellularUpstreamPermitted()); reset(mEntitleMgr); setupForRequiredProvisioning(); mTethering.startTethering(wifiNotExemptRequest, null); mLooper.dispatchAll(); verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false); verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI); assertFalse(mEntitleMgr.isCellularUpstreamPermitted()); mTethering.stopTethering(TETHERING_WIFI); mLooper.dispatchAll(); verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI); reset(mEntitleMgr); } private void setupForRequiredProvisioning() { // Produce some acceptable looking provision app setting if requested. when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) .thenReturn(PROVISIONING_APP_NAME); when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) .thenReturn(PROVISIONING_NO_UI_APP_NAME); // Act like the CarrierConfigManager is present and ready unless told otherwise. when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) .thenReturn(mCarrierConfigManager); when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig); mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true); mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true); sendConfigurationChanged(); } // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. }