Merge "Add Entitlement Manger to encapsulate provisioning mechanics" am: 1c66fd2d67
am: 8a01337f5c
Change-Id: I18240295245c7e9b3b5299910d17643bc7f3141a
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index a8f7259..3c14393 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -23,24 +23,18 @@
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
-import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
import static android.net.ConnectivityManager.EXTRA_ERRORED_TETHER;
import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
-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_MASTER_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_INVALID;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -50,6 +44,7 @@
import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
+
import static com.android.server.ConnectivityService.SHORT_ARG;
import android.app.Notification;
@@ -60,7 +55,6 @@
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfile.ServiceListener;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -68,7 +62,6 @@
import android.hardware.usb.UsbManager;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
-import android.net.ip.IpServer;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -76,7 +69,7 @@
import android.net.NetworkInfo;
import android.net.NetworkState;
import android.net.NetworkUtils;
-import android.net.RouteInfo;
+import android.net.ip.IpServer;
import android.net.util.InterfaceSet;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
@@ -89,15 +82,12 @@
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
-import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
-import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -113,6 +103,7 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.LocalServices;
+import com.android.server.connectivity.tethering.EntitlementManager;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadController;
import com.android.server.connectivity.tethering.TetheringConfiguration;
@@ -123,8 +114,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
@@ -145,18 +134,12 @@
private final static boolean DBG = false;
private final static boolean VDBG = false;
- protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
-
private static final Class[] messageClasses = {
Tethering.class, TetherMasterSM.class, IpServer.class
};
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
- // {@link ComponentName} of the Service used to run tether provisioning.
- private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
- .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
-
private static class TetherState {
public final IpServer ipServer;
public int lastState;
@@ -191,7 +174,6 @@
private final INetworkStatsService mStatsService;
private final INetworkPolicyManager mPolicyManager;
private final Looper mLooper;
- private final MockableSystemProperties mSystemProperties;
private final StateMachine mTetherMasterSM;
private final OffloadController mOffloadController;
private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@@ -200,6 +182,7 @@
private final HashSet<IpServer> mForwardedDownstreams;
private final VersionedBroadcastListener mCarrierConfigChange;
private final TetheringDependencies mDeps;
+ private final EntitlementManager mEntitlementMgr;
private volatile TetheringConfiguration mConfig;
private InterfaceSet mCurrentUpstreamIfaceSet;
@@ -220,7 +203,6 @@
mStatsService = statsService;
mPolicyManager = policyManager;
mLooper = looper;
- mSystemProperties = systemProperties;
mDeps = deps;
mPublicSync = new Object();
@@ -241,12 +223,13 @@
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
+ mEntitlementMgr = mDeps.getEntitlementManager(mContext, mLog, systemProperties);
mCarrierConfigChange = new VersionedBroadcastListener(
"CarrierConfigChangeListener", mContext, smHandler, filter,
(Intent ignored) -> {
mLog.log("OBSERVED carrier config change");
updateConfiguration();
- reevaluateSimCardProvisioning();
+ mEntitlementMgr.reevaluateSimCardProvisioning();
});
mStateReceiver = new StateReceiver();
@@ -289,6 +272,7 @@
private void updateConfiguration() {
mConfig = new TetheringConfiguration(mContext, mLog);
mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
+ mEntitlementMgr.updateConfiguration(mConfig);
}
private void maybeUpdateConfiguration() {
@@ -354,83 +338,54 @@
}
public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
- if (!isTetherProvisioningRequired()) {
+ mEntitlementMgr.startTethering(type);
+ if (!mEntitlementMgr.isTetherProvisioningRequired()) {
enableTetheringInternal(type, true, receiver);
return;
}
+ final ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
if (showProvisioningUi) {
- runUiTetherProvisioningAndEnable(type, receiver);
+ mEntitlementMgr.runUiTetherProvisioningAndEnable(type, proxyReceiver);
} else {
- runSilentTetherProvisioningAndEnable(type, receiver);
+ mEntitlementMgr.runSilentTetherProvisioningAndEnable(type, proxyReceiver);
}
}
public void stopTethering(int type) {
enableTetheringInternal(type, false, null);
- if (isTetherProvisioningRequired()) {
- cancelTetherProvisioningRechecks(type);
+ mEntitlementMgr.stopTethering(type);
+ if (mEntitlementMgr.isTetherProvisioningRequired()) {
+ // There are lurking bugs where the notion of "provisioning required" or
+ // "tethering supported" may change without notifying tethering properly, then
+ // tethering can't shutdown correctly.
+ // TODO: cancel re-check all the time
+ if (mDeps.isTetheringSupported()) {
+ mEntitlementMgr.cancelTetherProvisioningRechecks(type);
+ }
}
}
/**
- * Check if the device requires a provisioning check in order to enable tethering.
- *
- * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
- */
- @VisibleForTesting
- protected boolean isTetherProvisioningRequired() {
- final TetheringConfiguration cfg = mConfig;
- if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
- || cfg.provisioningApp.length == 0) {
- return false;
- }
- if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
- return false;
- }
- return (cfg.provisioningApp.length == 2);
- }
-
- // The logic here is aimed solely at confirming that a CarrierConfig exists
- // and affirms that entitlement checks are not required.
- //
- // TODO: find a better way to express this, or alter the checking process
- // entirely so that this is more intuitive.
- private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
- // Check carrier config for entitlement checks
- final CarrierConfigManager configManager = (CarrierConfigManager) mContext
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager == null) return false;
-
- final PersistableBundle carrierConfig = configManager.getConfig();
- if (carrierConfig == null) return false;
-
- // A CarrierConfigManager was found and it has a config.
- final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
- CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
- return !isEntitlementCheckRequired;
- }
-
- /**
* Enables or disables tethering for the given type. This should only be called once
* provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
* for the specified interface.
*/
private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
- boolean isProvisioningRequired = enable && isTetherProvisioningRequired();
+ boolean isProvisioningRequired = enable && mEntitlementMgr.isTetherProvisioningRequired();
int result;
switch (type) {
case TETHERING_WIFI:
result = setWifiTethering(enable);
if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
- scheduleProvisioningRechecks(type);
+ mEntitlementMgr.scheduleProvisioningRechecks(type);
}
sendTetherResult(receiver, result);
break;
case TETHERING_USB:
result = setUsbTethering(enable);
if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
- scheduleProvisioningRechecks(type);
+ mEntitlementMgr.scheduleProvisioningRechecks(type);
}
sendTetherResult(receiver, result);
break;
@@ -489,32 +444,14 @@
? TETHER_ERROR_NO_ERROR
: TETHER_ERROR_MASTER_ERROR;
sendTetherResult(receiver, result);
- if (enable && isTetherProvisioningRequired()) {
- scheduleProvisioningRechecks(TETHERING_BLUETOOTH);
+ if (enable && mEntitlementMgr.isTetherProvisioningRequired()) {
+ mEntitlementMgr.scheduleProvisioningRechecks(TETHERING_BLUETOOTH);
}
adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
}
}, BluetoothProfile.PAN);
}
- private void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
- ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
- sendUiTetherProvisionIntent(type, proxyReceiver);
- }
-
- private void sendUiTetherProvisionIntent(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);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
/**
* Creates a proxy {@link ResultReceiver} which enables tethering if the provisioning result
* is successful before firing back up to the wrapped receiver.
@@ -546,62 +483,6 @@
return receiverForSending;
}
- private void scheduleProvisioningRechecks(int type) {
- Intent intent = new Intent();
- intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(EXTRA_SET_ALARM, true);
- intent.setComponent(TETHER_SERVICE);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startServiceAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- private void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
- ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
- sendSilentTetherProvisionIntent(type, proxyReceiver);
- }
-
- private void sendSilentTetherProvisionIntent(int type, ResultReceiver receiver) {
- Intent intent = new Intent();
- intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
- intent.putExtra(EXTRA_RUN_PROVISION, true);
- intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
- intent.setComponent(TETHER_SERVICE);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startServiceAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- private void cancelTetherProvisioningRechecks(int type) {
- if (mDeps.isTetheringSupported()) {
- Intent intent = new Intent();
- intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
- intent.setComponent(TETHER_SERVICE);
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.startServiceAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- // Used by the SIM card change observation code.
- // TODO: De-duplicate with above code, where possible.
- private void startProvisionIntent(int tetherType) {
- final Intent startProvIntent = new Intent();
- startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
- startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
- startProvIntent.setComponent(TETHER_SERVICE);
- mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
- }
-
public int tether(String iface) {
return tether(iface, IpServer.STATE_TETHERED);
}
@@ -1166,30 +1047,6 @@
return false;
}
- private void reevaluateSimCardProvisioning() {
- if (!mConfig.hasMobileHotspotProvisionApp()) return;
- if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
-
- ArrayList<Integer> tethered = new ArrayList<>();
- synchronized (mPublicSync) {
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastState != IpServer.STATE_TETHERED) {
- continue; // Skip interfaces that aren't tethered.
- }
- String iface = mTetherStates.keyAt(i);
- int interfaceType = ifaceNameToType(iface);
- if (interfaceType != TETHERING_INVALID) {
- tethered.add(interfaceType);
- }
- }
- }
-
- for (int tetherType : tethered) {
- startProvisionIntent(tetherType);
- }
- }
-
class TetherMasterSM extends StateMachine {
private static final int BASE_MASTER = Protocol.BASE_TETHERING;
// an interface SM has requested Tethering/Local Hotspot
diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
new file mode 100644
index 0000000..a4e3e1d
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -0,0 +1,224 @@
+/*
+ * 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.EXTRA_ADD_TETHER_TYPE;
+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 com.android.internal.R.string.config_wifi_tether_enable;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.util.SharedLog;
+import android.os.Binder;
+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 com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.connectivity.MockableSystemProperties;
+
+/**
+ * This class encapsulates entitlement/provisioning mechanics
+ * provisioning check only applies to the use of the mobile network as an upstream
+ *
+ * @hide
+ */
+public class EntitlementManager {
+ private static final String TAG = EntitlementManager.class.getSimpleName();
+
+ // {@link ComponentName} of the Service used to run tether provisioning.
+ private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
+ Resources.getSystem().getString(config_wifi_tether_enable));
+ protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
+
+ // The ArraySet contains enabled downstream types, ex:
+ // {@link ConnectivityManager.TETHERING_WIFI}
+ // {@link ConnectivityManager.TETHERING_USB}
+ // {@link ConnectivityManager.TETHERING_BLUETOOTH}
+ @GuardedBy("mCurrentTethers")
+ private final ArraySet<Integer> mCurrentTethers;
+ private final Context mContext;
+ private final MockableSystemProperties mSystemProperties;
+ private final SharedLog mLog;
+ @Nullable
+ private TetheringConfiguration mConfig;
+
+ public EntitlementManager(Context ctx, SharedLog log,
+ MockableSystemProperties systemProperties) {
+ mContext = ctx;
+ mLog = log;
+ mCurrentTethers = new ArraySet<Integer>();
+ mSystemProperties = systemProperties;
+ }
+
+ /**
+ * Pass a new TetheringConfiguration instance each time when
+ * Tethering#updateConfiguration() is called.
+ */
+ public void updateConfiguration(TetheringConfiguration conf) {
+ mConfig = conf;
+ }
+
+ /**
+ * Tell EntitlementManager that a given type of tethering has been enabled
+ *
+ * @param type Tethering type
+ */
+ public void startTethering(int type) {
+ synchronized (mCurrentTethers) {
+ mCurrentTethers.add(type);
+ }
+ }
+
+ /**
+ * Tell EntitlementManager that a given type of tethering has been disabled
+ *
+ * @param type Tethering type
+ */
+ public void stopTethering(int type) {
+ synchronized (mCurrentTethers) {
+ mCurrentTethers.remove(type);
+ }
+ }
+
+ /**
+ * Check if the device requires a provisioning check in order to enable tethering.
+ *
+ * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
+ */
+ @VisibleForTesting
+ public boolean isTetherProvisioningRequired() {
+ if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
+ || mConfig.provisioningApp.length == 0) {
+ return false;
+ }
+ if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
+ return false;
+ }
+ return (mConfig.provisioningApp.length == 2);
+ }
+
+ /**
+ * Re-check tethering provisioning for enabled downstream tether types.
+ * Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
+ */
+ public void reevaluateSimCardProvisioning() {
+ if (!mConfig.hasMobileHotspotProvisionApp()) return;
+ if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
+
+ final ArraySet<Integer> reevaluateType;
+ synchronized (mCurrentTethers) {
+ reevaluateType = new ArraySet<Integer>(mCurrentTethers);
+ }
+ for (Integer type : reevaluateType) {
+ startProvisionIntent(type);
+ }
+ }
+
+ // The logic here is aimed solely at confirming that a CarrierConfig exists
+ // and affirms that entitlement checks are not required.
+ //
+ // TODO: find a better way to express this, or alter the checking process
+ // entirely so that this is more intuitive.
+ private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
+ // Check carrier config for entitlement checks
+ final CarrierConfigManager configManager = (CarrierConfigManager) mContext
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager == null) return false;
+
+ final PersistableBundle carrierConfig = configManager.getConfig();
+ if (carrierConfig == null) return false;
+
+ // A CarrierConfigManager was found and it has a config.
+ final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
+ return !isEntitlementCheckRequired;
+ }
+
+ public void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_RUN_PROVISION, true);
+ intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public void runUiTetherProvisioningAndEnable(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);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ // Used by the SIM card change observation code.
+ // TODO: De-duplicate with above code, where possible.
+ private void startProvisionIntent(int tetherType) {
+ final Intent startProvIntent = new Intent();
+ startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
+ startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
+ startProvIntent.setComponent(TETHER_SERVICE);
+ mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
+ }
+
+ public void scheduleProvisioningRechecks(int type) {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_SET_ALARM, true);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public void cancelTetherProvisioningRechecks(int type) {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
+ intent.setComponent(TETHER_SERVICE);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startServiceAsUser(intent, UserHandle.CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+}
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 8b40069..d56b167 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -17,19 +17,13 @@
package com.android.server.connectivity.tethering;
import android.content.Context;
-import android.net.INetd;
import android.net.NetworkRequest;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
import android.net.ip.IpServer;
-import android.net.ip.RouterAdvertisementDaemon;
-import android.net.util.InterfaceParams;
-import android.net.util.NetdService;
-import android.os.Handler;
import android.net.util.SharedLog;
-import android.os.Looper;
+import android.os.Handler;
import com.android.internal.util.StateMachine;
+import com.android.server.connectivity.MockableSystemProperties;
import java.util.ArrayList;
@@ -65,4 +59,9 @@
public NetworkRequest getDefaultNetworkRequest() {
return null;
}
+
+ public EntitlementManager getEntitlementManager(Context ctx, SharedLog log,
+ MockableSystemProperties systemProperties) {
+ return new EntitlementManager(ctx, log, systemProperties);
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index a6ed9f2..8081812 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -23,15 +23,15 @@
import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
-import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
@@ -39,19 +39,18 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.notNull;
-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.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.mock;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -91,9 +90,9 @@
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
-import android.os.test.TestLooper;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.test.TestLooper;
import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -126,7 +125,6 @@
public class TetheringTest {
private static final int IFINDEX_OFFSET = 100;
- private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
private static final String TEST_USB_IFNAME = "test_rndis0";
@@ -370,61 +368,6 @@
mServiceContext.unregisterReceiver(mBroadcastReceiver);
}
- private void setupForRequiredProvisioning() {
- // Produce some acceptable looking provision app setting if requested.
- when(mResources.getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app))
- .thenReturn(PROVISIONING_APP_NAME);
- // Don't disable tethering provisioning unless requested.
- when(mSystemProperties.getBoolean(eq(Tethering.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.getConfig()).thenReturn(mCarrierConfig);
- mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
- }
-
- @Test
- public void canRequireProvisioning() {
- setupForRequiredProvisioning();
- sendConfigurationChanged();
- assertTrue(mTethering.isTetherProvisioningRequired());
- }
-
- @Test
- public void toleratesCarrierConfigManagerMissing() {
- setupForRequiredProvisioning();
- when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
- .thenReturn(null);
- sendConfigurationChanged();
- // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
- // We therefore still require provisioning.
- assertTrue(mTethering.isTetherProvisioningRequired());
- }
-
- @Test
- public void toleratesCarrierConfigMissing() {
- setupForRequiredProvisioning();
- when(mCarrierConfigManager.getConfig()).thenReturn(null);
- sendConfigurationChanged();
- // We still have a provisioning app configured, so still require provisioning.
- assertTrue(mTethering.isTetherProvisioningRequired());
- }
-
- @Test
- public void provisioningNotRequiredWhenAppNotFound() {
- setupForRequiredProvisioning();
- when(mResources.getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app))
- .thenReturn(null);
- assertTrue(!mTethering.isTetherProvisioningRequired());
- when(mResources.getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app))
- .thenReturn(new String[] {"malformedApp"});
- assertTrue(!mTethering.isTetherProvisioningRequired());
- }
-
private void sendWifiApStateChanged(int state) {
final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
intent.putExtra(EXTRA_WIFI_AP_STATE, state);
diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
new file mode 100644
index 0000000..0f72229
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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 org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+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.PersistableBundle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.CarrierConfigManager;
+
+import com.android.internal.R;
+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;
+
+@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"};
+
+ @Mock private CarrierConfigManager mCarrierConfigManager;
+ @Mock private Context mContext;
+ @Mock private ContentResolver mContent;
+ @Mock private MockableSystemProperties mSystemProperties;
+ @Mock private Resources mResources;
+ @Mock private SharedLog mLog;
+
+ // 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 EntitlementManager mEnMgr;
+
+ @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))
+ .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);
+
+ mEnMgr = new EntitlementManager(mContext, mLog, mSystemProperties);
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ }
+
+ @After
+ public void tearDown() throws Exception {}
+
+ 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);
+ // 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.getConfig()).thenReturn(mCarrierConfig);
+ mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
+ }
+
+ @Test
+ public void canRequireProvisioning() {
+ setupForRequiredProvisioning();
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ assertTrue(mEnMgr.isTetherProvisioningRequired());
+ }
+
+ @Test
+ public void toleratesCarrierConfigManagerMissing() {
+ setupForRequiredProvisioning();
+ when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+ .thenReturn(null);
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
+ // Therefore provisioning still be required.
+ assertTrue(mEnMgr.isTetherProvisioningRequired());
+ }
+
+ @Test
+ public void toleratesCarrierConfigMissing() {
+ setupForRequiredProvisioning();
+ when(mCarrierConfigManager.getConfig()).thenReturn(null);
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ // We still have a provisioning app configured, so still require provisioning.
+ assertTrue(mEnMgr.isTetherProvisioningRequired());
+ }
+
+ @Test
+ public void provisioningNotRequiredWhenAppNotFound() {
+ setupForRequiredProvisioning();
+ when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+ .thenReturn(null);
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ assertFalse(mEnMgr.isTetherProvisioningRequired());
+ when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+ .thenReturn(new String[] {"malformedApp"});
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ assertFalse(mEnMgr.isTetherProvisioningRequired());
+ }
+
+}