summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/system-current.txt9
-rw-r--r--core/java/android/net/ConnectivityManager.java64
-rw-r--r--core/java/android/net/IConnectivityManager.aidl3
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java14
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java13
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java93
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java6
-rw-r--r--tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java219
8 files changed, 402 insertions, 19 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index 17dcda1e248c..05e78ee1e12f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3070,6 +3070,7 @@ package android.net {
method @RequiresPermission(android.Manifest.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);
@@ -3080,6 +3081,9 @@ package android.net {
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 {
@@ -3088,6 +3092,11 @@ package android.net {
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);
ctor public IpPrefix(String);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index f47ada6ae557..f807924e752d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2581,6 +2581,7 @@ public class ConnectivityManager {
}
/** {@hide} */
+ @SystemApi
public static final int TETHER_ERROR_NO_ERROR = 0;
/** {@hide} */
public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
@@ -2603,9 +2604,13 @@ public class ConnectivityManager {
/** {@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
@@ -2628,6 +2633,65 @@ public class ConnectivityManager {
}
/**
+ * 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 fd7360fd4c17..78fafebc4f37 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -197,4 +197,7 @@ interface IConnectivityManager
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 9892bfa50189..08e49034aa6c 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3643,6 +3643,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 eb5be77e4a33..a14fd17209e8 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.Collection;
import java.util.HashSet;
import java.util.Set;
-
/**
* @hide
*
@@ -223,7 +222,8 @@ public class Tethering extends BaseNetworkObserver {
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 @@ public class Tethering extends BaseNetworkObserver {
} else {
sendTetherResult(receiver, resultCode);
}
+ mEntitlementMgr.updateEntitlementCacheValue(type, resultCode);
}
};
@@ -1662,6 +1663,14 @@ public class Tethering extends BaseNetworkObserver {
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 a4e3e1d85bcb..75aac106e0e0 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_PROVISION_CALLBACK;
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.Intent;
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 @@ import com.android.server.connectivity.MockableSystemProperties;
*/
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 @@ public class EntitlementManager {
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 @@ public class EntitlementManager {
* 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 class EntitlementManager {
}
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 @@ public class EntitlementManager {
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 a42efe960ff9..6d6f81eb98e6 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 @@ public class TetheringDependencies {
/**
* 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 0f72229d38e6..ec286759354a 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.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.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.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 {
@@ -51,7 +73,6 @@ public final class EntitlementManagerTest {
@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 @@ public final class EntitlementManagerTest {
// 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 EntitlementManager 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 @@ public final class EntitlementManagerTest {
.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 @@ public final class EntitlementManagerTest {
@Test
public void canRequireProvisioning() {
setupForRequiredProvisioning();
- mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog));
assertTrue(mEnMgr.isTetherProvisioningRequired());
}
@@ -113,7 +177,7 @@ public final class EntitlementManagerTest {
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 final class EntitlementManagerTest {
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 @@ public final class EntitlementManagerTest {
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();
+ }
+ }
}