Merge "Add Entitlement Manger to encapsulate provisioning mechanics"

am: 1c66fd2d67

Change-Id: I5793f0b7d0c082a05e7d374d8b3a6d77cab6094d
This commit is contained in:
markchien
2018-11-25 18:37:01 -08:00
committed by android-build-merger
5 changed files with 412 additions and 245 deletions

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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());
}
}