Merge "Only apply entitlement check to cellular upstream"
This commit is contained in:
@@ -608,6 +608,9 @@
|
|||||||
|
|
||||||
<protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL" />
|
<protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL" />
|
||||||
|
|
||||||
|
<!-- For tether entitlement recheck-->
|
||||||
|
<protected-broadcast
|
||||||
|
android:name="com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM" />
|
||||||
<!-- ====================================================================== -->
|
<!-- ====================================================================== -->
|
||||||
<!-- RUNTIME PERMISSIONS -->
|
<!-- RUNTIME PERMISSIONS -->
|
||||||
<!-- ====================================================================== -->
|
<!-- ====================================================================== -->
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ import android.os.Handler;
|
|||||||
import android.os.INetworkManagementService;
|
import android.os.INetworkManagementService;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.RemoteCallbackList;
|
import android.os.RemoteCallbackList;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
@@ -230,8 +229,11 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
|
|
||||||
IntentFilter filter = new IntentFilter();
|
IntentFilter filter = new IntentFilter();
|
||||||
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
|
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
|
||||||
mEntitlementMgr = mDeps.getEntitlementManager(mContext, mTetherMasterSM,
|
// EntitlementManager will send EVENT_UPSTREAM_PERMISSION_CHANGED when cellular upstream
|
||||||
mLog, systemProperties);
|
// permission is changed according to entitlement check result.
|
||||||
|
mEntitlementMgr = mDeps.getEntitlementManager(mContext, mTetherMasterSM, mLog,
|
||||||
|
TetherMasterSM.EVENT_UPSTREAM_PERMISSION_CHANGED, systemProperties);
|
||||||
|
|
||||||
mCarrierConfigChange = new VersionedBroadcastListener(
|
mCarrierConfigChange = new VersionedBroadcastListener(
|
||||||
"CarrierConfigChangeListener", mContext, mHandler, filter,
|
"CarrierConfigChangeListener", mContext, mHandler, filter,
|
||||||
(Intent ignored) -> {
|
(Intent ignored) -> {
|
||||||
@@ -363,55 +365,28 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
|
public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
|
||||||
mEntitlementMgr.startTethering(type);
|
mEntitlementMgr.startProvisioningIfNeeded(type, showProvisioningUi);
|
||||||
if (!mEntitlementMgr.isTetherProvisioningRequired()) {
|
enableTetheringInternal(type, true /* enabled */, receiver);
|
||||||
enableTetheringInternal(type, true, receiver);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ResultReceiver proxyReceiver = getProxyReceiver(type, receiver);
|
|
||||||
if (showProvisioningUi) {
|
|
||||||
mEntitlementMgr.runUiTetherProvisioningAndEnable(type, proxyReceiver);
|
|
||||||
} else {
|
|
||||||
mEntitlementMgr.runSilentTetherProvisioningAndEnable(type, proxyReceiver);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopTethering(int type) {
|
public void stopTethering(int type) {
|
||||||
enableTetheringInternal(type, false, null);
|
enableTetheringInternal(type, false /* disabled */, null);
|
||||||
mEntitlementMgr.stopTethering(type);
|
mEntitlementMgr.stopProvisioningIfNeeded(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables or disables tethering for the given type. This should only be called once
|
* Enables or disables tethering for the given type. If provisioning is required, it will
|
||||||
* provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
|
* schedule provisioning rechecks for the specified interface.
|
||||||
* for the specified interface.
|
|
||||||
*/
|
*/
|
||||||
private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
|
private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
|
||||||
boolean isProvisioningRequired = enable && mEntitlementMgr.isTetherProvisioningRequired();
|
|
||||||
int result;
|
int result;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TETHERING_WIFI:
|
case TETHERING_WIFI:
|
||||||
result = setWifiTethering(enable);
|
result = setWifiTethering(enable);
|
||||||
if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
|
|
||||||
mEntitlementMgr.scheduleProvisioningRechecks(type);
|
|
||||||
}
|
|
||||||
sendTetherResult(receiver, result);
|
sendTetherResult(receiver, result);
|
||||||
break;
|
break;
|
||||||
case TETHERING_USB:
|
case TETHERING_USB:
|
||||||
result = setUsbTethering(enable);
|
result = setUsbTethering(enable);
|
||||||
if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
|
|
||||||
mEntitlementMgr.scheduleProvisioningRechecks(type);
|
|
||||||
}
|
|
||||||
sendTetherResult(receiver, result);
|
sendTetherResult(receiver, result);
|
||||||
break;
|
break;
|
||||||
case TETHERING_BLUETOOTH:
|
case TETHERING_BLUETOOTH:
|
||||||
@@ -469,46 +444,11 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
? TETHER_ERROR_NO_ERROR
|
? TETHER_ERROR_NO_ERROR
|
||||||
: TETHER_ERROR_MASTER_ERROR;
|
: TETHER_ERROR_MASTER_ERROR;
|
||||||
sendTetherResult(receiver, result);
|
sendTetherResult(receiver, result);
|
||||||
if (enable && mEntitlementMgr.isTetherProvisioningRequired()) {
|
|
||||||
mEntitlementMgr.scheduleProvisioningRechecks(TETHERING_BLUETOOTH);
|
|
||||||
}
|
|
||||||
adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
|
adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
|
||||||
}
|
}
|
||||||
}, BluetoothProfile.PAN);
|
}, BluetoothProfile.PAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a proxy {@link ResultReceiver} which enables tethering if the provisioning result
|
|
||||||
* is successful before firing back up to the wrapped receiver.
|
|
||||||
*
|
|
||||||
* @param type The type of tethering being enabled.
|
|
||||||
* @param receiver A ResultReceiver which will be called back with an int resultCode.
|
|
||||||
* @return The proxy receiver.
|
|
||||||
*/
|
|
||||||
private ResultReceiver getProxyReceiver(final int type, final ResultReceiver receiver) {
|
|
||||||
ResultReceiver rr = new ResultReceiver(null) {
|
|
||||||
@Override
|
|
||||||
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
|
||||||
// If provisioning is successful, enable tethering, otherwise just send the error.
|
|
||||||
if (resultCode == TETHER_ERROR_NO_ERROR) {
|
|
||||||
enableTetheringInternal(type, true, receiver);
|
|
||||||
} else {
|
|
||||||
sendTetherResult(receiver, resultCode);
|
|
||||||
}
|
|
||||||
mEntitlementMgr.updateEntitlementCacheValue(type, resultCode);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// The following is necessary to avoid unmarshalling issues when sending the receiver
|
|
||||||
// across processes.
|
|
||||||
Parcel parcel = Parcel.obtain();
|
|
||||||
rr.writeToParcel(parcel,0);
|
|
||||||
parcel.setDataPosition(0);
|
|
||||||
ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
|
|
||||||
parcel.recycle();
|
|
||||||
return receiverForSending;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int tether(String iface) {
|
public int tether(String iface) {
|
||||||
return tether(iface, IpServer.STATE_TETHERED);
|
return tether(iface, IpServer.STATE_TETHERED);
|
||||||
}
|
}
|
||||||
@@ -787,6 +727,7 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
if (!usbConnected && mRndisEnabled) {
|
if (!usbConnected && mRndisEnabled) {
|
||||||
// Turn off tethering if it was enabled and there is a disconnect.
|
// Turn off tethering if it was enabled and there is a disconnect.
|
||||||
tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB);
|
tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB);
|
||||||
|
mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_USB);
|
||||||
} else if (usbConfigured && rndisEnabled) {
|
} else if (usbConfigured && rndisEnabled) {
|
||||||
// Tether if rndis is enabled and usb is configured.
|
// Tether if rndis is enabled and usb is configured.
|
||||||
tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB);
|
tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB);
|
||||||
@@ -813,6 +754,7 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
case WifiManager.WIFI_AP_STATE_FAILED:
|
case WifiManager.WIFI_AP_STATE_FAILED:
|
||||||
default:
|
default:
|
||||||
disableWifiIpServingLocked(ifname, curState);
|
disableWifiIpServingLocked(ifname, curState);
|
||||||
|
mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1090,6 +1032,8 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
// we treated the error and want now to clear it
|
// we treated the error and want now to clear it
|
||||||
static final int CMD_CLEAR_ERROR = BASE_MASTER + 6;
|
static final int CMD_CLEAR_ERROR = BASE_MASTER + 6;
|
||||||
static final int EVENT_IFACE_UPDATE_LINKPROPERTIES = BASE_MASTER + 7;
|
static final int EVENT_IFACE_UPDATE_LINKPROPERTIES = BASE_MASTER + 7;
|
||||||
|
// Events from EntitlementManager to choose upstream again.
|
||||||
|
static final int EVENT_UPSTREAM_PERMISSION_CHANGED = BASE_MASTER + 8;
|
||||||
|
|
||||||
private final State mInitialState;
|
private final State mInitialState;
|
||||||
private final State mTetherModeAliveState;
|
private final State mTetherModeAliveState;
|
||||||
@@ -1504,6 +1448,7 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case EVENT_UPSTREAM_PERMISSION_CHANGED:
|
||||||
case CMD_UPSTREAM_CHANGED:
|
case CMD_UPSTREAM_CHANGED:
|
||||||
updateUpstreamWanted();
|
updateUpstreamWanted();
|
||||||
if (!mUpstreamWanted) break;
|
if (!mUpstreamWanted) break;
|
||||||
@@ -1694,7 +1639,8 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void systemReady() {
|
public void systemReady() {
|
||||||
mUpstreamNetworkMonitor.startTrackDefaultNetwork(mDeps.getDefaultNetworkRequest());
|
mUpstreamNetworkMonitor.startTrackDefaultNetwork(mDeps.getDefaultNetworkRequest(),
|
||||||
|
mEntitlementMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the latest value of the tethering entitlement check. */
|
/** Get the latest value of the tethering entitlement check. */
|
||||||
@@ -1755,6 +1701,11 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
cfg.dump(pw);
|
cfg.dump(pw);
|
||||||
pw.decreaseIndent();
|
pw.decreaseIndent();
|
||||||
|
|
||||||
|
pw.println("Entitlement:");
|
||||||
|
pw.increaseIndent();
|
||||||
|
mEntitlementMgr.dump(pw);
|
||||||
|
pw.decreaseIndent();
|
||||||
|
|
||||||
synchronized (mPublicSync) {
|
synchronized (mPublicSync) {
|
||||||
pw.println("Tether state:");
|
pw.println("Tether state:");
|
||||||
pw.increaseIndent();
|
pw.increaseIndent();
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ package com.android.server.connectivity.tethering;
|
|||||||
|
|
||||||
import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
|
import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
|
||||||
import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
|
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_RUN_PROVISION;
|
||||||
import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
|
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.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
|
import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
|
||||||
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
||||||
import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
|
import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
|
||||||
@@ -28,17 +30,24 @@ import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
|
|||||||
import static com.android.internal.R.string.config_wifi_tether_enable;
|
import static com.android.internal.R.string.config_wifi_tether_enable;
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.app.AlarmManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.PersistableBundle;
|
import android.os.PersistableBundle;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.telephony.CarrierConfigManager;
|
import android.telephony.CarrierConfigManager;
|
||||||
@@ -46,48 +55,78 @@ import android.util.ArraySet;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.StateMachine;
|
import com.android.internal.util.StateMachine;
|
||||||
import com.android.server.connectivity.MockableSystemProperties;
|
import com.android.server.connectivity.MockableSystemProperties;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class encapsulates entitlement/provisioning mechanics
|
* Re-check tethering provisioning for enabled downstream tether types.
|
||||||
* provisioning check only applies to the use of the mobile network as an upstream
|
* Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
|
||||||
*
|
*
|
||||||
|
* All methods of this class must be accessed from the thread of tethering
|
||||||
|
* state machine.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public class EntitlementManager {
|
public class EntitlementManager {
|
||||||
private static final String TAG = EntitlementManager.class.getSimpleName();
|
private static final String TAG = EntitlementManager.class.getSimpleName();
|
||||||
private static final boolean DBG = false;
|
private static final boolean DBG = false;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
|
||||||
|
private static final String ACTION_PROVISIONING_ALARM =
|
||||||
|
"com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM";
|
||||||
|
|
||||||
// {@link ComponentName} of the Service used to run tether provisioning.
|
// {@link ComponentName} of the Service used to run tether provisioning.
|
||||||
private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
|
private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
|
||||||
Resources.getSystem().getString(config_wifi_tether_enable));
|
Resources.getSystem().getString(config_wifi_tether_enable));
|
||||||
protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
|
private static final int MS_PER_HOUR = 60 * 60 * 1000;
|
||||||
|
private static final int EVENT_START_PROVISIONING = 0;
|
||||||
|
private static final int EVENT_STOP_PROVISIONING = 1;
|
||||||
|
private static final int EVENT_UPSTREAM_CHANGED = 2;
|
||||||
|
private static final int EVENT_MAYBE_RUN_PROVISIONING = 3;
|
||||||
|
private static final int EVENT_GET_ENTITLEMENT_VALUE = 4;
|
||||||
|
|
||||||
|
|
||||||
// The ArraySet contains enabled downstream types, ex:
|
// The ArraySet contains enabled downstream types, ex:
|
||||||
// {@link ConnectivityManager.TETHERING_WIFI}
|
// {@link ConnectivityManager.TETHERING_WIFI}
|
||||||
// {@link ConnectivityManager.TETHERING_USB}
|
// {@link ConnectivityManager.TETHERING_USB}
|
||||||
// {@link ConnectivityManager.TETHERING_BLUETOOTH}
|
// {@link ConnectivityManager.TETHERING_BLUETOOTH}
|
||||||
@GuardedBy("mCurrentTethers")
|
|
||||||
private final ArraySet<Integer> mCurrentTethers;
|
private final ArraySet<Integer> mCurrentTethers;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
private final int mPermissionChangeMessageCode;
|
||||||
private final MockableSystemProperties mSystemProperties;
|
private final MockableSystemProperties mSystemProperties;
|
||||||
private final SharedLog mLog;
|
private final SharedLog mLog;
|
||||||
private final Handler mMasterHandler;
|
|
||||||
private final SparseIntArray mEntitlementCacheValue;
|
private final SparseIntArray mEntitlementCacheValue;
|
||||||
@Nullable
|
private final EntitlementHandler mHandler;
|
||||||
private TetheringConfiguration mConfig;
|
private @Nullable TetheringConfiguration mConfig;
|
||||||
|
private final StateMachine mTetherMasterSM;
|
||||||
|
// Key: ConnectivityManager.TETHERING_*(downstream).
|
||||||
|
// Value: ConnectivityManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result).
|
||||||
|
private final SparseIntArray mCellularPermitted;
|
||||||
|
private PendingIntent mProvisioningRecheckAlarm;
|
||||||
|
private boolean mCellularUpstreamPermitted = true;
|
||||||
|
private boolean mUsingCellularAsUpstream = false;
|
||||||
|
private boolean mNeedReRunProvisioningUi = false;
|
||||||
|
|
||||||
public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
|
public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
|
||||||
MockableSystemProperties systemProperties) {
|
int permissionChangeMessageCode, MockableSystemProperties systemProperties) {
|
||||||
|
|
||||||
mContext = ctx;
|
mContext = ctx;
|
||||||
mLog = log.forSubComponent(TAG);
|
mLog = log.forSubComponent(TAG);
|
||||||
mCurrentTethers = new ArraySet<Integer>();
|
mCurrentTethers = new ArraySet<Integer>();
|
||||||
|
mCellularPermitted = new SparseIntArray();
|
||||||
mSystemProperties = systemProperties;
|
mSystemProperties = systemProperties;
|
||||||
mEntitlementCacheValue = new SparseIntArray();
|
mEntitlementCacheValue = new SparseIntArray();
|
||||||
mMasterHandler = tetherMasterSM.getHandler();
|
mTetherMasterSM = tetherMasterSM;
|
||||||
|
mPermissionChangeMessageCode = permissionChangeMessageCode;
|
||||||
|
final Handler masterHandler = tetherMasterSM.getHandler();
|
||||||
|
// Create entitlement's own handler which is associated with TetherMaster thread
|
||||||
|
// let all entitlement processes run in the same thread.
|
||||||
|
mHandler = new EntitlementHandler(masterHandler.getLooper());
|
||||||
|
mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
|
||||||
|
null, mHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,24 +138,118 @@ public class EntitlementManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tell EntitlementManager that a given type of tethering has been enabled
|
* Check if cellular upstream is permitted.
|
||||||
*
|
|
||||||
* @param type Tethering type
|
|
||||||
*/
|
*/
|
||||||
public void startTethering(int type) {
|
public boolean isCellularUpstreamPermitted() {
|
||||||
synchronized (mCurrentTethers) {
|
return mCellularUpstreamPermitted;
|
||||||
mCurrentTethers.add(type);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is called when tethering starts.
|
||||||
|
* Launch provisioning app if upstream is cellular.
|
||||||
|
*
|
||||||
|
* @param downstreamType tethering type from ConnectivityManager.TETHERING_{@code *}
|
||||||
|
* @param showProvisioningUi a boolean indicating whether to show the
|
||||||
|
* provisioning app UI if there is one.
|
||||||
|
*/
|
||||||
|
public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) {
|
||||||
|
mHandler.sendMessage(mHandler.obtainMessage(EVENT_START_PROVISIONING,
|
||||||
|
downstreamType, encodeBool(showProvisioningUi)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleStartProvisioningIfNeeded(int type, boolean showProvisioningUi) {
|
||||||
|
if (!isValidDownstreamType(type)) return;
|
||||||
|
|
||||||
|
if (!mCurrentTethers.contains(type)) mCurrentTethers.add(type);
|
||||||
|
|
||||||
|
if (isTetherProvisioningRequired()) {
|
||||||
|
// If provisioning is required and the result is not available yet,
|
||||||
|
// cellular upstream should not be allowed.
|
||||||
|
if (mCellularPermitted.size() == 0) {
|
||||||
|
mCellularUpstreamPermitted = false;
|
||||||
|
}
|
||||||
|
// If upstream is not cellular, provisioning app would not be launched
|
||||||
|
// till upstream change to cellular.
|
||||||
|
if (mUsingCellularAsUpstream) {
|
||||||
|
if (showProvisioningUi) {
|
||||||
|
runUiTetherProvisioning(type);
|
||||||
|
} else {
|
||||||
|
runSilentTetherProvisioning(type);
|
||||||
|
}
|
||||||
|
mNeedReRunProvisioningUi = false;
|
||||||
|
} else {
|
||||||
|
mNeedReRunProvisioningUi |= showProvisioningUi;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mCellularUpstreamPermitted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tell EntitlementManager that a given type of tethering has been disabled
|
* Tell EntitlementManager that a given type of tethering has been disabled
|
||||||
*
|
*
|
||||||
* @param type Tethering type
|
* @param type tethering type from ConnectivityManager.TETHERING_{@code *}
|
||||||
*/
|
*/
|
||||||
public void stopTethering(int type) {
|
public void stopProvisioningIfNeeded(int type) {
|
||||||
synchronized (mCurrentTethers) {
|
mHandler.sendMessage(mHandler.obtainMessage(EVENT_STOP_PROVISIONING, type, 0));
|
||||||
mCurrentTethers.remove(type);
|
}
|
||||||
|
|
||||||
|
private void handleStopProvisioningIfNeeded(int type) {
|
||||||
|
if (!isValidDownstreamType(type)) return;
|
||||||
|
|
||||||
|
mCurrentTethers.remove(type);
|
||||||
|
// There are lurking bugs where the notion of "provisioning required" or
|
||||||
|
// "tethering supported" may change without without tethering being notified properly.
|
||||||
|
// Remove the mapping all the time no matter provisioning is required or not.
|
||||||
|
removeDownstreamMapping(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify EntitlementManager if upstream is cellular or not.
|
||||||
|
*
|
||||||
|
* @param isCellular whether tethering upstream is cellular.
|
||||||
|
*/
|
||||||
|
public void notifyUpstream(boolean isCellular) {
|
||||||
|
mHandler.sendMessage(mHandler.obtainMessage(
|
||||||
|
EVENT_UPSTREAM_CHANGED, encodeBool(isCellular), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleNotifyUpstream(boolean isCellular) {
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG, "notifyUpstream: " + isCellular
|
||||||
|
+ ", mCellularUpstreamPermitted: " + mCellularUpstreamPermitted
|
||||||
|
+ ", mNeedReRunProvisioningUi: " + mNeedReRunProvisioningUi);
|
||||||
|
}
|
||||||
|
mUsingCellularAsUpstream = isCellular;
|
||||||
|
|
||||||
|
if (mUsingCellularAsUpstream) {
|
||||||
|
handleMaybeRunProvisioning();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Run provisioning if needed */
|
||||||
|
public void maybeRunProvisioning() {
|
||||||
|
mHandler.sendMessage(mHandler.obtainMessage(EVENT_MAYBE_RUN_PROVISIONING));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleMaybeRunProvisioning() {
|
||||||
|
if (mCurrentTethers.size() == 0 || !isTetherProvisioningRequired()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whenever any entitlement value changes, all downstreams will re-evaluate whether they
|
||||||
|
// are allowed. Therefore even if the silent check here ends in a failure and the UI later
|
||||||
|
// yields success, then the downstream that got a failure will re-evaluate as a result of
|
||||||
|
// the change and get the new correct value.
|
||||||
|
for (Integer downstream : mCurrentTethers) {
|
||||||
|
if (mCellularPermitted.indexOfKey(downstream) < 0) {
|
||||||
|
if (mNeedReRunProvisioningUi) {
|
||||||
|
mNeedReRunProvisioningUi = false;
|
||||||
|
runUiTetherProvisioning(downstream);
|
||||||
|
} else {
|
||||||
|
runSilentTetherProvisioning(downstream);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,23 +271,32 @@ public class EntitlementManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Re-check tethering provisioning for enabled downstream tether types.
|
* Re-check tethering provisioning for all enabled tether types.
|
||||||
* Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
|
* Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
|
||||||
|
*
|
||||||
|
* Note: this method is only called from TetherMaster on the handler thread.
|
||||||
|
* If there are new callers from different threads, the logic should move to
|
||||||
|
* masterHandler to avoid race conditions.
|
||||||
*/
|
*/
|
||||||
public void reevaluateSimCardProvisioning() {
|
public void reevaluateSimCardProvisioning() {
|
||||||
synchronized (mEntitlementCacheValue) {
|
if (DBG) Log.d(TAG, "reevaluateSimCardProvisioning");
|
||||||
mEntitlementCacheValue.clear();
|
|
||||||
|
if (!mHandler.getLooper().isCurrentThread()) {
|
||||||
|
// Except for test, this log should not appear in normal flow.
|
||||||
|
mLog.log("reevaluateSimCardProvisioning() don't run in TetherMaster thread");
|
||||||
|
}
|
||||||
|
mEntitlementCacheValue.clear();
|
||||||
|
mCellularPermitted.clear();
|
||||||
|
|
||||||
|
// TODO: refine provisioning check to isTetherProvisioningRequired() ??
|
||||||
|
if (!mConfig.hasMobileHotspotProvisionApp()
|
||||||
|
|| carrierConfigAffirmsEntitlementCheckNotRequired()) {
|
||||||
|
evaluateCellularPermission();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mConfig.hasMobileHotspotProvisionApp()) return;
|
if (mUsingCellularAsUpstream) {
|
||||||
if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
|
handleMaybeRunProvisioning();
|
||||||
|
|
||||||
final ArraySet<Integer> reevaluateType;
|
|
||||||
synchronized (mCurrentTethers) {
|
|
||||||
reevaluateType = new ArraySet<Integer>(mCurrentTethers);
|
|
||||||
}
|
|
||||||
for (Integer type : reevaluateType) {
|
|
||||||
startProvisionIntent(type);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +331,14 @@ public class EntitlementManager {
|
|||||||
return !isEntitlementCheckRequired;
|
return !isEntitlementCheckRequired;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
|
/**
|
||||||
|
* Run no UI tethering provisioning check.
|
||||||
|
* @param type tethering type from ConnectivityManager.TETHERING_{@code *}
|
||||||
|
*/
|
||||||
|
protected void runSilentTetherProvisioning(int type) {
|
||||||
|
if (DBG) Log.d(TAG, "runSilentTetherProvisioning: " + type);
|
||||||
|
ResultReceiver receiver = buildProxyReceiver(type, null);
|
||||||
|
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
|
intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
|
||||||
intent.putExtra(EXTRA_RUN_PROVISION, true);
|
intent.putExtra(EXTRA_RUN_PROVISION, true);
|
||||||
@@ -203,12 +352,20 @@ public class EntitlementManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
|
/**
|
||||||
|
* Run the UI-enabled tethering provisioning check.
|
||||||
|
* @param type tethering type from ConnectivityManager.TETHERING_{@code *}
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
protected void runUiTetherProvisioning(int type) {
|
||||||
|
ResultReceiver receiver = buildProxyReceiver(type, null);
|
||||||
runUiTetherProvisioning(type, receiver);
|
runUiTetherProvisioning(type, receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
protected void runUiTetherProvisioning(int type, ResultReceiver receiver) {
|
protected void runUiTetherProvisioning(int type, ResultReceiver receiver) {
|
||||||
|
if (DBG) Log.d(TAG, "runUiTetherProvisioning: " + type);
|
||||||
|
|
||||||
Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
|
Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
|
||||||
intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
|
intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
|
||||||
intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
|
intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
|
||||||
@@ -221,56 +378,206 @@ public class EntitlementManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used by the SIM card change observation code.
|
// Not needed to check if this don't run on the handler thread because it's private.
|
||||||
// TODO: De-duplicate with above code, where possible.
|
private void scheduleProvisioningRechecks() {
|
||||||
private void startProvisionIntent(int tetherType) {
|
if (mProvisioningRecheckAlarm == null) {
|
||||||
final Intent startProvIntent = new Intent();
|
final int period = mConfig.provisioningCheckPeriod;
|
||||||
startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
|
if (period <= 0) return;
|
||||||
startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
|
|
||||||
startProvIntent.setComponent(TETHER_SERVICE);
|
|
||||||
mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void scheduleProvisioningRechecks(int type) {
|
Intent intent = new Intent(ACTION_PROVISIONING_ALARM);
|
||||||
Intent intent = new Intent();
|
mProvisioningRecheckAlarm = PendingIntent.getBroadcast(mContext, 0, intent, 0);
|
||||||
intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
|
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
|
||||||
intent.putExtra(EXTRA_SET_ALARM, true);
|
Context.ALARM_SERVICE);
|
||||||
intent.setComponent(TETHER_SERVICE);
|
long periodMs = period * MS_PER_HOUR;
|
||||||
final long ident = Binder.clearCallingIdentity();
|
long firstAlarmTime = SystemClock.elapsedRealtime() + periodMs;
|
||||||
try {
|
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, firstAlarmTime, periodMs,
|
||||||
mContext.startServiceAsUser(intent, UserHandle.CURRENT);
|
mProvisioningRecheckAlarm);
|
||||||
} finally {
|
|
||||||
Binder.restoreCallingIdentity(ident);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancelTetherProvisioningRechecks(int type) {
|
private void cancelTetherProvisioningRechecks() {
|
||||||
Intent intent = new Intent();
|
if (mProvisioningRecheckAlarm != null) {
|
||||||
intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
|
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
|
||||||
intent.setComponent(TETHER_SERVICE);
|
Context.ALARM_SERVICE);
|
||||||
final long ident = Binder.clearCallingIdentity();
|
alarmManager.cancel(mProvisioningRecheckAlarm);
|
||||||
try {
|
mProvisioningRecheckAlarm = null;
|
||||||
mContext.startServiceAsUser(intent, UserHandle.CURRENT);
|
}
|
||||||
} finally {
|
}
|
||||||
Binder.restoreCallingIdentity(ident);
|
|
||||||
|
private void evaluateCellularPermission() {
|
||||||
|
final boolean oldPermitted = mCellularUpstreamPermitted;
|
||||||
|
mCellularUpstreamPermitted = (!isTetherProvisioningRequired()
|
||||||
|
|| mCellularPermitted.indexOfValue(TETHER_ERROR_NO_ERROR) > -1);
|
||||||
|
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG, "Cellular permission change from " + oldPermitted
|
||||||
|
+ " to " + mCellularUpstreamPermitted);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCellularUpstreamPermitted != oldPermitted) {
|
||||||
|
mLog.log("Cellular permission change: " + mCellularUpstreamPermitted);
|
||||||
|
mTetherMasterSM.sendMessage(mPermissionChangeMessageCode);
|
||||||
|
}
|
||||||
|
// Only schedule periodic re-check when tether is provisioned
|
||||||
|
// and the result is ok.
|
||||||
|
if (mCellularUpstreamPermitted && mCellularPermitted.size() > 0) {
|
||||||
|
scheduleProvisioningRechecks();
|
||||||
|
} else {
|
||||||
|
cancelTetherProvisioningRechecks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the mapping between provisioning result and tethering type.
|
||||||
|
* Notify UpstreamNetworkMonitor if Cellular permission changes.
|
||||||
|
*
|
||||||
|
* @param type tethering type from ConnectivityManager.TETHERING_{@code *}
|
||||||
|
* @param resultCode Provisioning result
|
||||||
|
*/
|
||||||
|
protected void addDownstreamMapping(int type, int resultCode) {
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG, "addDownstreamMapping: " + type + ", result: " + resultCode
|
||||||
|
+ " ,TetherTypeRequested: " + mCurrentTethers.contains(type));
|
||||||
|
}
|
||||||
|
if (!mCurrentTethers.contains(type)) return;
|
||||||
|
|
||||||
|
mCellularPermitted.put(type, resultCode);
|
||||||
|
evaluateCellularPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the mapping for input tethering type.
|
||||||
|
* @param type tethering type from ConnectivityManager.TETHERING_{@code *}
|
||||||
|
*/
|
||||||
|
protected void removeDownstreamMapping(int type) {
|
||||||
|
if (DBG) Log.d(TAG, "removeDownstreamMapping: " + type);
|
||||||
|
mCellularPermitted.delete(type);
|
||||||
|
evaluateCellularPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (ACTION_PROVISIONING_ALARM.equals(intent.getAction())) {
|
||||||
|
mLog.log("Received provisioning alarm");
|
||||||
|
reevaluateSimCardProvisioning();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private class EntitlementHandler extends Handler {
|
||||||
|
EntitlementHandler(Looper looper) {
|
||||||
|
super(looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
switch (msg.what) {
|
||||||
|
case EVENT_START_PROVISIONING:
|
||||||
|
handleStartProvisioningIfNeeded(msg.arg1, toBool(msg.arg2));
|
||||||
|
break;
|
||||||
|
case EVENT_STOP_PROVISIONING:
|
||||||
|
handleStopProvisioningIfNeeded(msg.arg1);
|
||||||
|
break;
|
||||||
|
case EVENT_UPSTREAM_CHANGED:
|
||||||
|
handleNotifyUpstream(toBool(msg.arg1));
|
||||||
|
break;
|
||||||
|
case EVENT_MAYBE_RUN_PROVISIONING:
|
||||||
|
handleMaybeRunProvisioning();
|
||||||
|
break;
|
||||||
|
case EVENT_GET_ENTITLEMENT_VALUE:
|
||||||
|
handleGetLatestTetheringEntitlementValue(msg.arg1, (ResultReceiver) msg.obj,
|
||||||
|
toBool(msg.arg2));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mLog.log("Unknown event: " + msg.what);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean toBool(int encodedBoolean) {
|
||||||
|
return encodedBoolean != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int encodeBool(boolean b) {
|
||||||
|
return b ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isValidDownstreamType(int type) {
|
||||||
|
switch (type) {
|
||||||
|
case TETHERING_BLUETOOTH:
|
||||||
|
case TETHERING_USB:
|
||||||
|
case TETHERING_WIFI:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump the infromation of EntitlementManager.
|
||||||
|
* @param pw {@link PrintWriter} is used to print formatted
|
||||||
|
*/
|
||||||
|
public void dump(PrintWriter pw) {
|
||||||
|
pw.print("mCellularUpstreamPermitted: ");
|
||||||
|
pw.println(mCellularUpstreamPermitted);
|
||||||
|
for (Integer type : mCurrentTethers) {
|
||||||
|
pw.print("Type: ");
|
||||||
|
pw.print(typeString(type));
|
||||||
|
if (mCellularPermitted.indexOfKey(type) > -1) {
|
||||||
|
pw.print(", Value: ");
|
||||||
|
pw.println(errorString(mCellularPermitted.get(type)));
|
||||||
|
} else {
|
||||||
|
pw.println(", Value: empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String typeString(int type) {
|
||||||
|
switch (type) {
|
||||||
|
case TETHERING_BLUETOOTH: return "TETHERING_BLUETOOTH";
|
||||||
|
case TETHERING_INVALID: return "TETHERING_INVALID";
|
||||||
|
case TETHERING_USB: return "TETHERING_USB";
|
||||||
|
case TETHERING_WIFI: return "TETHERING_WIFI";
|
||||||
|
default:
|
||||||
|
return String.format("TETHERING UNKNOWN TYPE (%d)", type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String errorString(int value) {
|
||||||
|
switch (value) {
|
||||||
|
case TETHER_ERROR_ENTITLEMENT_UNKONWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN";
|
||||||
|
case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR";
|
||||||
|
case TETHER_ERROR_PROVISION_FAILED: return "TETHER_ERROR_PROVISION_FAILED";
|
||||||
|
default:
|
||||||
|
return String.format("UNKNOWN ERROR (%d)", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResultReceiver buildProxyReceiver(int type, final ResultReceiver receiver) {
|
private ResultReceiver buildProxyReceiver(int type, final ResultReceiver receiver) {
|
||||||
ResultReceiver rr = new ResultReceiver(mMasterHandler) {
|
ResultReceiver rr = new ResultReceiver(mHandler) {
|
||||||
@Override
|
@Override
|
||||||
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||||||
int updatedCacheValue = updateEntitlementCacheValue(type, resultCode);
|
int updatedCacheValue = updateEntitlementCacheValue(type, resultCode);
|
||||||
receiver.send(updatedCacheValue, null);
|
addDownstreamMapping(type, updatedCacheValue);
|
||||||
|
if (receiver != null) receiver.send(updatedCacheValue, null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return writeToParcel(rr);
|
return writeToParcel(rr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Instances of ResultReceiver need to be public classes for remote processes to be able
|
||||||
|
// to load them (otherwise, ClassNotFoundException). For private classes, this method
|
||||||
|
// performs a trick : round-trip parceling any instance of ResultReceiver will return a
|
||||||
|
// vanilla instance of ResultReceiver sharing the binder token with the original receiver.
|
||||||
|
// The binder token has a reference to the original instance of the private class and will
|
||||||
|
// still call its methods, and can be sent over. However it cannot be used for anything
|
||||||
|
// else than sending over a Binder call.
|
||||||
|
// While round-trip parceling is not great, there is currently no other way of generating
|
||||||
|
// a vanilla instance of ResultReceiver because all its fields are private.
|
||||||
private ResultReceiver writeToParcel(final ResultReceiver receiver) {
|
private ResultReceiver writeToParcel(final ResultReceiver receiver) {
|
||||||
// This is necessary to avoid unmarshalling issues when sending the receiver
|
|
||||||
// across processes.
|
|
||||||
Parcel parcel = Parcel.obtain();
|
Parcel parcel = Parcel.obtain();
|
||||||
receiver.writeToParcel(parcel, 0);
|
receiver.writeToParcel(parcel, 0);
|
||||||
parcel.setDataPosition(0);
|
parcel.setDataPosition(0);
|
||||||
@@ -286,34 +593,37 @@ public class EntitlementManager {
|
|||||||
* @param resultCode last entitlement value
|
* @param resultCode last entitlement value
|
||||||
* @return the last updated entitlement value
|
* @return the last updated entitlement value
|
||||||
*/
|
*/
|
||||||
public int updateEntitlementCacheValue(int type, int resultCode) {
|
private int updateEntitlementCacheValue(int type, int resultCode) {
|
||||||
if (DBG) {
|
if (DBG) {
|
||||||
Log.d(TAG, "updateEntitlementCacheValue: " + type + ", result: " + resultCode);
|
Log.d(TAG, "updateEntitlementCacheValue: " + type + ", result: " + resultCode);
|
||||||
}
|
}
|
||||||
synchronized (mEntitlementCacheValue) {
|
if (resultCode == TETHER_ERROR_NO_ERROR) {
|
||||||
if (resultCode == TETHER_ERROR_NO_ERROR) {
|
mEntitlementCacheValue.put(type, resultCode);
|
||||||
mEntitlementCacheValue.put(type, resultCode);
|
return resultCode;
|
||||||
return resultCode;
|
} else {
|
||||||
} else {
|
mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISION_FAILED);
|
||||||
mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISION_FAILED);
|
return TETHER_ERROR_PROVISION_FAILED;
|
||||||
return TETHER_ERROR_PROVISION_FAILED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the last value of the tethering entitlement check. */
|
/** Get the last value of the tethering entitlement check. */
|
||||||
public void getLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
|
public void getLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
|
||||||
boolean showEntitlementUi) {
|
boolean showEntitlementUi) {
|
||||||
|
mHandler.sendMessage(mHandler.obtainMessage(EVENT_GET_ENTITLEMENT_VALUE,
|
||||||
|
downstream, encodeBool(showEntitlementUi), receiver));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleGetLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver,
|
||||||
|
boolean showEntitlementUi) {
|
||||||
|
|
||||||
if (!isTetherProvisioningRequired()) {
|
if (!isTetherProvisioningRequired()) {
|
||||||
receiver.send(TETHER_ERROR_NO_ERROR, null);
|
receiver.send(TETHER_ERROR_NO_ERROR, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int cacheValue;
|
final int cacheValue = mEntitlementCacheValue.get(
|
||||||
synchronized (mEntitlementCacheValue) {
|
|
||||||
cacheValue = mEntitlementCacheValue.get(
|
|
||||||
downstream, TETHER_ERROR_ENTITLEMENT_UNKONWN);
|
downstream, TETHER_ERROR_ENTITLEMENT_UNKONWN);
|
||||||
}
|
|
||||||
if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) {
|
if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) {
|
||||||
receiver.send(cacheValue, null);
|
receiver.send(cacheValue, null);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import static com.android.internal.R.array.config_tether_upstream_types;
|
|||||||
import static com.android.internal.R.array.config_tether_usb_regexs;
|
import static com.android.internal.R.array.config_tether_usb_regexs;
|
||||||
import static com.android.internal.R.array.config_tether_wifi_regexs;
|
import static com.android.internal.R.array.config_tether_wifi_regexs;
|
||||||
import static com.android.internal.R.bool.config_tether_upstream_automatic;
|
import static com.android.internal.R.bool.config_tether_upstream_automatic;
|
||||||
|
import static com.android.internal.R.integer.config_mobile_hotspot_provision_check_period;
|
||||||
import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
|
import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
@@ -94,6 +95,7 @@ public class TetheringConfiguration {
|
|||||||
|
|
||||||
public final String[] provisioningApp;
|
public final String[] provisioningApp;
|
||||||
public final String provisioningAppNoUi;
|
public final String provisioningAppNoUi;
|
||||||
|
public final int provisioningCheckPeriod;
|
||||||
|
|
||||||
public final int subId;
|
public final int subId;
|
||||||
|
|
||||||
@@ -121,6 +123,9 @@ public class TetheringConfiguration {
|
|||||||
|
|
||||||
provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app);
|
provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app);
|
||||||
provisioningAppNoUi = getProvisioningAppNoUi(res);
|
provisioningAppNoUi = getProvisioningAppNoUi(res);
|
||||||
|
provisioningCheckPeriod = getResourceInteger(res,
|
||||||
|
config_mobile_hotspot_provision_check_period,
|
||||||
|
0 /* No periodic re-check */);
|
||||||
|
|
||||||
configLog.log(toString());
|
configLog.log(toString());
|
||||||
}
|
}
|
||||||
@@ -311,6 +316,14 @@ public class TetheringConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getResourceInteger(Resources res, int resId, int defaultValue) {
|
||||||
|
try {
|
||||||
|
return res.getInteger(resId);
|
||||||
|
} catch (Resources.NotFoundException e404) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean getEnableLegacyDhcpServer(Context ctx) {
|
private static boolean getEnableLegacyDhcpServer(Context ctx) {
|
||||||
final ContentResolver cr = ctx.getContentResolver();
|
final ContentResolver cr = ctx.getContentResolver();
|
||||||
final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
|
final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ public class TetheringDependencies {
|
|||||||
* Get a reference to the EntitlementManager to be used by tethering.
|
* Get a reference to the EntitlementManager to be used by tethering.
|
||||||
*/
|
*/
|
||||||
public EntitlementManager getEntitlementManager(Context ctx, StateMachine target,
|
public EntitlementManager getEntitlementManager(Context ctx, StateMachine target,
|
||||||
SharedLog log, MockableSystemProperties systemProperties) {
|
SharedLog log, int what, MockableSystemProperties systemProperties) {
|
||||||
return new EntitlementManager(ctx, target, log, systemProperties);
|
return new EntitlementManager(ctx, target, log, what, systemProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,36 +16,32 @@
|
|||||||
|
|
||||||
package com.android.server.connectivity.tethering;
|
package com.android.server.connectivity.tethering;
|
||||||
|
|
||||||
import static android.net.ConnectivityManager.getNetworkTypeName;
|
|
||||||
import static android.net.ConnectivityManager.TYPE_NONE;
|
|
||||||
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
|
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
|
||||||
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
|
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
|
||||||
|
import static android.net.ConnectivityManager.TYPE_NONE;
|
||||||
|
import static android.net.ConnectivityManager.getNetworkTypeName;
|
||||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
|
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
|
||||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
|
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
|
||||||
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
|
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.Process;
|
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.ConnectivityManager.NetworkCallback;
|
import android.net.ConnectivityManager.NetworkCallback;
|
||||||
import android.net.IpPrefix;
|
import android.net.IpPrefix;
|
||||||
import android.net.LinkAddress;
|
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
import android.net.NetworkState;
|
import android.net.NetworkState;
|
||||||
import android.net.util.NetworkConstants;
|
|
||||||
import android.net.util.PrefixUtils;
|
import android.net.util.PrefixUtils;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Process;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.StateMachine;
|
import com.android.internal.util.StateMachine;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -97,10 +93,13 @@ public class UpstreamNetworkMonitor {
|
|||||||
private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
|
private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
|
||||||
private HashSet<IpPrefix> mLocalPrefixes;
|
private HashSet<IpPrefix> mLocalPrefixes;
|
||||||
private ConnectivityManager mCM;
|
private ConnectivityManager mCM;
|
||||||
|
private EntitlementManager mEntitlementMgr;
|
||||||
private NetworkCallback mListenAllCallback;
|
private NetworkCallback mListenAllCallback;
|
||||||
private NetworkCallback mDefaultNetworkCallback;
|
private NetworkCallback mDefaultNetworkCallback;
|
||||||
private NetworkCallback mMobileNetworkCallback;
|
private NetworkCallback mMobileNetworkCallback;
|
||||||
private boolean mDunRequired;
|
private boolean mDunRequired;
|
||||||
|
// Whether the current default upstream is mobile or not.
|
||||||
|
private boolean mIsDefaultCellularUpstream;
|
||||||
// The current system default network (not really used yet).
|
// The current system default network (not really used yet).
|
||||||
private Network mDefaultInternetNetwork;
|
private Network mDefaultInternetNetwork;
|
||||||
// The current upstream network used for tethering.
|
// The current upstream network used for tethering.
|
||||||
@@ -113,6 +112,7 @@ public class UpstreamNetworkMonitor {
|
|||||||
mLog = log.forSubComponent(TAG);
|
mLog = log.forSubComponent(TAG);
|
||||||
mWhat = what;
|
mWhat = what;
|
||||||
mLocalPrefixes = new HashSet<>();
|
mLocalPrefixes = new HashSet<>();
|
||||||
|
mIsDefaultCellularUpstream = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -122,7 +122,15 @@ public class UpstreamNetworkMonitor {
|
|||||||
mCM = cm;
|
mCM = cm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest) {
|
/**
|
||||||
|
* Tracking the system default network. This method should be called when system is ready.
|
||||||
|
*
|
||||||
|
* @param defaultNetworkRequest should be the same as ConnectivityService default request
|
||||||
|
* @param entitle a EntitlementManager object to communicate between EntitlementManager and
|
||||||
|
* UpstreamNetworkMonitor
|
||||||
|
*/
|
||||||
|
public void startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest,
|
||||||
|
EntitlementManager entitle) {
|
||||||
// This is not really a "request", just a way of tracking the system default network.
|
// This is not really a "request", just a way of tracking the system default network.
|
||||||
// It's guaranteed not to actually bring up any networks because it's the same request
|
// It's guaranteed not to actually bring up any networks because it's the same request
|
||||||
// as the ConnectivityService default request, and thus shares fate with it. We can't
|
// as the ConnectivityService default request, and thus shares fate with it. We can't
|
||||||
@@ -133,6 +141,9 @@ public class UpstreamNetworkMonitor {
|
|||||||
mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
|
mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
|
||||||
cm().requestNetwork(trackDefaultRequest, mDefaultNetworkCallback, mHandler);
|
cm().requestNetwork(trackDefaultRequest, mDefaultNetworkCallback, mHandler);
|
||||||
}
|
}
|
||||||
|
if (mEntitlementMgr == null) {
|
||||||
|
mEntitlementMgr = entitle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startObserveAllNetworks() {
|
public void startObserveAllNetworks() {
|
||||||
@@ -168,11 +179,15 @@ public class UpstreamNetworkMonitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void registerMobileNetworkRequest() {
|
public void registerMobileNetworkRequest() {
|
||||||
|
if (!isCellularUpstreamPermitted()) {
|
||||||
|
mLog.i("registerMobileNetworkRequest() is not permitted");
|
||||||
|
releaseMobileNetworkRequest();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (mMobileNetworkCallback != null) {
|
if (mMobileNetworkCallback != null) {
|
||||||
mLog.e("registerMobileNetworkRequest() already registered");
|
mLog.e("registerMobileNetworkRequest() already registered");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following use of the legacy type system cannot be removed until
|
// The following use of the legacy type system cannot be removed until
|
||||||
// after upstream selection no longer finds networks by legacy type.
|
// after upstream selection no longer finds networks by legacy type.
|
||||||
// See also http://b/34364553 .
|
// See also http://b/34364553 .
|
||||||
@@ -206,29 +221,32 @@ public class UpstreamNetworkMonitor {
|
|||||||
// becomes available and useful we (a) file a request to keep it up as
|
// becomes available and useful we (a) file a request to keep it up as
|
||||||
// necessary and (b) change all upstream tracking state accordingly (by
|
// necessary and (b) change all upstream tracking state accordingly (by
|
||||||
// passing LinkProperties up to Tethering).
|
// passing LinkProperties up to Tethering).
|
||||||
//
|
|
||||||
// Next TODO: return NetworkState instead of just the type.
|
|
||||||
public NetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
|
public NetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
|
||||||
final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType(
|
final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType(
|
||||||
mNetworkMap.values(), preferredTypes);
|
mNetworkMap.values(), preferredTypes, isCellularUpstreamPermitted());
|
||||||
|
|
||||||
mLog.log("preferred upstream type: " + getNetworkTypeName(typeStatePair.type));
|
mLog.log("preferred upstream type: " + getNetworkTypeName(typeStatePair.type));
|
||||||
|
|
||||||
switch (typeStatePair.type) {
|
switch (typeStatePair.type) {
|
||||||
case TYPE_MOBILE_DUN:
|
case TYPE_MOBILE_DUN:
|
||||||
case TYPE_MOBILE_HIPRI:
|
case TYPE_MOBILE_HIPRI:
|
||||||
|
// Tethering just selected mobile upstream in spite of the default network being
|
||||||
|
// not mobile. This can happen because of the priority list.
|
||||||
|
// Notify EntitlementManager to check permission for using mobile upstream.
|
||||||
|
if (!mIsDefaultCellularUpstream) {
|
||||||
|
mEntitlementMgr.maybeRunProvisioning();
|
||||||
|
}
|
||||||
// If we're on DUN, put our own grab on it.
|
// If we're on DUN, put our own grab on it.
|
||||||
registerMobileNetworkRequest();
|
registerMobileNetworkRequest();
|
||||||
break;
|
break;
|
||||||
case TYPE_NONE:
|
case TYPE_NONE:
|
||||||
|
// If we found NONE and mobile upstream is permitted we don't want to do this
|
||||||
|
// as we want any previous requests to keep trying to bring up something we can use.
|
||||||
|
if (!isCellularUpstreamPermitted()) releaseMobileNetworkRequest();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* If we've found an active upstream connection that's not DUN/HIPRI
|
// If we've found an active upstream connection that's not DUN/HIPRI
|
||||||
* we should stop any outstanding DUN/HIPRI requests.
|
// we should stop any outstanding DUN/HIPRI requests.
|
||||||
*
|
|
||||||
* If we found NONE we don't want to do this as we want any previous
|
|
||||||
* requests to keep trying to bring up something we can use.
|
|
||||||
*/
|
|
||||||
releaseMobileNetworkRequest();
|
releaseMobileNetworkRequest();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -241,10 +259,12 @@ public class UpstreamNetworkMonitor {
|
|||||||
final NetworkState dfltState = (mDefaultInternetNetwork != null)
|
final NetworkState dfltState = (mDefaultInternetNetwork != null)
|
||||||
? mNetworkMap.get(mDefaultInternetNetwork)
|
? mNetworkMap.get(mDefaultInternetNetwork)
|
||||||
: null;
|
: null;
|
||||||
if (!mDunRequired) return dfltState;
|
|
||||||
|
|
||||||
if (isNetworkUsableAndNotCellular(dfltState)) return dfltState;
|
if (isNetworkUsableAndNotCellular(dfltState)) return dfltState;
|
||||||
|
|
||||||
|
if (!isCellularUpstreamPermitted()) return null;
|
||||||
|
|
||||||
|
if (!mDunRequired) return dfltState;
|
||||||
|
|
||||||
// Find a DUN network. Note that code in Tethering causes a DUN request
|
// Find a DUN network. Note that code in Tethering causes a DUN request
|
||||||
// to be filed, but this might be moved into this class in future.
|
// to be filed, but this might be moved into this class in future.
|
||||||
return findFirstDunNetwork(mNetworkMap.values());
|
return findFirstDunNetwork(mNetworkMap.values());
|
||||||
@@ -258,6 +278,15 @@ public class UpstreamNetworkMonitor {
|
|||||||
return (Set<IpPrefix>) mLocalPrefixes.clone();
|
return (Set<IpPrefix>) mLocalPrefixes.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isCellularUpstreamPermitted() {
|
||||||
|
if (mEntitlementMgr != null) {
|
||||||
|
return mEntitlementMgr.isCellularUpstreamPermitted();
|
||||||
|
} else {
|
||||||
|
// This flow should only happens in testing.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleAvailable(Network network) {
|
private void handleAvailable(Network network) {
|
||||||
if (mNetworkMap.containsKey(network)) return;
|
if (mNetworkMap.containsKey(network)) return;
|
||||||
|
|
||||||
@@ -388,8 +417,14 @@ public class UpstreamNetworkMonitor {
|
|||||||
public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
|
public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
|
||||||
if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
|
if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
|
||||||
mDefaultInternetNetwork = network;
|
mDefaultInternetNetwork = network;
|
||||||
|
final boolean newIsCellular = isCellular(newNc);
|
||||||
|
if (mIsDefaultCellularUpstream != newIsCellular) {
|
||||||
|
mIsDefaultCellularUpstream = newIsCellular;
|
||||||
|
mEntitlementMgr.notifyUpstream(newIsCellular);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNetCap(network, newNc);
|
handleNetCap(network, newNc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,8 +459,11 @@ public class UpstreamNetworkMonitor {
|
|||||||
public void onLost(Network network) {
|
public void onLost(Network network) {
|
||||||
if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
|
if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
|
||||||
mDefaultInternetNetwork = null;
|
mDefaultInternetNetwork = null;
|
||||||
|
mIsDefaultCellularUpstream = false;
|
||||||
|
mEntitlementMgr.notifyUpstream(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLost(network);
|
handleLost(network);
|
||||||
// Any non-LISTEN_ALL callback will necessarily concern a network that will
|
// Any non-LISTEN_ALL callback will necessarily concern a network that will
|
||||||
// also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback.
|
// also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback.
|
||||||
@@ -454,7 +492,8 @@ public class UpstreamNetworkMonitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static TypeStatePair findFirstAvailableUpstreamByType(
|
private static TypeStatePair findFirstAvailableUpstreamByType(
|
||||||
Iterable<NetworkState> netStates, Iterable<Integer> preferredTypes) {
|
Iterable<NetworkState> netStates, Iterable<Integer> preferredTypes,
|
||||||
|
boolean isCellularUpstreamPermitted) {
|
||||||
final TypeStatePair result = new TypeStatePair();
|
final TypeStatePair result = new TypeStatePair();
|
||||||
|
|
||||||
for (int type : preferredTypes) {
|
for (int type : preferredTypes) {
|
||||||
@@ -466,6 +505,10 @@ public class UpstreamNetworkMonitor {
|
|||||||
ConnectivityManager.getNetworkTypeName(type));
|
ConnectivityManager.getNetworkTypeName(type));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!isCellularUpstreamPermitted && isCellular(nc)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
nc.setSingleUid(Process.myUid());
|
nc.setSingleUid(Process.myUid());
|
||||||
|
|
||||||
for (NetworkState value : netStates) {
|
for (NetworkState value : netStates) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.server.connectivity.tethering;
|
package com.android.server.connectivity.tethering;
|
||||||
|
|
||||||
|
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
|
||||||
import static android.net.ConnectivityManager.TETHERING_USB;
|
import static android.net.ConnectivityManager.TETHERING_USB;
|
||||||
import static android.net.ConnectivityManager.TETHERING_WIFI;
|
import static android.net.ConnectivityManager.TETHERING_WIFI;
|
||||||
import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
|
import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
|
||||||
@@ -72,6 +73,7 @@ public final class EntitlementManagerTest {
|
|||||||
|
|
||||||
private static final int EVENT_EM_UPDATE = 1;
|
private static final int EVENT_EM_UPDATE = 1;
|
||||||
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
|
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
|
||||||
|
private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
|
||||||
|
|
||||||
@Mock private CarrierConfigManager mCarrierConfigManager;
|
@Mock private CarrierConfigManager mCarrierConfigManager;
|
||||||
@Mock private Context mContext;
|
@Mock private Context mContext;
|
||||||
@@ -108,10 +110,12 @@ public final class EntitlementManagerTest {
|
|||||||
public class WrappedEntitlementManager extends EntitlementManager {
|
public class WrappedEntitlementManager extends EntitlementManager {
|
||||||
public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN;
|
public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN;
|
||||||
public boolean everRunUiEntitlement = false;
|
public boolean everRunUiEntitlement = false;
|
||||||
|
public int uiProvisionCount = 0;
|
||||||
|
public int silentProvisionCount = 0;
|
||||||
|
|
||||||
public WrappedEntitlementManager(Context ctx, StateMachine target,
|
public WrappedEntitlementManager(Context ctx, StateMachine target,
|
||||||
SharedLog log, MockableSystemProperties systemProperties) {
|
SharedLog log, int what, MockableSystemProperties systemProperties) {
|
||||||
super(ctx, target, log, systemProperties);
|
super(ctx, target, log, what, systemProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -119,6 +123,16 @@ public final class EntitlementManagerTest {
|
|||||||
everRunUiEntitlement = true;
|
everRunUiEntitlement = true;
|
||||||
receiver.send(fakeEntitlementResult, null);
|
receiver.send(fakeEntitlementResult, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void runUiTetherProvisioning(int type) {
|
||||||
|
uiProvisionCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void runSilentTetherProvisioning(int type) {
|
||||||
|
silentProvisionCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -141,7 +155,8 @@ public final class EntitlementManagerTest {
|
|||||||
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
|
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
|
||||||
mMockContext = new MockContext(mContext);
|
mMockContext = new MockContext(mContext);
|
||||||
mSM = new TestStateMachine();
|
mSM = new TestStateMachine();
|
||||||
mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, mSystemProperties);
|
mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE,
|
||||||
|
mSystemProperties);
|
||||||
mEnMgr.updateConfiguration(
|
mEnMgr.updateConfiguration(
|
||||||
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
|
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
|
||||||
}
|
}
|
||||||
@@ -158,7 +173,9 @@ public final class EntitlementManagerTest {
|
|||||||
// Produce some acceptable looking provision app setting if requested.
|
// Produce some acceptable looking provision app setting if requested.
|
||||||
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
|
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
|
||||||
.thenReturn(PROVISIONING_APP_NAME);
|
.thenReturn(PROVISIONING_APP_NAME);
|
||||||
// Don't disable tethering provisioning unless requested.
|
when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
|
||||||
|
.thenReturn(PROVISIONING_NO_UI_APP_NAME);
|
||||||
|
// Don't disable tethering provisioning unless requested.
|
||||||
when(mSystemProperties.getBoolean(eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY),
|
when(mSystemProperties.getBoolean(eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY),
|
||||||
anyBoolean())).thenReturn(false);
|
anyBoolean())).thenReturn(false);
|
||||||
// Act like the CarrierConfigManager is present and ready unless told otherwise.
|
// Act like the CarrierConfigManager is present and ready unless told otherwise.
|
||||||
@@ -238,6 +255,7 @@ public final class EntitlementManagerTest {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
|
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
callbackTimeoutHelper(mCallbacklatch);
|
callbackTimeoutHelper(mCallbacklatch);
|
||||||
assertFalse(mEnMgr.everRunUiEntitlement);
|
assertFalse(mEnMgr.everRunUiEntitlement);
|
||||||
|
|
||||||
@@ -254,6 +272,7 @@ public final class EntitlementManagerTest {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
|
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
|
||||||
|
mLooper.dispatchAll();
|
||||||
callbackTimeoutHelper(mCallbacklatch);
|
callbackTimeoutHelper(mCallbacklatch);
|
||||||
assertFalse(mEnMgr.everRunUiEntitlement);
|
assertFalse(mEnMgr.everRunUiEntitlement);
|
||||||
// 3. No cache value and ui entitlement check is needed.
|
// 3. No cache value and ui entitlement check is needed.
|
||||||
@@ -281,6 +300,7 @@ public final class EntitlementManagerTest {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
|
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
|
||||||
|
mLooper.dispatchAll();
|
||||||
callbackTimeoutHelper(mCallbacklatch);
|
callbackTimeoutHelper(mCallbacklatch);
|
||||||
assertFalse(mEnMgr.everRunUiEntitlement);
|
assertFalse(mEnMgr.everRunUiEntitlement);
|
||||||
// 5. Cache value is TETHER_ERROR_PROVISION_FAILED and ui entitlement check is needed.
|
// 5. Cache value is TETHER_ERROR_PROVISION_FAILED and ui entitlement check is needed.
|
||||||
@@ -308,6 +328,7 @@ public final class EntitlementManagerTest {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
|
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
callbackTimeoutHelper(mCallbacklatch);
|
callbackTimeoutHelper(mCallbacklatch);
|
||||||
assertFalse(mEnMgr.everRunUiEntitlement);
|
assertFalse(mEnMgr.everRunUiEntitlement);
|
||||||
// 7. Test get value for other downstream type.
|
// 7. Test get value for other downstream type.
|
||||||
@@ -320,19 +341,128 @@ public final class EntitlementManagerTest {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
|
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
|
||||||
|
mLooper.dispatchAll();
|
||||||
callbackTimeoutHelper(mCallbacklatch);
|
callbackTimeoutHelper(mCallbacklatch);
|
||||||
assertFalse(mEnMgr.everRunUiEntitlement);
|
assertFalse(mEnMgr.everRunUiEntitlement);
|
||||||
}
|
}
|
||||||
|
|
||||||
void callbackTimeoutHelper(final CountDownLatch latch) throws Exception {
|
void callbackTimeoutHelper(final CountDownLatch latch) throws Exception {
|
||||||
if (!latch.await(1, TimeUnit.SECONDS)) {
|
if (!latch.await(1, TimeUnit.SECONDS)) {
|
||||||
fail("Timout, fail to recieve callback");
|
fail("Timout, fail to receive callback");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void verifyPermissionResult() {
|
||||||
|
setupForRequiredProvisioning();
|
||||||
|
mEnMgr.notifyUpstream(true);
|
||||||
|
mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog,
|
||||||
|
INVALID_SUBSCRIPTION_ID));
|
||||||
|
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
mEnMgr.addDownstreamMapping(TETHERING_WIFI, TETHER_ERROR_PROVISION_FAILED);
|
||||||
|
assertFalse(mEnMgr.isCellularUpstreamPermitted());
|
||||||
|
mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
mEnMgr.addDownstreamMapping(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
|
||||||
|
assertTrue(mEnMgr.isCellularUpstreamPermitted());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void verifyPermissionIfAllNotApproved() {
|
||||||
|
setupForRequiredProvisioning();
|
||||||
|
mEnMgr.notifyUpstream(true);
|
||||||
|
mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog,
|
||||||
|
INVALID_SUBSCRIPTION_ID));
|
||||||
|
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
mEnMgr.addDownstreamMapping(TETHERING_WIFI, TETHER_ERROR_PROVISION_FAILED);
|
||||||
|
assertFalse(mEnMgr.isCellularUpstreamPermitted());
|
||||||
|
mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
mEnMgr.addDownstreamMapping(TETHERING_USB, TETHER_ERROR_PROVISION_FAILED);
|
||||||
|
assertFalse(mEnMgr.isCellularUpstreamPermitted());
|
||||||
|
mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
mEnMgr.addDownstreamMapping(TETHERING_BLUETOOTH, TETHER_ERROR_PROVISION_FAILED);
|
||||||
|
assertFalse(mEnMgr.isCellularUpstreamPermitted());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void verifyPermissionIfAnyApproved() {
|
||||||
|
setupForRequiredProvisioning();
|
||||||
|
mEnMgr.notifyUpstream(true);
|
||||||
|
mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog,
|
||||||
|
INVALID_SUBSCRIPTION_ID));
|
||||||
|
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
mEnMgr.addDownstreamMapping(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
|
||||||
|
assertTrue(mEnMgr.isCellularUpstreamPermitted());
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
mEnMgr.addDownstreamMapping(TETHERING_USB, TETHER_ERROR_PROVISION_FAILED);
|
||||||
|
assertTrue(mEnMgr.isCellularUpstreamPermitted());
|
||||||
|
mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
assertFalse(mEnMgr.isCellularUpstreamPermitted());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRunTetherProvisioning() {
|
||||||
|
setupForRequiredProvisioning();
|
||||||
|
mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog,
|
||||||
|
INVALID_SUBSCRIPTION_ID));
|
||||||
|
// 1. start ui provisioning, upstream is mobile
|
||||||
|
mEnMgr.notifyUpstream(true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
assertTrue(mEnMgr.uiProvisionCount == 1);
|
||||||
|
assertTrue(mEnMgr.silentProvisionCount == 0);
|
||||||
|
mEnMgr.addDownstreamMapping(TETHERING_USB, TETHER_ERROR_PROVISION_FAILED);
|
||||||
|
// 2. start no-ui provisioning
|
||||||
|
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, false);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
assertTrue(mEnMgr.silentProvisionCount == 1);
|
||||||
|
assertTrue(mEnMgr.uiProvisionCount == 1);
|
||||||
|
mEnMgr.addDownstreamMapping(TETHERING_WIFI, TETHER_ERROR_PROVISION_FAILED);
|
||||||
|
// 3. tear down mobile, then start ui provisioning
|
||||||
|
mEnMgr.notifyUpstream(false);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
assertTrue(mEnMgr.uiProvisionCount == 1);
|
||||||
|
assertTrue(mEnMgr.silentProvisionCount == 1);
|
||||||
|
// 4. switch upstream back to mobile
|
||||||
|
mEnMgr.notifyUpstream(true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
assertTrue(mEnMgr.uiProvisionCount == 2);
|
||||||
|
assertTrue(mEnMgr.silentProvisionCount == 1);
|
||||||
|
mEnMgr.addDownstreamMapping(TETHERING_BLUETOOTH, TETHER_ERROR_PROVISION_FAILED);
|
||||||
|
// 5. tear down mobile, then switch SIM
|
||||||
|
mEnMgr.notifyUpstream(false);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
mEnMgr.reevaluateSimCardProvisioning();
|
||||||
|
assertTrue(mEnMgr.uiProvisionCount == 2);
|
||||||
|
assertTrue(mEnMgr.silentProvisionCount == 1);
|
||||||
|
// 6. switch upstream back to mobile again
|
||||||
|
mEnMgr.notifyUpstream(true);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
assertTrue(mEnMgr.uiProvisionCount == 2);
|
||||||
|
assertTrue(mEnMgr.silentProvisionCount == 4);
|
||||||
|
mEnMgr.addDownstreamMapping(TETHERING_USB, TETHER_ERROR_PROVISION_FAILED);
|
||||||
|
mEnMgr.addDownstreamMapping(TETHERING_WIFI, TETHER_ERROR_PROVISION_FAILED);
|
||||||
|
mEnMgr.addDownstreamMapping(TETHERING_BLUETOOTH, TETHER_ERROR_PROVISION_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
public class TestStateMachine extends StateMachine {
|
public class TestStateMachine extends StateMachine {
|
||||||
public final ArrayList<Message> messages = new ArrayList<>();
|
public final ArrayList<Message> messages = new ArrayList<>();
|
||||||
private final State mLoggingState =
|
private final State
|
||||||
new EntitlementManagerTest.TestStateMachine.LoggingState();
|
mLoggingState = new EntitlementManagerTest.TestStateMachine.LoggingState();
|
||||||
|
|
||||||
class LoggingState extends State {
|
class LoggingState extends State {
|
||||||
@Override public void enter() {
|
@Override public void enter() {
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
|
private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
|
||||||
|
|
||||||
@Mock private Context mContext;
|
@Mock private Context mContext;
|
||||||
|
@Mock private EntitlementManager mEntitleMgr;
|
||||||
@Mock private IConnectivityManager mCS;
|
@Mock private IConnectivityManager mCS;
|
||||||
@Mock private SharedLog mLog;
|
@Mock private SharedLog mLog;
|
||||||
|
|
||||||
@@ -103,6 +104,7 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
reset(mCS);
|
reset(mCS);
|
||||||
reset(mLog);
|
reset(mLog);
|
||||||
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
|
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
|
||||||
|
when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
|
||||||
|
|
||||||
mCM = spy(new TestConnectivityManager(mContext, mCS));
|
mCM = spy(new TestConnectivityManager(mContext, mCS));
|
||||||
mSM = new TestStateMachine();
|
mSM = new TestStateMachine();
|
||||||
@@ -138,7 +140,7 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testDefaultNetworkIsTracked() throws Exception {
|
public void testDefaultNetworkIsTracked() throws Exception {
|
||||||
assertTrue(mCM.hasNoCallbacks());
|
assertTrue(mCM.hasNoCallbacks());
|
||||||
mUNM.startTrackDefaultNetwork(mDefaultRequest);
|
mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
|
||||||
|
|
||||||
mUNM.startObserveAllNetworks();
|
mUNM.startObserveAllNetworks();
|
||||||
assertEquals(1, mCM.trackingDefault.size());
|
assertEquals(1, mCM.trackingDefault.size());
|
||||||
@@ -151,7 +153,7 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
public void testListensForAllNetworks() throws Exception {
|
public void testListensForAllNetworks() throws Exception {
|
||||||
assertTrue(mCM.listening.isEmpty());
|
assertTrue(mCM.listening.isEmpty());
|
||||||
|
|
||||||
mUNM.startTrackDefaultNetwork(mDefaultRequest);
|
mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
|
||||||
mUNM.startObserveAllNetworks();
|
mUNM.startObserveAllNetworks();
|
||||||
assertFalse(mCM.listening.isEmpty());
|
assertFalse(mCM.listening.isEmpty());
|
||||||
assertTrue(mCM.isListeningForAll());
|
assertTrue(mCM.isListeningForAll());
|
||||||
@@ -162,7 +164,7 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCallbacksRegistered() {
|
public void testCallbacksRegistered() {
|
||||||
mUNM.startTrackDefaultNetwork(mDefaultRequest);
|
mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
|
||||||
verify(mCM, times(1)).requestNetwork(
|
verify(mCM, times(1)).requestNetwork(
|
||||||
eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
|
eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
|
||||||
mUNM.startObserveAllNetworks();
|
mUNM.startObserveAllNetworks();
|
||||||
@@ -285,7 +287,7 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
final Collection<Integer> preferredTypes = new ArrayList<>();
|
final Collection<Integer> preferredTypes = new ArrayList<>();
|
||||||
preferredTypes.add(TYPE_WIFI);
|
preferredTypes.add(TYPE_WIFI);
|
||||||
|
|
||||||
mUNM.startTrackDefaultNetwork(mDefaultRequest);
|
mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
|
||||||
mUNM.startObserveAllNetworks();
|
mUNM.startObserveAllNetworks();
|
||||||
// There are no networks, so there is nothing to select.
|
// There are no networks, so there is nothing to select.
|
||||||
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
|
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
|
||||||
@@ -319,6 +321,14 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
NetworkRequest netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
|
NetworkRequest netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
|
||||||
assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR));
|
assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR));
|
||||||
assertFalse(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
|
assertFalse(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
|
||||||
|
// mobile is not permitted, we should not use HIPRI.
|
||||||
|
when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
|
||||||
|
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
|
||||||
|
assertEquals(0, mCM.requested.size());
|
||||||
|
// mobile change back to permitted, HIRPI should come back
|
||||||
|
when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
|
||||||
|
assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,
|
||||||
|
mUNM.selectPreferredUpstreamType(preferredTypes));
|
||||||
|
|
||||||
wifiAgent.fakeConnect();
|
wifiAgent.fakeConnect();
|
||||||
// WiFi is up, and we should prefer it over cell.
|
// WiFi is up, and we should prefer it over cell.
|
||||||
@@ -347,11 +357,19 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
|
netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
|
||||||
assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR));
|
assertTrue(netReq.networkCapabilities.hasTransport(TRANSPORT_CELLULAR));
|
||||||
assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
|
assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
|
||||||
|
// mobile is not permitted, we should not use DUN.
|
||||||
|
when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
|
||||||
|
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
|
||||||
|
assertEquals(0, mCM.requested.size());
|
||||||
|
// mobile change back to permitted, DUN should come back
|
||||||
|
when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
|
||||||
|
assertSatisfiesLegacyType(TYPE_MOBILE_DUN,
|
||||||
|
mUNM.selectPreferredUpstreamType(preferredTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetCurrentPreferredUpstream() throws Exception {
|
public void testGetCurrentPreferredUpstream() throws Exception {
|
||||||
mUNM.startTrackDefaultNetwork(mDefaultRequest);
|
mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
|
||||||
mUNM.startObserveAllNetworks();
|
mUNM.startObserveAllNetworks();
|
||||||
mUNM.updateMobileRequiresDun(false);
|
mUNM.updateMobileRequiresDun(false);
|
||||||
|
|
||||||
@@ -361,37 +379,46 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
mCM.makeDefaultNetwork(cellAgent);
|
mCM.makeDefaultNetwork(cellAgent);
|
||||||
assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
||||||
|
|
||||||
// [1] WiFi connects but not validated/promoted to default -> mobile selected.
|
// [1] Mobile connects but not permitted -> null selected
|
||||||
|
when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
|
||||||
|
assertEquals(null, mUNM.getCurrentPreferredUpstream());
|
||||||
|
when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
|
||||||
|
|
||||||
|
// [2] WiFi connects but not validated/promoted to default -> mobile selected.
|
||||||
final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
|
final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
|
||||||
wifiAgent.fakeConnect();
|
wifiAgent.fakeConnect();
|
||||||
assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
||||||
|
|
||||||
// [2] WiFi validates and is promoted to the default network -> WiFi selected.
|
// [3] WiFi validates and is promoted to the default network -> WiFi selected.
|
||||||
mCM.makeDefaultNetwork(wifiAgent);
|
mCM.makeDefaultNetwork(wifiAgent);
|
||||||
assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
||||||
|
|
||||||
// [3] DUN required, no other changes -> WiFi still selected
|
// [4] DUN required, no other changes -> WiFi still selected
|
||||||
mUNM.updateMobileRequiresDun(true);
|
mUNM.updateMobileRequiresDun(true);
|
||||||
assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
||||||
|
|
||||||
// [4] WiFi no longer validated, mobile becomes default, DUN required -> null selected.
|
// [5] WiFi no longer validated, mobile becomes default, DUN required -> null selected.
|
||||||
mCM.makeDefaultNetwork(cellAgent);
|
mCM.makeDefaultNetwork(cellAgent);
|
||||||
assertEquals(null, mUNM.getCurrentPreferredUpstream());
|
assertEquals(null, mUNM.getCurrentPreferredUpstream());
|
||||||
// TODO: make sure that a DUN request has been filed. This is currently
|
// TODO: make sure that a DUN request has been filed. This is currently
|
||||||
// triggered by code over in Tethering, but once that has been moved
|
// triggered by code over in Tethering, but once that has been moved
|
||||||
// into UNM we should test for this here.
|
// into UNM we should test for this here.
|
||||||
|
|
||||||
// [5] DUN network arrives -> DUN selected
|
// [6] DUN network arrives -> DUN selected
|
||||||
final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
|
final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
|
||||||
dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
|
dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
|
||||||
dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
|
dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
|
||||||
dunAgent.fakeConnect();
|
dunAgent.fakeConnect();
|
||||||
assertEquals(dunAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
assertEquals(dunAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
|
||||||
|
|
||||||
|
// [7] Mobile is not permitted -> null selected
|
||||||
|
when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(false);
|
||||||
|
assertEquals(null, mUNM.getCurrentPreferredUpstream());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocalPrefixes() throws Exception {
|
public void testLocalPrefixes() throws Exception {
|
||||||
mUNM.startTrackDefaultNetwork(mDefaultRequest);
|
mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
|
||||||
mUNM.startObserveAllNetworks();
|
mUNM.startObserveAllNetworks();
|
||||||
|
|
||||||
// [0] Test minimum set of local prefixes.
|
// [0] Test minimum set of local prefixes.
|
||||||
@@ -492,6 +519,26 @@ public class UpstreamNetworkMonitorTest {
|
|||||||
assertTrue(local.isEmpty());
|
assertTrue(local.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSelectMobileWhenMobileIsNotDefault() {
|
||||||
|
final Collection<Integer> preferredTypes = new ArrayList<>();
|
||||||
|
// Mobile has higher pirority than wifi.
|
||||||
|
preferredTypes.add(TYPE_MOBILE_HIPRI);
|
||||||
|
preferredTypes.add(TYPE_WIFI);
|
||||||
|
mUNM.startTrackDefaultNetwork(mDefaultRequest, mEntitleMgr);
|
||||||
|
mUNM.startObserveAllNetworks();
|
||||||
|
// Setup wifi and make wifi as default network.
|
||||||
|
final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
|
||||||
|
wifiAgent.fakeConnect();
|
||||||
|
mCM.makeDefaultNetwork(wifiAgent);
|
||||||
|
// Setup mobile network.
|
||||||
|
final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
|
||||||
|
cellAgent.fakeConnect();
|
||||||
|
|
||||||
|
assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,
|
||||||
|
mUNM.selectPreferredUpstreamType(preferredTypes));
|
||||||
|
verify(mEntitleMgr, times(1)).maybeRunProvisioning();
|
||||||
|
}
|
||||||
private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) {
|
private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) {
|
||||||
if (legacyType == TYPE_NONE) {
|
if (legacyType == TYPE_NONE) {
|
||||||
assertTrue(ns == null);
|
assertTrue(ns == null);
|
||||||
|
|||||||
Reference in New Issue
Block a user