Add get last entitlement value callback API
The callback would be used to notify entitlement value. If the
cache value indicates entitlement succeeded, it just fire
callback with cache value instead of run entitlement check.
Bug: 120887283
Test: atest FrameworksNetTests
Change-Id: I8afe928423bd75c54c61533a50a5c0814922ceb1
diff --git a/api/system-current.txt b/api/system-current.txt
index 20ad25b..8b00789 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3067,6 +3067,7 @@
method @RequiresPermission("android.permission.PACKET_KEEPALIVE_OFFLOAD") public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull java.io.FileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
method public boolean getAvoidBadWifi();
method @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) public String getCaptivePortalServerUrl();
+ method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
@@ -3077,6 +3078,9 @@
field public static final int TETHERING_BLUETOOTH = 2; // 0x2
field public static final int TETHERING_USB = 1; // 0x1
field public static final int TETHERING_WIFI = 0; // 0x0
+ field public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd
+ field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+ field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
}
public abstract static class ConnectivityManager.OnStartTetheringCallback {
@@ -3085,6 +3089,11 @@
method public void onTetheringStarted();
}
+ public abstract static class ConnectivityManager.TetheringEntitlementValueListener {
+ ctor public ConnectivityManager.TetheringEntitlementValueListener();
+ method public void onEntitlementResult(int);
+ }
+
public final class IpPrefix implements android.os.Parcelable {
ctor public IpPrefix(java.net.InetAddress, int);
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 5bb24ba..2ecc647 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2544,6 +2544,7 @@
}
/** {@hide} */
+ @SystemApi
public static final int TETHER_ERROR_NO_ERROR = 0;
/** {@hide} */
public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
@@ -2566,9 +2567,13 @@
/** {@hide} */
public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
/** {@hide} */
+ @SystemApi
public static final int TETHER_ERROR_PROVISION_FAILED = 11;
/** {@hide} */
public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12;
+ /** {@hide} */
+ @SystemApi
+ public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13;
/**
* Get a more detailed error code after a Tethering or Untethering
@@ -2591,6 +2596,65 @@
}
/**
+ * Callback for use with {@link #getLatestTetheringEntitlementValue} to find out whether
+ * entitlement succeeded.
+ * @hide
+ */
+ @SystemApi
+ public abstract static class TetheringEntitlementValueListener {
+ /**
+ * Called to notify entitlement result.
+ *
+ * @param resultCode a int value of entitlement result. It may be one of
+ * {@link #TETHER_ERROR_NO_ERROR},
+ * {@link #TETHER_ERROR_PROVISION_FAILED}, or
+ * {@link #TETHER_ERROR_ENTITLEMENT_UNKONWN}.
+ */
+ public void onEntitlementResult(int resultCode) {}
+ }
+
+ /**
+ * Get the last value of the entitlement check on this downstream. If the cached value is
+ * {@link #TETHER_ERROR_NO_ERROR} or showEntitlementUi argument is false, it just return the
+ * cached value. Otherwise, a UI-based entitlement check would be performed. It is not
+ * guaranteed that the UI-based entitlement check will complete in any specific time period
+ * and may in fact never complete. Any successful entitlement check the platform performs for
+ * any reason will update the cached value.
+ *
+ * @param type the downstream type of tethering. Must be one of
+ * {@link #TETHERING_WIFI},
+ * {@link #TETHERING_USB}, or
+ * {@link #TETHERING_BLUETOOTH}.
+ * @param showEntitlementUi a boolean indicating whether to run UI-based entitlement check.
+ * @param listener an {@link TetheringEntitlementValueListener} which will be called to notify
+ * the caller of the result of entitlement check. The listener may be called zero or
+ * one time.
+ * @param handler {@link Handler} to specify the thread upon which the listener will be invoked.
+ * {@hide}
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+ public void getLatestTetheringEntitlementValue(int type, boolean showEntitlementUi,
+ @NonNull final TetheringEntitlementValueListener listener, @Nullable Handler handler) {
+ Preconditions.checkNotNull(listener, "TetheringEntitlementValueListener cannot be null.");
+ ResultReceiver wrappedListener = new ResultReceiver(handler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ listener.onEntitlementResult(resultCode);
+ }
+ };
+
+ try {
+ String pkgName = mContext.getOpPackageName();
+ Log.i(TAG, "getLatestTetheringEntitlementValue:" + pkgName);
+ mService.getLatestTetheringEntitlementValue(type, wrappedListener,
+ showEntitlementUi, pkgName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Report network connectivity status. This is currently used only
* to alter status bar UI.
* <p>This method requires the caller to hold the permission
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index e97060a..949b772 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -194,4 +194,7 @@
int getConnectionOwnerUid(in ConnectionInfo connectionInfo);
boolean isCallerCurrentAlwaysOnVpnApp();
boolean isCallerCurrentAlwaysOnVpnLockdownApp();
+
+ void getLatestTetheringEntitlementValue(int type, in ResultReceiver receiver,
+ boolean showEntitlementUi, String callerPkg);
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 14e2354..bc9f9e3 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3624,6 +3624,20 @@
mTethering.stopTethering(type);
}
+ /**
+ * Get the latest value of the tethering entitlement check.
+ *
+ * Note: Allow privileged apps who have TETHER_PRIVILEGED permission to access. If it turns
+ * out some such apps are observed to abuse this API, change to per-UID limits on this API
+ * if it's really needed.
+ */
+ @Override
+ public void getLatestTetheringEntitlementValue(int type, ResultReceiver receiver,
+ boolean showEntitlementUi, String callerPkg) {
+ ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
+ mTethering.getLatestTetheringEntitlementValue(type, receiver, showEntitlementUi);
+ }
+
// Called when we lose the default network and have no replacement yet.
// This will automatically be cleared after X seconds or a new default network
// becomes CONNECTED, whichever happens first. The timer is started by the
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index eb5be77..a14fd17 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -121,7 +121,6 @@
import java.util.HashSet;
import java.util.Set;
-
/**
* @hide
*
@@ -223,7 +222,8 @@
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
- mEntitlementMgr = mDeps.getEntitlementManager(mContext, mLog, systemProperties);
+ mEntitlementMgr = mDeps.getEntitlementManager(mContext, mTetherMasterSM,
+ mLog, systemProperties);
mCarrierConfigChange = new VersionedBroadcastListener(
"CarrierConfigChangeListener", mContext, smHandler, filter,
(Intent ignored) -> {
@@ -470,6 +470,7 @@
} else {
sendTetherResult(receiver, resultCode);
}
+ mEntitlementMgr.updateEntitlementCacheValue(type, resultCode);
}
};
@@ -1662,6 +1663,14 @@
mUpstreamNetworkMonitor.startTrackDefaultNetwork(mDeps.getDefaultNetworkRequest());
}
+ /** Get the latest value of the tethering entitlement check. */
+ public void getLatestTetheringEntitlementValue(int type, ResultReceiver receiver,
+ boolean showEntitlementUi) {
+ if (receiver != null) {
+ mEntitlementMgr.getLatestTetheringEntitlementValue(type, receiver, showEntitlementUi);
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
// Binder.java closes the resource for us.
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 a4e3e1d..75aac10 100644
--- a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -21,6 +21,9 @@
import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
+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 com.android.internal.R.string.config_wifi_tether_enable;
@@ -31,15 +34,21 @@
import android.content.res.Resources;
import android.net.util.SharedLog;
import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.StateMachine;
import com.android.server.connectivity.MockableSystemProperties;
/**
@@ -50,6 +59,7 @@
*/
public class EntitlementManager {
private static final String TAG = EntitlementManager.class.getSimpleName();
+ private static final boolean DBG = false;
// {@link ComponentName} of the Service used to run tether provisioning.
private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
@@ -65,15 +75,19 @@
private final Context mContext;
private final MockableSystemProperties mSystemProperties;
private final SharedLog mLog;
+ private final Handler mMasterHandler;
+ private final SparseIntArray mEntitlementCacheValue;
@Nullable
private TetheringConfiguration mConfig;
- public EntitlementManager(Context ctx, SharedLog log,
+ public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
MockableSystemProperties systemProperties) {
mContext = ctx;
- mLog = log;
+ mLog = log.forSubComponent(TAG);
mCurrentTethers = new ArraySet<Integer>();
mSystemProperties = systemProperties;
+ mEntitlementCacheValue = new SparseIntArray();
+ mMasterHandler = tetherMasterSM.getHandler();
}
/**
@@ -128,6 +142,10 @@
* Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
*/
public void reevaluateSimCardProvisioning() {
+ synchronized (mEntitlementCacheValue) {
+ mEntitlementCacheValue.clear();
+ }
+
if (!mConfig.hasMobileHotspotProvisionApp()) return;
if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
@@ -175,6 +193,11 @@
}
public void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+ runUiTetherProvisioning(type, receiver);
+ }
+
+ @VisibleForTesting
+ protected void runUiTetherProvisioning(int type, ResultReceiver receiver) {
Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
@@ -221,4 +244,70 @@
Binder.restoreCallingIdentity(ident);
}
}
+
+ private ResultReceiver buildProxyReceiver(int type, final ResultReceiver receiver) {
+ ResultReceiver rr = new ResultReceiver(mMasterHandler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ int updatedCacheValue = updateEntitlementCacheValue(type, resultCode);
+ receiver.send(updatedCacheValue, null);
+ }
+ };
+
+ return writeToParcel(rr);
+ }
+
+ private ResultReceiver writeToParcel(final ResultReceiver receiver) {
+ // This is necessary to avoid unmarshalling issues when sending the receiver
+ // across processes.
+ Parcel parcel = Parcel.obtain();
+ receiver.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ return receiverForSending;
+ }
+
+ /**
+ * Update the last entitlement value to internal cache
+ *
+ * @param type tethering type from ConnectivityManager.TETHERING_{@code *}
+ * @param resultCode last entitlement value
+ * @return the last updated entitlement value
+ */
+ public int updateEntitlementCacheValue(int type, int resultCode) {
+ if (DBG) {
+ Log.d(TAG, "updateEntitlementCacheValue: " + type + ", result: " + resultCode);
+ }
+ synchronized (mEntitlementCacheValue) {
+ if (resultCode == TETHER_ERROR_NO_ERROR) {
+ mEntitlementCacheValue.put(type, resultCode);
+ return resultCode;
+ } else {
+ mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISION_FAILED);
+ return TETHER_ERROR_PROVISION_FAILED;
+ }
+ }
+ }
+
+ /** Get the last value of the tethering entitlement check. */
+ public void getLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver,
+ boolean showEntitlementUi) {
+ if (!isTetherProvisioningRequired()) {
+ receiver.send(TETHER_ERROR_NO_ERROR, null);
+ return;
+ }
+
+ final int cacheValue;
+ synchronized (mEntitlementCacheValue) {
+ cacheValue = mEntitlementCacheValue.get(
+ downstream, TETHER_ERROR_ENTITLEMENT_UNKONWN);
+ }
+ if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) {
+ receiver.send(cacheValue, null);
+ } else {
+ ResultReceiver proxy = buildProxyReceiver(downstream, receiver);
+ runUiTetherProvisioning(downstream, proxy);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index a42efe9..6d6f81e 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -81,8 +81,8 @@
/**
* Get a reference to the EntitlementManager to be used by tethering.
*/
- public EntitlementManager getEntitlementManager(Context ctx, SharedLog log,
- MockableSystemProperties systemProperties) {
- return new EntitlementManager(ctx, log, systemProperties);
+ public EntitlementManager getEntitlementManager(Context ctx, StateMachine target,
+ SharedLog log, MockableSystemProperties systemProperties) {
+ return new EntitlementManager(ctx, target, log, systemProperties);
}
}
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 0f72229..ec286759 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -16,8 +16,16 @@
package com.android.server.connectivity.tethering;
+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 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.anyString;
import static org.mockito.Matchers.eq;
@@ -27,12 +35,22 @@
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.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.telephony.CarrierConfigManager;
+import android.test.mock.MockContentResolver;
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;
@@ -42,6 +60,10 @@
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 {
@@ -51,7 +73,6 @@
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private Context mContext;
- @Mock private ContentResolver mContent;
@Mock private MockableSystemProperties mSystemProperties;
@Mock private Resources mResources;
@Mock private SharedLog mLog;
@@ -59,15 +80,49 @@
// 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 EntitlementManager mEnMgr;
+ private TestStateMachine mSM;
+ private WrappedEntitlementManager mEnMgr;
+
+ 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 boolean everRunUiEntitlement = false;
+
+ public WrappedEntitlementManager(Context ctx, StateMachine target,
+ SharedLog log, MockableSystemProperties systemProperties) {
+ super(ctx, target, log, systemProperties);
+ }
+
+ @Override
+ protected void runUiTetherProvisioning(int type, ResultReceiver receiver) {
+ everRunUiEntitlement = true;
+ receiver.send(fakeEntitlementResult, null);
+ }
+ }
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mContext.getResources()).thenReturn(mResources);
- when(mContext.getContentResolver()).thenReturn(mContent);
when(mResources.getStringArray(R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
@@ -80,12 +135,21 @@
.thenReturn(new int[0]);
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
- mEnMgr = new EntitlementManager(mContext, mLog, mSystemProperties);
- mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ mContentResolver = new MockContentResolver();
+ mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ mMockContext = new MockContext(mContext);
+ mSM = new TestStateMachine();
+ mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, mSystemProperties);
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
}
@After
- public void tearDown() throws Exception {}
+ public void tearDown() throws Exception {
+ if (mSM != null) {
+ mSM.quit();
+ mSM = null;
+ }
+ }
private void setupForRequiredProvisioning() {
// Produce some acceptable looking provision app setting if requested.
@@ -104,7 +168,7 @@
@Test
public void canRequireProvisioning() {
setupForRequiredProvisioning();
- mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
assertTrue(mEnMgr.isTetherProvisioningRequired());
}
@@ -113,7 +177,7 @@
setupForRequiredProvisioning();
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
.thenReturn(null);
- mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
// Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
// Therefore provisioning still be required.
assertTrue(mEnMgr.isTetherProvisioningRequired());
@@ -123,7 +187,7 @@
public void toleratesCarrierConfigMissing() {
setupForRequiredProvisioning();
when(mCarrierConfigManager.getConfig()).thenReturn(null);
- mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
// We still have a provisioning app configured, so still require provisioning.
assertTrue(mEnMgr.isTetherProvisioningRequired());
}
@@ -133,12 +197,143 @@
setupForRequiredProvisioning();
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
.thenReturn(null);
- mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
assertFalse(mEnMgr.isTetherProvisioningRequired());
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
.thenReturn(new String[] {"malformedApp"});
- mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
assertFalse(mEnMgr.isTetherProvisioningRequired());
}
+ @Test
+ public void testGetLastEntitlementCacheValue() throws Exception {
+ 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) {
+ assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, true);
+ callbackTimeoutHelper(mCallbacklatch);
+ assertFalse(mEnMgr.everRunUiEntitlement);
+
+ setupForRequiredProvisioning();
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
+ // 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) {
+ assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, false);
+ callbackTimeoutHelper(mCallbacklatch);
+ assertFalse(mEnMgr.everRunUiEntitlement);
+ // 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) {
+ assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, true);
+ mLooper.dispatchAll();
+ callbackTimeoutHelper(mCallbacklatch);
+ assertTrue(mEnMgr.everRunUiEntitlement);
+ // 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) {
+ assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, false);
+ callbackTimeoutHelper(mCallbacklatch);
+ assertFalse(mEnMgr.everRunUiEntitlement);
+ // 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) {
+ assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, true);
+ mLooper.dispatchAll();
+ callbackTimeoutHelper(mCallbacklatch);
+ assertTrue(mEnMgr.everRunUiEntitlement);
+ // 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) {
+ assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, true);
+ callbackTimeoutHelper(mCallbacklatch);
+ assertFalse(mEnMgr.everRunUiEntitlement);
+ // 7. Test get value for other downstream type.
+ mEnMgr.everRunUiEntitlement = false;
+ receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_USB, receiver, false);
+ callbackTimeoutHelper(mCallbacklatch);
+ assertFalse(mEnMgr.everRunUiEntitlement);
+ }
+
+ void callbackTimeoutHelper(final CountDownLatch latch) throws Exception {
+ if (!latch.await(1, TimeUnit.SECONDS)) {
+ fail("Timout, fail to recieve callback");
+ }
+ }
+ 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();
+ }
+ }
}