Merge changes I41ae1d61,I334860af

* changes:
  Make tethering APIs unsupported synchronously when disallowed
  Revert "Revert "Add onSupportedTetheringType callback""
This commit is contained in:
Mark Chien
2022-07-28 02:22:07 +00:00
committed by Gerrit Code Review
8 changed files with 306 additions and 58 deletions

View File

@@ -36,4 +36,5 @@ oneway interface ITetheringEventCallback
void onTetherStatesChanged(in TetherStatesParcel states); void onTetherStatesChanged(in TetherStatesParcel states);
void onTetherClientsChanged(in List<TetheredClient> clients); void onTetherClientsChanged(in List<TetheredClient> clients);
void onOffloadStatusChanged(int status); void onOffloadStatusChanged(int status);
void onSupportedTetheringTypes(long supportedBitmap);
} }

View File

@@ -26,7 +26,7 @@ import android.net.TetherStatesParcel;
* @hide * @hide
*/ */
parcelable TetheringCallbackStartedParcel { parcelable TetheringCallbackStartedParcel {
boolean tetheringSupported; long supportedTypes;
Network upstreamNetwork; Network upstreamNetwork;
TetheringConfigurationParcel config; TetheringConfigurationParcel config;
TetherStatesParcel states; TetherStatesParcel states;

View File

@@ -183,6 +183,12 @@ public class TetheringManager {
*/ */
public static final int TETHERING_WIGIG = 6; public static final int TETHERING_WIGIG = 6;
/**
* The int value of last tethering type.
* @hide
*/
public static final int MAX_TETHERING_TYPE = TETHERING_WIGIG;
/** @hide */ /** @hide */
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef(value = { @IntDef(value = {
@@ -519,6 +525,9 @@ public class TetheringManager {
} }
} }
@Override
public void onSupportedTetheringTypes(long supportedBitmap) { }
@Override @Override
public void onUpstreamChanged(Network network) { } public void onUpstreamChanged(Network network) { }
@@ -1030,6 +1039,19 @@ public class TetheringManager {
* upstream status. * upstream status.
*/ */
public interface TetheringEventCallback { public interface TetheringEventCallback {
/**
* Called when tethering supported status changed.
*
* <p>This callback will be called immediately after the callback is
* registered, and never be called if there is changes afterward.
*
* <p>Tethering may be disabled via system properties, device configuration, or device
* policy restrictions.
*
* @param supported whether any tethering type is supported.
*/
default void onTetheringSupported(boolean supported) {}
/** /**
* Called when tethering supported status changed. * Called when tethering supported status changed.
* *
@@ -1039,9 +1061,10 @@ public class TetheringManager {
* <p>Tethering may be disabled via system properties, device configuration, or device * <p>Tethering may be disabled via system properties, device configuration, or device
* policy restrictions. * policy restrictions.
* *
* @param supported The new supported status * @param supportedTypes a set of @TetheringType which is supported.
* @hide
*/ */
default void onTetheringSupported(boolean supported) {} default void onSupportedTetheringTypes(@NonNull Set<Integer> supportedTypes) {}
/** /**
* Called when tethering upstream changed. * Called when tethering upstream changed.
@@ -1339,7 +1362,8 @@ public class TetheringManager {
@Override @Override
public void onCallbackStarted(TetheringCallbackStartedParcel parcel) { public void onCallbackStarted(TetheringCallbackStartedParcel parcel) {
executor.execute(() -> { executor.execute(() -> {
callback.onTetheringSupported(parcel.tetheringSupported); callback.onSupportedTetheringTypes(unpackBits(parcel.supportedTypes));
callback.onTetheringSupported(parcel.supportedTypes != 0);
callback.onUpstreamChanged(parcel.upstreamNetwork); callback.onUpstreamChanged(parcel.upstreamNetwork);
sendErrorCallbacks(parcel.states); sendErrorCallbacks(parcel.states);
sendRegexpsChanged(parcel.config); sendRegexpsChanged(parcel.config);
@@ -1358,6 +1382,13 @@ public class TetheringManager {
}); });
} }
@Override
public void onSupportedTetheringTypes(long supportedBitmap) {
executor.execute(() -> {
callback.onSupportedTetheringTypes(unpackBits(supportedBitmap));
});
}
private void sendRegexpsChanged(TetheringConfigurationParcel parcel) { private void sendRegexpsChanged(TetheringConfigurationParcel parcel) {
callback.onTetherableInterfaceRegexpsChanged(new TetheringInterfaceRegexps( callback.onTetherableInterfaceRegexpsChanged(new TetheringInterfaceRegexps(
parcel.tetherableBluetoothRegexs, parcel.tetherableBluetoothRegexs,
@@ -1395,6 +1426,23 @@ public class TetheringManager {
} }
} }
/**
* Unpack bitmap to a set of bit position intergers.
* @hide
*/
public static ArraySet<Integer> unpackBits(long val) {
final ArraySet<Integer> result = new ArraySet<>(Long.bitCount(val));
int bitPos = 0;
while (val != 0) {
if ((val & 1) == 1) result.add(bitPos);
val = val >>> 1;
bitPos++;
}
return result;
}
/** /**
* Remove tethering event callback previously registered with * Remove tethering event callback previously registered with
* {@link #registerTetheringEventCallback}. * {@link #registerTetheringEventCallback}.

View File

@@ -281,6 +281,11 @@ public class Tethering {
private BluetoothPan mBluetoothPan; private BluetoothPan mBluetoothPan;
private PanServiceListener mBluetoothPanListener; private PanServiceListener mBluetoothPanListener;
private ArrayList<Pair<Boolean, IIntResultListener>> mPendingPanRequests; private ArrayList<Pair<Boolean, IIntResultListener>> mPendingPanRequests;
// AIDL doesn't support Set<Integer>. Maintain a int bitmap here. When the bitmap is passed to
// TetheringManager, TetheringManager would convert it to a set of Integer types.
// mSupportedTypeBitmap should always be updated inside tethering internal thread but it may be
// read from binder thread which called TetheringService directly.
private volatile long mSupportedTypeBitmap;
public Tethering(TetheringDependencies deps) { public Tethering(TetheringDependencies deps) {
mLog.mark("Tethering.constructed"); mLog.mark("Tethering.constructed");
@@ -512,6 +517,8 @@ public class Tethering {
mUpstreamNetworkMonitor.setUpstreamConfig(mConfig.chooseUpstreamAutomatically, mUpstreamNetworkMonitor.setUpstreamConfig(mConfig.chooseUpstreamAutomatically,
mConfig.isDunRequired); mConfig.isDunRequired);
reportConfigurationChanged(mConfig.toStableParcelable()); reportConfigurationChanged(mConfig.toStableParcelable());
updateSupportedDownstreams(mConfig);
} }
private void maybeDunSettingChanged() { private void maybeDunSettingChanged() {
@@ -1550,26 +1557,6 @@ public class Tethering {
return mConfig; return mConfig;
} }
boolean hasAnySupportedDownstream() {
if ((mConfig.tetherableUsbRegexs.length != 0)
|| (mConfig.tetherableWifiRegexs.length != 0)
|| (mConfig.tetherableBluetoothRegexs.length != 0)) {
return true;
}
// Before T, isTetheringSupported would return true if wifi, usb and bluetooth tethering are
// disabled (whole tethering settings would be hidden). This means tethering would also not
// support wifi p2p, ethernet tethering and mirrorlink. This is wrong but probably there are
// some devices in the field rely on this to disable tethering entirely.
if (!SdkLevel.isAtLeastT()) return false;
return (mConfig.tetherableWifiP2pRegexs.length != 0)
|| (mConfig.tetherableNcmRegexs.length != 0)
|| isEthernetSupported();
}
// TODO: using EtherentManager new API to check whether ethernet is supported when the API is
// ready to use.
private boolean isEthernetSupported() { private boolean isEthernetSupported() {
return mContext.getSystemService(Context.ETHERNET_SERVICE) != null; return mContext.getSystemService(Context.ETHERNET_SERVICE) != null;
} }
@@ -2359,7 +2346,7 @@ public class Tethering {
mHandler.post(() -> { mHandler.post(() -> {
mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission)); mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission));
final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel(); final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel();
parcel.tetheringSupported = isTetheringSupported(); parcel.supportedTypes = mSupportedTypeBitmap;
parcel.upstreamNetwork = mTetherUpstream; parcel.upstreamNetwork = mTetherUpstream;
parcel.config = mConfig.toStableParcelable(); parcel.config = mConfig.toStableParcelable();
parcel.states = parcel.states =
@@ -2398,6 +2385,22 @@ public class Tethering {
}); });
} }
private void reportTetheringSupportedChange(final long supportedBitmap) {
final int length = mTetheringEventCallbacks.beginBroadcast();
try {
for (int i = 0; i < length; i++) {
try {
mTetheringEventCallbacks.getBroadcastItem(i).onSupportedTetheringTypes(
supportedBitmap);
} catch (RemoteException e) {
// Not really very much to do here.
}
}
} finally {
mTetheringEventCallbacks.finishBroadcast();
}
}
private void reportUpstreamChanged(UpstreamNetworkState ns) { private void reportUpstreamChanged(UpstreamNetworkState ns) {
final int length = mTetheringEventCallbacks.beginBroadcast(); final int length = mTetheringEventCallbacks.beginBroadcast();
final Network network = (ns != null) ? ns.network : null; final Network network = (ns != null) ? ns.network : null;
@@ -2482,18 +2485,56 @@ public class Tethering {
} }
} }
private void updateSupportedDownstreams(final TetheringConfiguration config) {
final long preSupportedBitmap = mSupportedTypeBitmap;
if (!isTetheringAllowed() || mEntitlementMgr.isProvisioningNeededButUnavailable()) {
mSupportedTypeBitmap = 0;
} else {
mSupportedTypeBitmap = makeSupportedDownstreams(config);
}
if (preSupportedBitmap != mSupportedTypeBitmap) {
reportTetheringSupportedChange(mSupportedTypeBitmap);
}
}
private long makeSupportedDownstreams(final TetheringConfiguration config) {
long types = 0;
if (config.tetherableUsbRegexs.length != 0) types |= (1 << TETHERING_USB);
if (config.tetherableWifiRegexs.length != 0) types |= (1 << TETHERING_WIFI);
if (config.tetherableBluetoothRegexs.length != 0) types |= (1 << TETHERING_BLUETOOTH);
// Before T, isTetheringSupported would return true if wifi, usb and bluetooth tethering are
// disabled (whole tethering settings would be hidden). This means tethering would also not
// support wifi p2p, ethernet tethering and mirrorlink. This is wrong but probably there are
// some devices in the field rely on this to disable tethering entirely.
if (!SdkLevel.isAtLeastT() && types == 0) return types;
if (config.tetherableNcmRegexs.length != 0) types |= (1 << TETHERING_NCM);
if (config.tetherableWifiP2pRegexs.length != 0) types |= (1 << TETHERING_WIFI_P2P);
if (isEthernetSupported()) types |= (1 << TETHERING_ETHERNET);
return types;
}
// if ro.tether.denied = true we default to no tethering // if ro.tether.denied = true we default to no tethering
// gservices could set the secure setting to 1 though to enable it on a build where it // gservices could set the secure setting to 1 though to enable it on a build where it
// had previously been turned off. // had previously been turned off.
boolean isTetheringSupported() { boolean isTetheringAllowed() {
final int defaultVal = mDeps.isTetheringDenied() ? 0 : 1; final int defaultVal = mDeps.isTetheringDenied() ? 0 : 1;
final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
final boolean tetherEnabledInSettings = tetherSupported return tetherSupported
&& !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
}
return tetherEnabledInSettings && hasAnySupportedDownstream() boolean isTetheringSupported() {
&& !mEntitlementMgr.isProvisioningNeededButUnavailable(); return mSupportedTypeBitmap > 0;
} }
private void dumpBpf(IndentingPrintWriter pw) { private void dumpBpf(IndentingPrintWriter pw) {

View File

@@ -237,7 +237,7 @@ public class TetheringService extends Service {
listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
return true; return true;
} }
if (!mTethering.isTetheringSupported()) { if (!mTethering.isTetheringSupported() || !mTethering.isTetheringAllowed()) {
listener.onResult(TETHER_ERROR_UNSUPPORTED); listener.onResult(TETHER_ERROR_UNSUPPORTED);
return true; return true;
} }
@@ -255,7 +255,7 @@ public class TetheringService extends Service {
receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null); receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null);
return true; return true;
} }
if (!mTethering.isTetheringSupported()) { if (!mTethering.isTetheringSupported() || !mTethering.isTetheringAllowed()) {
receiver.send(TETHER_ERROR_UNSUPPORTED, null); receiver.send(TETHER_ERROR_UNSUPPORTED, null);
return true; return true;
} }

View File

@@ -192,12 +192,13 @@ public class EthernetTetheringTest {
mUiAutomation.adoptShellPermissionIdentity( mUiAutomation.adoptShellPermissionIdentity(
MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED, ACCESS_NETWORK_STATE, MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED, ACCESS_NETWORK_STATE,
CONNECTIVITY_USE_RESTRICTED_NETWORKS, DUMP); CONNECTIVITY_USE_RESTRICTED_NETWORKS, DUMP);
mRunTests = mTm.isTetheringSupported() && mEm != null;
assumeTrue(mRunTests);
mHandlerThread = new HandlerThread(getClass().getSimpleName()); mHandlerThread = new HandlerThread(getClass().getSimpleName());
mHandlerThread.start(); mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper()); mHandler = new Handler(mHandlerThread.getLooper());
mRunTests = isEthernetTetheringSupported();
assumeTrue(mRunTests);
mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm); mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
} }
@@ -225,7 +226,6 @@ public class EthernetTetheringTest {
mHandler.post(() -> reader.stop()); mHandler.post(() -> reader.stop());
mDownstreamReader = null; mDownstreamReader = null;
} }
mHandlerThread.quitSafely();
mTetheredInterfaceRequester.release(); mTetheredInterfaceRequester.release();
mEm.setIncludeTestInterfaces(false); mEm.setIncludeTestInterfaces(false);
maybeDeleteTestInterface(); maybeDeleteTestInterface();
@@ -236,6 +236,7 @@ public class EthernetTetheringTest {
try { try {
if (mRunTests) cleanUp(); if (mRunTests) cleanUp();
} finally { } finally {
mHandlerThread.quitSafely();
mUiAutomation.dropShellPermissionIdentity(); mUiAutomation.dropShellPermissionIdentity();
} }
} }
@@ -410,6 +411,23 @@ public class EthernetTetheringTest {
// client, which is not possible in this test. // client, which is not possible in this test.
} }
private boolean isEthernetTetheringSupported() throws Exception {
final CompletableFuture<Boolean> future = new CompletableFuture<>();
final TetheringEventCallback callback = new TetheringEventCallback() {
@Override
public void onSupportedTetheringTypes(Set<Integer> supportedTypes) {
future.complete(supportedTypes.contains(TETHERING_ETHERNET));
}
};
try {
mTm.registerTetheringEventCallback(mHandler::post, callback);
return future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
} finally {
mTm.unregisterTetheringEventCallback(callback);
}
}
private static final class MyTetheringEventCallback implements TetheringEventCallback { private static final class MyTetheringEventCallback implements TetheringEventCallback {
private final TetheringManager mTm; private final TetheringManager mTm;
private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1); private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1);

View File

@@ -24,6 +24,7 @@ import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION; import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION; import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@@ -139,23 +140,27 @@ public final class TetheringServiceTest {
} }
private void runAsNoPermission(final TestTetheringCall test) throws Exception { private void runAsNoPermission(final TestTetheringCall test) throws Exception {
runTetheringCall(test, new String[0]); runTetheringCall(test, true /* isTetheringAllowed */, new String[0]);
} }
private void runAsTetherPrivileged(final TestTetheringCall test) throws Exception { private void runAsTetherPrivileged(final TestTetheringCall test) throws Exception {
runTetheringCall(test, TETHER_PRIVILEGED); runTetheringCall(test, true /* isTetheringAllowed */, TETHER_PRIVILEGED);
} }
private void runAsAccessNetworkState(final TestTetheringCall test) throws Exception { private void runAsAccessNetworkState(final TestTetheringCall test) throws Exception {
runTetheringCall(test, ACCESS_NETWORK_STATE); runTetheringCall(test, true /* isTetheringAllowed */, ACCESS_NETWORK_STATE);
} }
private void runAsWriteSettings(final TestTetheringCall test) throws Exception { private void runAsWriteSettings(final TestTetheringCall test) throws Exception {
runTetheringCall(test, WRITE_SETTINGS); runTetheringCall(test, true /* isTetheringAllowed */, WRITE_SETTINGS);
} }
private void runTetheringCall(final TestTetheringCall test, String... permissions) private void runAsTetheringDisallowed(final TestTetheringCall test) throws Exception {
throws Exception { runTetheringCall(test, false /* isTetheringAllowed */, TETHER_PRIVILEGED);
}
private void runTetheringCall(final TestTetheringCall test, boolean isTetheringAllowed,
String... permissions) throws Exception {
// Allow the test to run even if ACCESS_NETWORK_STATE was granted at the APK level // Allow the test to run even if ACCESS_NETWORK_STATE was granted at the APK level
if (!CollectionUtils.contains(permissions, ACCESS_NETWORK_STATE)) { if (!CollectionUtils.contains(permissions, ACCESS_NETWORK_STATE)) {
mMockConnector.setPermission(ACCESS_NETWORK_STATE, PERMISSION_DENIED); mMockConnector.setPermission(ACCESS_NETWORK_STATE, PERMISSION_DENIED);
@@ -164,6 +169,7 @@ public final class TetheringServiceTest {
if (permissions.length > 0) mUiAutomation.adoptShellPermissionIdentity(permissions); if (permissions.length > 0) mUiAutomation.adoptShellPermissionIdentity(permissions);
try { try {
when(mTethering.isTetheringSupported()).thenReturn(true); when(mTethering.isTetheringSupported()).thenReturn(true);
when(mTethering.isTetheringAllowed()).thenReturn(isTetheringAllowed);
test.runTetheringCall(new TestTetheringResult()); test.runTetheringCall(new TestTetheringResult());
} finally { } finally {
mUiAutomation.dropShellPermissionIdentity(); mUiAutomation.dropShellPermissionIdentity();
@@ -180,6 +186,7 @@ public final class TetheringServiceTest {
private void runTether(final TestTetheringResult result) throws Exception { private void runTether(final TestTetheringResult result) throws Exception {
mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
verify(mTethering).tether(TEST_IFACE_NAME, IpServer.STATE_TETHERED, result); verify(mTethering).tether(TEST_IFACE_NAME, IpServer.STATE_TETHERED, result);
} }
@@ -203,12 +210,22 @@ public final class TetheringServiceTest {
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsTetheringDisallowed((result) -> {
mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_UNSUPPORTED);
verifyNoMoreInteractionsForTethering();
});
} }
private void runUnTether(final TestTetheringResult result) throws Exception { private void runUnTether(final TestTetheringResult result) throws Exception {
mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result); result);
verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
verify(mTethering).untether(eq(TEST_IFACE_NAME), eq(result)); verify(mTethering).untether(eq(TEST_IFACE_NAME), eq(result));
} }
@@ -232,6 +249,15 @@ public final class TetheringServiceTest {
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsTetheringDisallowed((result) -> {
mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_UNSUPPORTED);
verifyNoMoreInteractionsForTethering();
});
} }
private void runSetUsbTethering(final TestTetheringResult result) throws Exception { private void runSetUsbTethering(final TestTetheringResult result) throws Exception {
@@ -243,6 +269,7 @@ public final class TetheringServiceTest {
mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result); TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
verify(mTethering).setUsbTethering(eq(true) /* enable */, any(IIntResultListener.class)); verify(mTethering).setUsbTethering(eq(true) /* enable */, any(IIntResultListener.class));
result.assertResult(TETHER_ERROR_NO_ERROR); result.assertResult(TETHER_ERROR_NO_ERROR);
} }
@@ -268,6 +295,14 @@ public final class TetheringServiceTest {
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsTetheringDisallowed((result) -> {
mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_UNSUPPORTED);
verifyNoMoreInteractionsForTethering();
});
} }
private void runStartTethering(final TestTetheringResult result, private void runStartTethering(final TestTetheringResult result,
@@ -275,6 +310,7 @@ public final class TetheringServiceTest {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result); result);
verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
verify(mTethering).startTethering(eq(request), eq(TEST_CALLER_PKG), eq(result)); verify(mTethering).startTethering(eq(request), eq(TEST_CALLER_PKG), eq(result));
} }
@@ -301,6 +337,15 @@ public final class TetheringServiceTest {
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsTetheringDisallowed((result) -> {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_UNSUPPORTED);
verifyNoMoreInteractionsForTethering();
});
} }
private void runStartTetheringAndVerifyNoPermission(final TestTetheringResult result) private void runStartTetheringAndVerifyNoPermission(final TestTetheringResult result)
@@ -337,6 +382,7 @@ public final class TetheringServiceTest {
mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result); TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
verify(mTethering).stopTethering(TETHERING_WIFI); verify(mTethering).stopTethering(TETHERING_WIFI);
result.assertResult(TETHER_ERROR_NO_ERROR); result.assertResult(TETHER_ERROR_NO_ERROR);
} }
@@ -361,6 +407,15 @@ public final class TetheringServiceTest {
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsTetheringDisallowed((result) -> {
mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_UNSUPPORTED);
verifyNoMoreInteractionsForTethering();
});
} }
private void runRequestLatestTetheringEntitlementResult() throws Exception { private void runRequestLatestTetheringEntitlementResult() throws Exception {
@@ -368,6 +423,7 @@ public final class TetheringServiceTest {
mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result, mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG); true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG);
verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI), verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI),
eq(result), eq(true) /* showEntitlementUi */); eq(result), eq(true) /* showEntitlementUi */);
} }
@@ -392,6 +448,16 @@ public final class TetheringServiceTest {
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsTetheringDisallowed((none) -> {
final MyResultReceiver receiver = new MyResultReceiver(null);
mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver,
true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG);
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
receiver.assertResult(TETHER_ERROR_UNSUPPORTED);
verifyNoMoreInteractionsForTethering();
});
} }
private void runRegisterTetheringEventCallback() throws Exception { private void runRegisterTetheringEventCallback() throws Exception {
@@ -419,6 +485,12 @@ public final class TetheringServiceTest {
runRegisterTetheringEventCallback(); runRegisterTetheringEventCallback();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
// should still be able to register callback even tethering is restricted.
runAsTetheringDisallowed((result) -> {
runRegisterTetheringEventCallback();
verifyNoMoreInteractionsForTethering();
});
} }
private void runUnregisterTetheringEventCallback() throws Exception { private void runUnregisterTetheringEventCallback() throws Exception {
@@ -446,11 +518,19 @@ public final class TetheringServiceTest {
runUnregisterTetheringEventCallback(); runUnregisterTetheringEventCallback();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
// should still be able to unregister callback even tethering is restricted.
runAsTetheringDisallowed((result) -> {
runUnregisterTetheringEventCallback();
verifyNoMoreInteractionsForTethering();
});
} }
private void runStopAllTethering(final TestTetheringResult result) throws Exception { private void runStopAllTethering(final TestTetheringResult result) throws Exception {
mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
verify(mTethering).untetherAll(); verify(mTethering).untetherAll();
result.assertResult(TETHER_ERROR_NO_ERROR); result.assertResult(TETHER_ERROR_NO_ERROR);
} }
@@ -474,11 +554,20 @@ public final class TetheringServiceTest {
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsTetheringDisallowed((result) -> {
mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_UNSUPPORTED);
verifyNoMoreInteractionsForTethering();
});
} }
private void runIsTetheringSupported(final TestTetheringResult result) throws Exception { private void runIsTetheringSupported(final TestTetheringResult result) throws Exception {
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_NO_ERROR); result.assertResult(TETHER_ERROR_NO_ERROR);
} }
@@ -502,6 +591,15 @@ public final class TetheringServiceTest {
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsTetheringDisallowed((result) -> {
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_UNSUPPORTED);
verifyNoMoreInteractionsForTethering();
});
} }
private class ConnectorSupplier<T> implements Supplier<T> { private class ConnectorSupplier<T> implements Supplier<T> {

View File

@@ -144,6 +144,7 @@ import android.net.TetheredClient.AddressInfo;
import android.net.TetheringCallbackStartedParcel; import android.net.TetheringCallbackStartedParcel;
import android.net.TetheringConfigurationParcel; import android.net.TetheringConfigurationParcel;
import android.net.TetheringInterface; import android.net.TetheringInterface;
import android.net.TetheringManager;
import android.net.TetheringRequestParcel; import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpLeaseParcelable; import android.net.dhcp.DhcpLeaseParcelable;
import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServerCallbacks;
@@ -175,6 +176,7 @@ import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener; import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.test.mock.MockContentResolver; import android.test.mock.MockContentResolver;
import android.util.ArraySet;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
@@ -217,6 +219,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.Vector; import java.util.Vector;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@@ -1729,6 +1732,7 @@ public class TetheringTest {
private final ArrayList<TetherStatesParcel> mTetherStates = new ArrayList<>(); private final ArrayList<TetherStatesParcel> mTetherStates = new ArrayList<>();
private final ArrayList<Integer> mOffloadStatus = new ArrayList<>(); private final ArrayList<Integer> mOffloadStatus = new ArrayList<>();
private final ArrayList<List<TetheredClient>> mTetheredClients = new ArrayList<>(); private final ArrayList<List<TetheredClient>> mTetheredClients = new ArrayList<>();
private final ArrayList<Long> mSupportedBitmaps = new ArrayList<>();
// This function will remove the recorded callbacks, so it must be called once for // This function will remove the recorded callbacks, so it must be called once for
// each callback. If this is called after multiple callback, the order matters. // each callback. If this is called after multiple callback, the order matters.
@@ -1781,6 +1785,10 @@ public class TetheringTest {
assertTrue(leases.containsAll(result)); assertTrue(leases.containsAll(result));
} }
public void expectSupportedTetheringTypes(Set<Integer> expectedTypes) {
assertEquals(expectedTypes, TetheringManager.unpackBits(mSupportedBitmaps.remove(0)));
}
@Override @Override
public void onUpstreamChanged(Network network) { public void onUpstreamChanged(Network network) {
mActualUpstreams.add(network); mActualUpstreams.add(network);
@@ -1813,11 +1821,17 @@ public class TetheringTest {
mTetherStates.add(parcel.states); mTetherStates.add(parcel.states);
mOffloadStatus.add(parcel.offloadStatus); mOffloadStatus.add(parcel.offloadStatus);
mTetheredClients.add(parcel.tetheredClients); mTetheredClients.add(parcel.tetheredClients);
mSupportedBitmaps.add(parcel.supportedTypes);
} }
@Override @Override
public void onCallbackStopped(int errorCode) { } public void onCallbackStopped(int errorCode) { }
@Override
public void onSupportedTetheringTypes(long supportedBitmap) {
mSupportedBitmaps.add(supportedBitmap);
}
public void assertNoUpstreamChangeCallback() { public void assertNoUpstreamChangeCallback() {
assertTrue(mActualUpstreams.isEmpty()); assertTrue(mActualUpstreams.isEmpty());
} }
@@ -2945,53 +2959,81 @@ public class TetheringTest {
runStopUSBTethering(); runStopUSBTethering();
} }
public static ArraySet<Integer> getAllSupportedTetheringTypes() {
return new ArraySet<>(new Integer[] { TETHERING_USB, TETHERING_NCM, TETHERING_WIFI,
TETHERING_WIFI_P2P, TETHERING_BLUETOOTH, TETHERING_ETHERNET });
}
@Test @Test
public void testTetheringSupported() throws Exception { public void testTetheringSupported() throws Exception {
final ArraySet<Integer> expectedTypes = getAllSupportedTetheringTypes();
// Check tethering is supported after initialization.
setTetheringSupported(true /* supported */); setTetheringSupported(true /* supported */);
updateConfigAndVerifySupported(true /* supported */); TestTetheringEventCallback callback = new TestTetheringEventCallback();
mTethering.registerTetheringEventCallback(callback);
mLooper.dispatchAll();
updateConfigAndVerifySupported(callback, expectedTypes);
// Could disable tethering supported by settings. // Could disable tethering supported by settings.
Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED, 0); Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED, 0);
updateConfigAndVerifySupported(false /* supported */); updateConfigAndVerifySupported(callback, new ArraySet<>());
// Could disable tethering supported by user restriction. // Could disable tethering supported by user restriction.
setTetheringSupported(true /* supported */); setTetheringSupported(true /* supported */);
updateConfigAndVerifySupported(callback, expectedTypes);
when(mUserManager.hasUserRestriction( when(mUserManager.hasUserRestriction(
UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(true); UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(true);
updateConfigAndVerifySupported(false /* supported */); updateConfigAndVerifySupported(callback, new ArraySet<>());
// Tethering is supported if it has any supported downstream. // Tethering is supported if it has any supported downstream.
setTetheringSupported(true /* supported */); setTetheringSupported(true /* supported */);
updateConfigAndVerifySupported(callback, expectedTypes);
// Usb tethering is not supported:
expectedTypes.remove(TETHERING_USB);
when(mResources.getStringArray(R.array.config_tether_usb_regexs)) when(mResources.getStringArray(R.array.config_tether_usb_regexs))
.thenReturn(new String[0]); .thenReturn(new String[0]);
updateConfigAndVerifySupported(true /* supported */); updateConfigAndVerifySupported(callback, expectedTypes);
// Wifi tethering is not supported:
expectedTypes.remove(TETHERING_WIFI);
when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
.thenReturn(new String[0]); .thenReturn(new String[0]);
updateConfigAndVerifySupported(true /* supported */); updateConfigAndVerifySupported(callback, expectedTypes);
// Bluetooth tethering is not supported:
expectedTypes.remove(TETHERING_BLUETOOTH);
when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
.thenReturn(new String[0]);
if (isAtLeastT()) { if (isAtLeastT()) {
when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) updateConfigAndVerifySupported(callback, expectedTypes);
.thenReturn(new String[0]);
updateConfigAndVerifySupported(true /* supported */); // P2p tethering is not supported:
expectedTypes.remove(TETHERING_WIFI_P2P);
when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
.thenReturn(new String[0]); .thenReturn(new String[0]);
updateConfigAndVerifySupported(true /* supported */); updateConfigAndVerifySupported(callback, expectedTypes);
// Ncm tethering is not supported:
expectedTypes.remove(TETHERING_NCM);
when(mResources.getStringArray(R.array.config_tether_ncm_regexs)) when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
.thenReturn(new String[0]); .thenReturn(new String[0]);
updateConfigAndVerifySupported(true /* supported */); updateConfigAndVerifySupported(callback, expectedTypes);
// Ethernet tethering (last supported type) is not supported:
expectedTypes.remove(TETHERING_ETHERNET);
mForceEthernetServiceUnavailable = true; mForceEthernetServiceUnavailable = true;
updateConfigAndVerifySupported(false /* supported */); updateConfigAndVerifySupported(callback, new ArraySet<>());
} else { } else {
when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) // If wifi, usb and bluetooth are all not supported, all the types are not supported.
.thenReturn(new String[0]); expectedTypes.clear();
updateConfigAndVerifySupported(false /* supported */); updateConfigAndVerifySupported(callback, expectedTypes);
} }
} }
private void updateConfigAndVerifySupported(boolean supported) { private void updateConfigAndVerifySupported(final TestTetheringEventCallback callback,
final ArraySet<Integer> expectedTypes) {
sendConfigurationChanged(); sendConfigurationChanged();
assertEquals(supported, mTethering.isTetheringSupported());
assertEquals(expectedTypes.size() > 0, mTethering.isTetheringSupported());
callback.expectSupportedTetheringTypes(expectedTypes);
} }
// TODO: Test that a request for hotspot mode doesn't interfere with an // TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface. // already operating tethering mode interface.