Merge "Add Entitlement Manger to encapsulate provisioning mechanics"
am: 1c66fd2d67
Change-Id: I5793f0b7d0c082a05e7d374d8b3a6d77cab6094d
This commit is contained in:
@@ -23,24 +23,18 @@ import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
|
||||
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_TETHERED;
|
||||
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.BluetoothPan;
|
||||
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.content.res.Resources;
|
||||
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.Network;
|
||||
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.INetworkManagementService;
|
||||
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.Protocol;
|
||||
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 com.android.server.net.BaseNetworkObserver;
|
||||
|
||||
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 @@ public class Tethering extends BaseNetworkObserver {
|
||||
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 @@ public class Tethering extends BaseNetworkObserver {
|
||||
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 @@ public class Tethering extends BaseNetworkObserver {
|
||||
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 @@ public class Tethering extends BaseNetworkObserver {
|
||||
mStatsService = statsService;
|
||||
mPolicyManager = policyManager;
|
||||
mLooper = looper;
|
||||
mSystemProperties = systemProperties;
|
||||
mDeps = deps;
|
||||
|
||||
mPublicSync = new Object();
|
||||
@@ -241,12 +223,13 @@ public class Tethering extends BaseNetworkObserver {
|
||||
|
||||
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 @@ public class Tethering extends BaseNetworkObserver {
|
||||
private void updateConfiguration() {
|
||||
mConfig = new TetheringConfiguration(mContext, mLog);
|
||||
mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
|
||||
mEntitlementMgr.updateConfiguration(mConfig);
|
||||
}
|
||||
|
||||
private void maybeUpdateConfiguration() {
|
||||
@@ -354,83 +338,54 @@ public class Tethering extends BaseNetworkObserver {
|
||||
}
|
||||
|
||||
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 @@ public class Tethering extends BaseNetworkObserver {
|
||||
? 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 @@ public class Tethering extends BaseNetworkObserver {
|
||||
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 @@ public class Tethering extends BaseNetworkObserver {
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 class TetheringDependencies {
|
||||
public NetworkRequest getDefaultNetworkRequest() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public EntitlementManager getEntitlementManager(Context ctx, SharedLog log,
|
||||
MockableSystemProperties systemProperties) {
|
||||
return new EntitlementManager(ctx, log, systemProperties);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,15 +23,15 @@ import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
|
||||
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.assertEquals;
|
||||
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.INetworkManagementService;
|
||||
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 @@ import java.util.Vector;
|
||||
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 @@ public class TetheringTest {
|
||||
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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user