[Tether04] Migrate EntitlementManager into module

Bug: 136040414
Test: -build, flash, boot
      -atest TetheringTests
      -atest FrameworksNetTests

Change-Id: Ifdfc6cd95377351c37946a146b60896f07ece59d
Merged-In: Ifdfc6cd95377351c37946a146b60896f07ece59d
diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp
index da62107..5564bd6 100644
--- a/Tethering/tests/unit/Android.bp
+++ b/Tethering/tests/unit/Android.bp
@@ -17,7 +17,10 @@
 android_test {
     name: "TetheringTests",
     certificate: "platform",
-    srcs: ["src/**/*.java"],
+    srcs: [
+        ":servicescore-tethering-src",
+        "src/**/*.java",
+    ],
     test_suites: ["device-tests"],
     static_libs: [
         "androidx.test.rules",
@@ -42,6 +45,7 @@
 filegroup {
     name: "tethering-tests-src",
     srcs: [
+        "src/com/android/server/connectivity/tethering/EntitlementManagerTest.java",
         "src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java",
         "src/android/net/dhcp/DhcpServingParamsParcelExtTest.java",
         "src/android/net/ip/IpServerTest.java",
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
new file mode 100644
index 0000000..5217e26
--- /dev/null
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
+import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+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;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.util.SharedLog;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.ResultReceiver;
+import android.os.test.TestLooper;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.test.mock.MockContentResolver;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.MockableSystemProperties;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class EntitlementManagerTest {
+
+    private static final int EVENT_EM_UPDATE = 1;
+    private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+    private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
+
+    @Mock private CarrierConfigManager mCarrierConfigManager;
+    @Mock private Context mContext;
+    @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.
+    private final PersistableBundle mCarrierConfig = new PersistableBundle();
+    private final TestLooper mLooper = new TestLooper();
+    private Context mMockContext;
+    private MockContentResolver mContentResolver;
+
+    private TestStateMachine mSM;
+    private WrappedEntitlementManager mEnMgr;
+    private TetheringConfiguration mConfig;
+
+    private class MockContext extends BroadcastInterceptingContext {
+        MockContext(Context base) {
+            super(base);
+        }
+
+        @Override
+        public Resources getResources() {
+            return mResources;
+        }
+
+        @Override
+        public ContentResolver getContentResolver() {
+            return mContentResolver;
+        }
+    }
+
+    public class WrappedEntitlementManager extends EntitlementManager {
+        public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN;
+        public int uiProvisionCount = 0;
+        public int silentProvisionCount = 0;
+
+        public WrappedEntitlementManager(Context ctx, StateMachine target,
+                SharedLog log, int what, MockableSystemProperties systemProperties) {
+            super(ctx, target, log, what, systemProperties);
+        }
+
+        public void reset() {
+            fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN;
+            uiProvisionCount = 0;
+            silentProvisionCount = 0;
+        }
+
+        @Override
+        protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) {
+            uiProvisionCount++;
+            receiver.send(fakeEntitlementResult, null);
+        }
+
+        @Override
+        protected void runSilentTetherProvisioning(int type, int subId) {
+            silentProvisionCount++;
+            addDownstreamMapping(type, fakeEntitlementResult);
+        }
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mResources.getStringArray(R.array.config_tether_dhcp_range))
+            .thenReturn(new String[0]);
+        when(mResources.getStringArray(R.array.config_tether_usb_regexs))
+            .thenReturn(new String[0]);
+        when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
+            .thenReturn(new String[0]);
+        when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
+            .thenReturn(new String[0]);
+        when(mResources.getIntArray(R.array.config_tether_upstream_types))
+            .thenReturn(new int[0]);
+        when(mLog.forSubComponent(anyString())).thenReturn(mLog);
+
+        mContentResolver = new MockContentResolver();
+        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        mMockContext = new MockContext(mContext);
+        mSM = new TestStateMachine();
+        mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE,
+                mSystemProperties);
+        mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener);
+        mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        mEnMgr.setTetheringConfigurationFetcher(() -> {
+            return mConfig;
+        });
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mSM != null) {
+            mSM.quit();
+            mSM = null;
+        }
+    }
+
+    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);
+       // Don't disable tethering provisioning unless requested.
+        when(mSystemProperties.getBoolean(eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY),
+                anyBoolean())).thenReturn(false);
+        // 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);
+        mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+    }
+
+    @Test
+    public void canRequireProvisioning() {
+        setupForRequiredProvisioning();
+        assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
+    }
+
+    @Test
+    public void toleratesCarrierConfigManagerMissing() {
+        setupForRequiredProvisioning();
+        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+            .thenReturn(null);
+        mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
+        // Therefore provisioning still be required.
+        assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
+    }
+
+    @Test
+    public void toleratesCarrierConfigMissing() {
+        setupForRequiredProvisioning();
+        when(mCarrierConfigManager.getConfig()).thenReturn(null);
+        mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        // We still have a provisioning app configured, so still require provisioning.
+        assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
+    }
+
+    @Test
+    public void toleratesCarrierConfigNotLoaded() {
+        setupForRequiredProvisioning();
+        mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
+        // We still have a provisioning app configured, so still require provisioning.
+        assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
+    }
+
+    @Test
+    public void provisioningNotRequiredWhenAppNotFound() {
+        setupForRequiredProvisioning();
+        when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+            .thenReturn(null);
+        mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig));
+        when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+            .thenReturn(new String[] {"malformedApp"});
+        mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig));
+    }
+
+    @Test
+    public void testGetLastEntitlementCacheValue() throws Exception {
+        final CountDownLatch mCallbacklatch = new CountDownLatch(1);
+        // 1. Entitlement check is not required.
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        ResultReceiver receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
+        mLooper.dispatchAll();
+        callbackTimeoutHelper(mCallbacklatch);
+        assertEquals(0, mEnMgr.uiProvisionCount);
+        mEnMgr.reset();
+
+        setupForRequiredProvisioning();
+        // 2. No cache value and don't need to run entitlement check.
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
+        mLooper.dispatchAll();
+        callbackTimeoutHelper(mCallbacklatch);
+        assertEquals(0, mEnMgr.uiProvisionCount);
+        mEnMgr.reset();
+        // 3. No cache value and ui entitlement check is needed.
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
+        mLooper.dispatchAll();
+        callbackTimeoutHelper(mCallbacklatch);
+        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;
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
+        mLooper.dispatchAll();
+        callbackTimeoutHelper(mCallbacklatch);
+        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;
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
+        mLooper.dispatchAll();
+        callbackTimeoutHelper(mCallbacklatch);
+        assertEquals(1, mEnMgr.uiProvisionCount);
+        mEnMgr.reset();
+        // 6. Cache value is TETHER_ERROR_NO_ERROR.
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
+        mLooper.dispatchAll();
+        callbackTimeoutHelper(mCallbacklatch);
+        assertEquals(0, mEnMgr.uiProvisionCount);
+        mEnMgr.reset();
+        // 7. Test get value for other downstream type.
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.getLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
+        mLooper.dispatchAll();
+        callbackTimeoutHelper(mCallbacklatch);
+        assertEquals(0, mEnMgr.uiProvisionCount);
+        mEnMgr.reset();
+    }
+
+    void callbackTimeoutHelper(final CountDownLatch latch) throws Exception {
+        if (!latch.await(1, TimeUnit.SECONDS)) {
+            fail("Timout, fail to receive callback");
+        }
+    }
+
+    @Test
+    public void verifyPermissionResult() {
+        setupForRequiredProvisioning();
+        mEnMgr.notifyUpstream(true);
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
+        mLooper.dispatchAll();
+        assertFalse(mEnMgr.isCellularUpstreamPermitted());
+        mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
+        mLooper.dispatchAll();
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
+        mLooper.dispatchAll();
+        assertTrue(mEnMgr.isCellularUpstreamPermitted());
+    }
+
+    @Test
+    public void verifyPermissionIfAllNotApproved() {
+        setupForRequiredProvisioning();
+        mEnMgr.notifyUpstream(true);
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
+        mLooper.dispatchAll();
+        assertFalse(mEnMgr.isCellularUpstreamPermitted());
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
+        mLooper.dispatchAll();
+        assertFalse(mEnMgr.isCellularUpstreamPermitted());
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true);
+        mLooper.dispatchAll();
+        assertFalse(mEnMgr.isCellularUpstreamPermitted());
+    }
+
+    @Test
+    public void verifyPermissionIfAnyApproved() {
+        setupForRequiredProvisioning();
+        mEnMgr.notifyUpstream(true);
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
+        mLooper.dispatchAll();
+        assertTrue(mEnMgr.isCellularUpstreamPermitted());
+        mLooper.dispatchAll();
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
+        mLooper.dispatchAll();
+        assertTrue(mEnMgr.isCellularUpstreamPermitted());
+        mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
+        mLooper.dispatchAll();
+        assertFalse(mEnMgr.isCellularUpstreamPermitted());
+
+    }
+
+    @Test
+    public void verifyPermissionWhenProvisioningNotStarted() {
+        assertTrue(mEnMgr.isCellularUpstreamPermitted());
+        setupForRequiredProvisioning();
+        assertFalse(mEnMgr.isCellularUpstreamPermitted());
+    }
+
+    @Test
+    public void testRunTetherProvisioning() {
+        setupForRequiredProvisioning();
+        // 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();
+        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();
+        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();
+        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();
+        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(mConfig);
+        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();
+        assertEquals(0, mEnMgr.uiProvisionCount);
+        assertEquals(3, mEnMgr.silentProvisionCount);
+        mEnMgr.reset();
+    }
+
+    @Test
+    public void testCallStopTetheringWhenUiProvisioningFail() {
+        setupForRequiredProvisioning();
+        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
+                mLoggingState = new EntitlementManagerTest.TestStateMachine.LoggingState();
+
+        class LoggingState extends State {
+            @Override public void enter() {
+                messages.clear();
+            }
+
+            @Override public void exit() {
+                messages.clear();
+            }
+
+            @Override public boolean processMessage(Message msg) {
+                messages.add(msg);
+                return false;
+            }
+        }
+
+        public TestStateMachine() {
+            super("EntitlementManagerTest.TestStateMachine", mLooper.getLooper());
+            addState(mLoggingState);
+            setInitialState(mLoggingState);
+            super.start();
+        }
+    }
+}