Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29b7014d authored by markchien's avatar markchien
Browse files

Close tethering when UI entitlement fails

This is a alternative way for moving TetherService logic
into EntitlementManager[1]. Settings would close tethering
when silent entitlement check fail. To be consistent,
tethering should also be closed if UI entitlement check
fails.

[1] https://android-review.googlesource.com/c/platform/frameworks/base/+/928136

bug: 129330738
Test: -build, flash, boot,
      -atest EntitlementManagerTest
      -manual test with carrier SIM

Change-Id: Idfc35461b6359f3a624e78d49b6f73812db6d8ab
parent 3b519638
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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,
+26 −4
Original line number Diff line number Diff line
@@ -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);
        }
    }
+73 −45
Original line number Diff line number Diff line
@@ -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