diff options
3 files changed, 103 insertions, 49 deletions
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 0b1a98ee6c55..99868099ad4d 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -233,6 +233,10 @@ public class Tethering extends BaseNetworkObserver { // permission is changed according to entitlement check result. mEntitlementMgr = mDeps.getEntitlementManager(mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_PERMISSION_CHANGED, systemProperties); + mEntitlementMgr.setOnUiEntitlementFailedListener((int downstream) -> { + mLog.log("OBSERVED UiEnitlementFailed"); + stopTethering(downstream); + }); mCarrierConfigChange = new VersionedBroadcastListener( "CarrierConfigChangeListener", mContext, mHandler, filter, diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java index 5c4539741cae..764a6ebc2d98 100644 --- a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java +++ b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java @@ -109,6 +109,7 @@ public class EntitlementManager { private boolean mCellularUpstreamPermitted = true; private boolean mUsingCellularAsUpstream = false; private boolean mNeedReRunProvisioningUi = false; + private OnUiEntitlementFailedListener mListener; public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log, int permissionChangeMessageCode, MockableSystemProperties systemProperties) { @@ -129,6 +130,20 @@ public class EntitlementManager { null, mHandler); } + public void setOnUiEntitlementFailedListener(final OnUiEntitlementFailedListener listener) { + mListener = listener; + } + + /** Callback fired when UI entitlement failed. */ + public interface OnUiEntitlementFailedListener { + /** + * Ui entitlement check fails in |downstream|. + * + * @param downstream tethering type from ConnectivityManager.TETHERING_{@code *}. + */ + void onUiEntitlementFailed(int downstream); + } + /** * Pass a new TetheringConfiguration instance each time when * Tethering#updateConfiguration() is called. @@ -337,7 +352,9 @@ public class EntitlementManager { */ protected void runSilentTetherProvisioning(int type) { if (DBG) Log.d(TAG, "runSilentTetherProvisioning: " + type); - ResultReceiver receiver = buildProxyReceiver(type, null); + // For silent provisioning, settings would stop tethering when entitlement fail. + ResultReceiver receiver = buildProxyReceiver(type, + false/* notifyFail */, null); Intent intent = new Intent(); intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); @@ -358,7 +375,8 @@ public class EntitlementManager { */ @VisibleForTesting protected void runUiTetherProvisioning(int type) { - ResultReceiver receiver = buildProxyReceiver(type, null); + ResultReceiver receiver = buildProxyReceiver(type, + true/* notifyFail */, null); runUiTetherProvisioning(type, receiver); } @@ -555,12 +573,16 @@ public class EntitlementManager { } } - private ResultReceiver buildProxyReceiver(int type, final ResultReceiver receiver) { + private ResultReceiver buildProxyReceiver(int type, boolean notifyFail, + final ResultReceiver receiver) { ResultReceiver rr = new ResultReceiver(mHandler) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { int updatedCacheValue = updateEntitlementCacheValue(type, resultCode); addDownstreamMapping(type, updatedCacheValue); + if (updatedCacheValue == TETHER_ERROR_PROVISION_FAILED && notifyFail) { + mListener.onUiEntitlementFailed(type); + } if (receiver != null) receiver.send(updatedCacheValue, null); } }; @@ -627,7 +649,7 @@ public class EntitlementManager { if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) { receiver.send(cacheValue, null); } else { - ResultReceiver proxy = buildProxyReceiver(downstream, receiver); + ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver); runUiTetherProvisioning(downstream, proxy); } } diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java index 9eab4bec2973..d28ab708f051 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -31,6 +31,8 @@ import static org.junit.Assert.fail; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ContentResolver; @@ -80,6 +82,7 @@ public final class EntitlementManagerTest { @Mock private MockableSystemProperties mSystemProperties; @Mock private Resources mResources; @Mock private SharedLog mLog; + @Mock private EntitlementManager.OnUiEntitlementFailedListener mEntitlementFailedListener; // Like so many Android system APIs, these cannot be mocked because it is marked final. // We have to use the real versions. @@ -109,7 +112,6 @@ public final class EntitlementManagerTest { public class WrappedEntitlementManager extends EntitlementManager { public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN; - public boolean everRunUiEntitlement = false; public int uiProvisionCount = 0; public int silentProvisionCount = 0; @@ -118,20 +120,22 @@ public final class EntitlementManagerTest { super(ctx, target, log, what, systemProperties); } - @Override - protected void runUiTetherProvisioning(int type, ResultReceiver receiver) { - everRunUiEntitlement = true; - receiver.send(fakeEntitlementResult, null); + public void reset() { + fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN; + uiProvisionCount = 0; + silentProvisionCount = 0; } @Override - protected void runUiTetherProvisioning(int type) { + protected void runUiTetherProvisioning(int type, ResultReceiver receiver) { uiProvisionCount++; + receiver.send(fakeEntitlementResult, null); } @Override protected void runSilentTetherProvisioning(int type) { silentProvisionCount++; + addDownstreamMapping(type, fakeEntitlementResult); } } @@ -157,6 +161,7 @@ public final class EntitlementManagerTest { mSM = new TestStateMachine(); mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE, mSystemProperties); + mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener); mEnMgr.updateConfiguration( new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); } @@ -246,7 +251,6 @@ public final class EntitlementManagerTest { final CountDownLatch mCallbacklatch = new CountDownLatch(1); // 1. Entitlement check is not required. mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.everRunUiEntitlement = false; ResultReceiver receiver = new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { @@ -257,13 +261,13 @@ public final class EntitlementManagerTest { mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); - assertFalse(mEnMgr.everRunUiEntitlement); + assertEquals(0, mEnMgr.uiProvisionCount); + mEnMgr.reset(); setupForRequiredProvisioning(); mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); // 2. No cache value and don't need to run entitlement check. - mEnMgr.everRunUiEntitlement = false; receiver = new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { @@ -274,10 +278,10 @@ public final class EntitlementManagerTest { mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); - assertFalse(mEnMgr.everRunUiEntitlement); + assertEquals(0, mEnMgr.uiProvisionCount); + mEnMgr.reset(); // 3. No cache value and ui entitlement check is needed. mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; - mEnMgr.everRunUiEntitlement = false; receiver = new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { @@ -288,10 +292,10 @@ public final class EntitlementManagerTest { mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); - assertTrue(mEnMgr.everRunUiEntitlement); + assertEquals(1, mEnMgr.uiProvisionCount); + mEnMgr.reset(); // 4. Cache value is TETHER_ERROR_PROVISION_FAILED and don't need to run entitlement check. mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.everRunUiEntitlement = false; receiver = new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { @@ -302,10 +306,10 @@ public final class EntitlementManagerTest { mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); - assertFalse(mEnMgr.everRunUiEntitlement); + assertEquals(0, mEnMgr.uiProvisionCount); + mEnMgr.reset(); // 5. Cache value is TETHER_ERROR_PROVISION_FAILED and ui entitlement check is needed. mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.everRunUiEntitlement = false; receiver = new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { @@ -316,10 +320,10 @@ public final class EntitlementManagerTest { mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); - assertTrue(mEnMgr.everRunUiEntitlement); + assertEquals(1, mEnMgr.uiProvisionCount); + mEnMgr.reset(); // 6. Cache value is TETHER_ERROR_NO_ERROR. mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; - mEnMgr.everRunUiEntitlement = false; receiver = new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { @@ -330,9 +334,9 @@ public final class EntitlementManagerTest { mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); - assertFalse(mEnMgr.everRunUiEntitlement); + assertEquals(0, mEnMgr.uiProvisionCount); + mEnMgr.reset(); // 7. Test get value for other downstream type. - mEnMgr.everRunUiEntitlement = false; receiver = new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { @@ -343,7 +347,8 @@ public final class EntitlementManagerTest { mEnMgr.getLatestTetheringEntitlementResult(TETHERING_USB, receiver, false); mLooper.dispatchAll(); callbackTimeoutHelper(mCallbacklatch); - assertFalse(mEnMgr.everRunUiEntitlement); + assertEquals(0, mEnMgr.uiProvisionCount); + mEnMgr.reset(); } void callbackTimeoutHelper(final CountDownLatch latch) throws Exception { @@ -358,15 +363,15 @@ public final class EntitlementManagerTest { mEnMgr.notifyUpstream(true); mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); mLooper.dispatchAll(); - mEnMgr.addDownstreamMapping(TETHERING_WIFI, TETHER_ERROR_PROVISION_FAILED); assertFalse(mEnMgr.isCellularUpstreamPermitted()); mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); mLooper.dispatchAll(); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); mLooper.dispatchAll(); - mEnMgr.addDownstreamMapping(TETHERING_WIFI, TETHER_ERROR_NO_ERROR); assertTrue(mEnMgr.isCellularUpstreamPermitted()); } @@ -376,17 +381,17 @@ public final class EntitlementManagerTest { mEnMgr.notifyUpstream(true); mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); mLooper.dispatchAll(); - mEnMgr.addDownstreamMapping(TETHERING_WIFI, TETHER_ERROR_PROVISION_FAILED); assertFalse(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); mLooper.dispatchAll(); - mEnMgr.addDownstreamMapping(TETHERING_USB, TETHER_ERROR_PROVISION_FAILED); assertFalse(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true); mLooper.dispatchAll(); - mEnMgr.addDownstreamMapping(TETHERING_BLUETOOTH, TETHER_ERROR_PROVISION_FAILED); assertFalse(mEnMgr.isCellularUpstreamPermitted()); } @@ -396,14 +401,14 @@ public final class EntitlementManagerTest { mEnMgr.notifyUpstream(true); mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); mLooper.dispatchAll(); - mEnMgr.addDownstreamMapping(TETHERING_WIFI, TETHER_ERROR_NO_ERROR); assertTrue(mEnMgr.isCellularUpstreamPermitted()); mLooper.dispatchAll(); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); mLooper.dispatchAll(); - mEnMgr.addDownstreamMapping(TETHERING_USB, TETHER_ERROR_PROVISION_FAILED); assertTrue(mEnMgr.isCellularUpstreamPermitted()); mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI); mLooper.dispatchAll(); @@ -417,48 +422,71 @@ public final class EntitlementManagerTest { mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID)); // 1. start ui provisioning, upstream is mobile + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; mEnMgr.notifyUpstream(true); mLooper.dispatchAll(); mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true); mLooper.dispatchAll(); - assertTrue(mEnMgr.uiProvisionCount == 1); - assertTrue(mEnMgr.silentProvisionCount == 0); - mEnMgr.addDownstreamMapping(TETHERING_USB, TETHER_ERROR_PROVISION_FAILED); + assertEquals(1, mEnMgr.uiProvisionCount); + assertEquals(0, mEnMgr.silentProvisionCount); + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.reset(); // 2. start no-ui provisioning + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, false); mLooper.dispatchAll(); - assertTrue(mEnMgr.silentProvisionCount == 1); - assertTrue(mEnMgr.uiProvisionCount == 1); - mEnMgr.addDownstreamMapping(TETHERING_WIFI, TETHER_ERROR_PROVISION_FAILED); + assertEquals(0, mEnMgr.uiProvisionCount); + assertEquals(1, mEnMgr.silentProvisionCount); + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.reset(); // 3. tear down mobile, then start ui provisioning mEnMgr.notifyUpstream(false); mLooper.dispatchAll(); mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true); mLooper.dispatchAll(); - assertTrue(mEnMgr.uiProvisionCount == 1); - assertTrue(mEnMgr.silentProvisionCount == 1); + assertEquals(0, mEnMgr.uiProvisionCount); + assertEquals(0, mEnMgr.silentProvisionCount); + mEnMgr.reset(); // 4. switch upstream back to mobile + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; mEnMgr.notifyUpstream(true); mLooper.dispatchAll(); - assertTrue(mEnMgr.uiProvisionCount == 2); - assertTrue(mEnMgr.silentProvisionCount == 1); - mEnMgr.addDownstreamMapping(TETHERING_BLUETOOTH, TETHER_ERROR_PROVISION_FAILED); + assertEquals(1, mEnMgr.uiProvisionCount); + assertEquals(0, mEnMgr.silentProvisionCount); + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.reset(); // 5. tear down mobile, then switch SIM mEnMgr.notifyUpstream(false); mLooper.dispatchAll(); mEnMgr.reevaluateSimCardProvisioning(); - assertTrue(mEnMgr.uiProvisionCount == 2); - assertTrue(mEnMgr.silentProvisionCount == 1); + assertEquals(0, mEnMgr.uiProvisionCount); + assertEquals(0, mEnMgr.silentProvisionCount); + mEnMgr.reset(); // 6. switch upstream back to mobile again + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; mEnMgr.notifyUpstream(true); mLooper.dispatchAll(); - assertTrue(mEnMgr.uiProvisionCount == 2); - assertTrue(mEnMgr.silentProvisionCount == 4); - mEnMgr.addDownstreamMapping(TETHERING_USB, TETHER_ERROR_PROVISION_FAILED); - mEnMgr.addDownstreamMapping(TETHERING_WIFI, TETHER_ERROR_PROVISION_FAILED); - mEnMgr.addDownstreamMapping(TETHERING_BLUETOOTH, TETHER_ERROR_PROVISION_FAILED); + assertEquals(0, mEnMgr.uiProvisionCount); + assertEquals(3, mEnMgr.silentProvisionCount); + mEnMgr.reset(); } + @Test + public void testCallStopTetheringWhenUiProvisioningFail() { + setupForRequiredProvisioning(); + mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog, + INVALID_SUBSCRIPTION_ID)); + verify(mEntitlementFailedListener, times(0)).onUiEntitlementFailed(TETHERING_WIFI); + mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED; + mEnMgr.notifyUpstream(true); + mLooper.dispatchAll(); + mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true); + mLooper.dispatchAll(); + assertEquals(1, mEnMgr.uiProvisionCount); + verify(mEntitlementFailedListener, times(1)).onUiEntitlementFailed(TETHERING_WIFI); + } + + public class TestStateMachine extends StateMachine { public final ArrayList<Message> messages = new ArrayList<>(); private final State |