Add DO app bypass for start/stopTethering
Allow DO apps to call startTethering and stopTethering for wifi
tethering with a non-null SoftApConfiguration.
Bug: 295979433
Test: atest TetheringTests CtsTetheringTests
Change-Id: Ie5836e9e30afb2df1de0640645d696e185aa2191
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
index f501a50..b92cf69 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -22,6 +22,7 @@
import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
+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_CHANGE_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
@@ -58,6 +59,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.networkstack.apishim.SettingsShimImpl;
import com.android.networkstack.apishim.common.SettingsShim;
+import com.android.networkstack.tethering.util.TetheringPermissionsUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -72,6 +74,7 @@
private TetheringConnector mConnector;
private SettingsShim mSettingsShim;
+ private TetheringPermissionsUtils mTetheringPermissionsUtils;
@Override
public void onCreate() {
@@ -81,6 +84,7 @@
mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this);
mSettingsShim = SettingsShimImpl.newInstance();
+ mTetheringPermissionsUtils = new TetheringPermissionsUtils(deps.getContext());
}
/**
@@ -109,7 +113,11 @@
@Override
public void tether(String iface, String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
+ listener)) {
+ return;
+ }
mTethering.legacyTether(iface, listener);
}
@@ -117,7 +125,11 @@
@Override
public void untether(String iface, String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
+ listener)) {
+ return;
+ }
mTethering.legacyUntether(iface, listener);
}
@@ -125,32 +137,45 @@
@Override
public void setUsbTethering(boolean enable, String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
+ listener)) {
+ return;
+ }
mTethering.setUsbTethering(enable, listener);
}
+ private boolean isRequestAllowedForDeviceOwner(@NonNull TetheringRequest request) {
+ return request.getTetheringType() == TETHERING_WIFI
+ && request.getSoftApConfiguration() != null;
+ }
+
@Override
- public void startTethering(TetheringRequestParcel request, String callerPkg,
+ public void startTethering(TetheringRequestParcel requestParcel, String callerPkg,
String callingAttributionTag, IIntResultListener listener) {
- boolean onlyAllowPrivileged = request.exemptFromEntitlementCheck
- || request.interfaceName != null;
- if (checkAndNotifyCommonError(callerPkg,
- callingAttributionTag,
- onlyAllowPrivileged,
- listener)) {
+ TetheringRequest request = new TetheringRequest(requestParcel);
+ request.setUid(getBinderCallingUid());
+ request.setPackageName(callerPkg);
+ boolean onlyAllowPrivileged = request.isExemptFromEntitlementCheck()
+ || request.getInterfaceName() != null;
+ boolean isDeviceOwnerAllowed = mTethering.isTetheringWithSoftApConfigEnabled()
+ && isRequestAllowedForDeviceOwner(request);
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, onlyAllowPrivileged,
+ isDeviceOwnerAllowed, listener)) {
return;
}
- TetheringRequest external = new TetheringRequest(request);
- external.setUid(getBinderCallingUid());
- external.setPackageName(callerPkg);
- mTethering.startTethering(external, callerPkg, listener);
+ mTethering.startTethering(request, callerPkg, listener);
}
@Override
public void stopTethering(int type, String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
+ listener)) {
+ return;
+ }
try {
mTethering.stopTethering(type);
@@ -164,9 +189,16 @@
IIntResultListener listener) {
if (request == null) return;
if (listener == null) return;
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
request.setUid(getBinderCallingUid());
request.setPackageName(callerPkg);
+ boolean isDeviceOwnerAllowed = mTethering.isTetheringWithSoftApConfigEnabled()
+ && isRequestAllowedForDeviceOwner(request);
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, isDeviceOwnerAllowed, listener)) {
+ return;
+ }
+ // Note: Whether tethering is actually stopped or not will depend on whether the request
+ // matches an active one with the same UID (see RequestTracker#findFuzzyMatchedRequest).
mTethering.stopTetheringRequest(request, listener);
}
@@ -187,7 +219,11 @@
+ " internal-only listener");
}
};
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
+ listener)) {
+ return;
+ }
mTethering.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
}
@@ -223,7 +259,11 @@
@Override
public void stopAllTethering(String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
+ listener)) {
+ return;
+ }
try {
mTethering.stopAllTethering();
@@ -234,8 +274,11 @@
@Override
public void isTetheringSupported(String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
+ boolean isDeviceOwnerAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled();
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, isDeviceOwnerAppAllowed, listener)) {
+ return;
+ }
try {
listener.onResult(TETHER_ERROR_NO_ERROR);
} catch (RemoteException e) { }
@@ -260,23 +303,17 @@
}
private boolean checkAndNotifyCommonError(final String callerPkg,
- final String callingAttributionTag, final IIntResultListener listener) {
- return checkAndNotifyCommonError(callerPkg, callingAttributionTag,
- false /* onlyAllowPrivileged */, listener);
- }
-
- private boolean checkAndNotifyCommonError(final String callerPkg,
final String callingAttributionTag, final boolean onlyAllowPrivileged,
- final IIntResultListener listener) {
+ final boolean isDeviceOwnerAppAllowed, final IIntResultListener listener) {
try {
- if (!checkPackageNameMatchesUid(getBinderCallingUid(), callerPkg)) {
- Log.e(TAG, "Package name " + callerPkg + " does not match UID "
- + getBinderCallingUid());
+ final int uid = getBinderCallingUid();
+ if (!checkPackageNameMatchesUid(uid, callerPkg)) {
+ Log.e(TAG, "Package name " + callerPkg + " does not match UID " + uid);
listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
return true;
}
- if (!hasTetherChangePermission(callerPkg, callingAttributionTag,
- onlyAllowPrivileged)) {
+ if (!hasTetherChangePermission(uid, callerPkg, callingAttributionTag,
+ onlyAllowPrivileged, isDeviceOwnerAppAllowed)) {
listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
return true;
}
@@ -308,21 +345,25 @@
return mService.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
}
- private boolean hasTetherChangePermission(final String callerPkg,
- final String callingAttributionTag, final boolean onlyAllowPrivileged) {
+ private boolean hasTetherChangePermission(final int uid, final String callerPkg,
+ final String callingAttributionTag, final boolean onlyAllowPrivileged,
+ final boolean isDeviceOwnerAppAllowed) {
if (onlyAllowPrivileged && !hasNetworkStackPermission()
&& !hasNetworkSettingsPermission()) return false;
if (hasTetherPrivilegedPermission()) return true;
+ // Allow DO apps to change tethering even if they don't have TETHER_PRIVILEGED.
+ if (isDeviceOwnerAppAllowed && mService.isDeviceOwner(uid, callerPkg)) {
+ return true;
+ }
+
// After TetheringManager moves to public API, prevent third-party apps from being able
// to change tethering with only WRITE_SETTINGS permission.
if (mTethering.isTetheringWithSoftApConfigEnabled()) return false;
if (mTethering.isTetherProvisioningRequired()) return false;
- int uid = getBinderCallingUid();
-
// If callerPkg's uid is not same as getBinderCallingUid(),
// checkAndNoteWriteSettingsOperation will return false and the operation will be
// denied.
@@ -387,6 +428,14 @@
}
/**
+ * Wrapper for {@link TetheringPermissionsUtils#isDeviceOwner(int, String)}, used for mocks.
+ */
+ @VisibleForTesting
+ boolean isDeviceOwner(final int uid, final String callerPkg) {
+ return mTetheringPermissionsUtils.isDeviceOwner(uid, callerPkg);
+ }
+
+ /**
* An injection method for testing.
*/
@VisibleForTesting
diff --git a/Tethering/src/com/android/networkstack/tethering/util/TetheringPermissionsUtils.java b/Tethering/src/com/android/networkstack/tethering/util/TetheringPermissionsUtils.java
new file mode 100644
index 0000000..944e861
--- /dev/null
+++ b/Tethering/src/com/android/networkstack/tethering/util/TetheringPermissionsUtils.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.tethering.util;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Utils class for checking permissions related to Tethering APIs.
+ */
+public class TetheringPermissionsUtils {
+ private static final String TAG = "TetherPermUtils";
+
+ @NonNull private final Context mContext;
+
+ public TetheringPermissionsUtils(@NonNull Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Checks if the package name is a Device Owner.
+ */
+ public boolean isDeviceOwner(final int uid, @NonNull final String packageName) {
+ Context userContext;
+ try {
+ // There is no safe way to invoke this method since tethering package might not be
+ // installed for a certain user on the OEM devices, refer to b/382628161.
+ userContext = mContext.createContextAsUser(UserHandle.getUserHandleForUid(uid),
+ 0 /* flags */);
+ } catch (IllegalStateException e) {
+ // TODO: Add a terrible error metric for this case.
+ Log.e(TAG, "createContextAsUser failed, skipping Device Owner check", e);
+ return false;
+ }
+ DevicePolicyManager devicePolicyManager =
+ retrieveDevicePolicyManagerFromContext(userContext);
+ if (devicePolicyManager == null) return false;
+ return devicePolicyManager.isDeviceOwnerApp(packageName);
+ }
+
+ private DevicePolicyManager retrieveDevicePolicyManagerFromContext(
+ @NonNull final Context context) {
+ DevicePolicyManager devicePolicyManager =
+ context.getSystemService(DevicePolicyManager.class);
+ if (devicePolicyManager == null
+ && context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_DEVICE_ADMIN)) {
+ Log.w(TAG, "Error retrieving DPM service");
+ }
+ return devicePolicyManager;
+ }
+}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
index 7fcc5f1..a8bd221 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
@@ -25,14 +25,18 @@
import android.os.Binder;
import android.os.IBinder;
import android.util.ArrayMap;
+import android.util.ArraySet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.Set;
+
public class MockTetheringService extends TetheringService {
private final Tethering mTethering = mock(Tethering.class);
private final ArrayMap<String, Integer> mMockedPermissions = new ArrayMap<>();
private final ArrayMap<String, Integer> mMockedPackageUids = new ArrayMap<>();
+ private final Set<String> mMockedDeviceOwnerPackages = new ArraySet<>();
private int mMockCallingUid;
@Override
@@ -74,6 +78,11 @@
return mMockCallingUid;
}
+ @Override
+ boolean isDeviceOwner(final int uid, final String callerPkg) {
+ return mMockedDeviceOwnerPackages.contains(callerPkg);
+ }
+
public Tethering getTethering() {
return mTethering;
}
@@ -118,5 +127,19 @@
public void setCallingUid(int uid) {
mMockCallingUid = uid;
}
+
+ /**
+ * Add a mocked carrier privileges package
+ */
+ public void addDeviceOwnerPackage(final String packageName) {
+ mMockedDeviceOwnerPackages.add(packageName);
+ }
+
+ /**
+ * Remove a mocked carrier privileges package
+ */
+ public void removeDeviceOwnerPackage(final String packageName) {
+ mMockedDeviceOwnerPackages.remove(packageName);
+ }
}
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
index b550ada..87163ef 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -21,6 +21,7 @@
import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.Manifest.permission.WRITE_SETTINGS;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_VIRTUAL;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
@@ -36,6 +37,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
@@ -53,6 +55,7 @@
import android.net.TetheringManager;
import android.net.TetheringManager.TetheringRequest;
import android.net.TetheringRequestParcel;
+import android.net.wifi.SoftApConfiguration;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -153,43 +156,58 @@
}
private void runAsNoPermission(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */,
- new String[0]);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, new String[0]);
}
private void runAsTetherPrivileged(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */,
- TETHER_PRIVILEGED);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, TETHER_PRIVILEGED);
}
private void runAsAccessNetworkState(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */,
- ACCESS_NETWORK_STATE);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, ACCESS_NETWORK_STATE);
}
private void runAsWriteSettings(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */,
- WRITE_SETTINGS);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, WRITE_SETTINGS);
}
private void runAsWriteSettingsWhenWriteSettingsAllowed(
final TestTetheringCall test) throws Exception {
- runTetheringCall(test, true /* isTetheringAllowed */, true /* isWriteSettingsAllowed */,
- WRITE_SETTINGS);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ false /* isTetheringWithSoftApConfigEnabled */, WRITE_SETTINGS);
}
private void runAsTetheringDisallowed(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, false /* isTetheringAllowed */, false /* isWriteSettingsAllowed */,
- TETHER_PRIVILEGED);
+ runTetheringCall(test, false /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, TETHER_PRIVILEGED);
}
private void runAsNetworkSettings(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */,
- NETWORK_SETTINGS, TETHER_PRIVILEGED);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, NETWORK_SETTINGS, TETHER_PRIVILEGED);
+ }
+
+ private void runAsDeviceOwner(final TestTetheringCall test) throws Exception {
+ mMockConnector.addDeviceOwnerPackage(TEST_CALLER_PKG);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, new String[0]);
+ mMockConnector.removeDeviceOwnerPackage(TEST_CALLER_PKG);
+ }
+
+ private void runAsDeviceOwnerWhenDeviceOwnerBypassNotEnabled(final TestTetheringCall test)
+ throws Exception {
+ mMockConnector.addDeviceOwnerPackage(TEST_CALLER_PKG);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ false /* isTetheringWithSoftApConfigEnabled */, new String[0]);
+ mMockConnector.removeDeviceOwnerPackage(TEST_CALLER_PKG);
}
private void runTetheringCall(final TestTetheringCall test, boolean isTetheringAllowed,
- boolean isWriteSettingsAllowed, String... permissions) throws Exception {
+ boolean isTetheringWithSoftApConfigEnabled, String... permissions) throws Exception {
// Allow the test to run even if ACCESS_NETWORK_STATE was granted at the APK level
if (!CollectionUtils.contains(permissions, ACCESS_NETWORK_STATE)) {
mMockConnector.setPermission(ACCESS_NETWORK_STATE, PERMISSION_DENIED);
@@ -200,7 +218,7 @@
when(mTethering.isTetheringSupported()).thenReturn(true);
when(mTethering.isTetheringAllowed()).thenReturn(isTetheringAllowed);
when(mTethering.isTetheringWithSoftApConfigEnabled())
- .thenReturn(!isWriteSettingsAllowed);
+ .thenReturn(isTetheringWithSoftApConfigEnabled);
test.runTetheringCall(new TestTetheringResult());
} finally {
mUiAutomation.dropShellPermissionIdentity();
@@ -226,7 +244,7 @@
runAsNoPermission((result) -> {
mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -239,14 +257,14 @@
runAsWriteSettings((result) -> {
mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runTether(result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -274,7 +292,7 @@
runAsNoPermission((result) -> {
mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -287,14 +305,14 @@
runAsWriteSettings((result) -> {
mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runUnTether(result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -328,7 +346,7 @@
runAsNoPermission((result) -> {
mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -341,14 +359,14 @@
runAsWriteSettings((result) -> {
mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runSetUsbTethering(result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -367,6 +385,7 @@
final TetheringRequestParcel request) throws Exception {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
verify(mTethering).startTethering(
@@ -381,14 +400,61 @@
runAsNoPermission((result) -> {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
+ // Not a Wifi request - Fail
+ runAsDeviceOwner((result) -> {
+ final TetheringRequestParcel notWifi = new TetheringRequestParcel();
+ notWifi.tetheringType = TETHERING_USB;
+ mTetheringConnector.startTethering(notWifi, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ // Request has no SoftApConfiguration - Fail
+ runAsDeviceOwner((result) -> {
+ final TetheringRequestParcel noConfig = new TetheringRequestParcel();
+ noConfig.tetheringType = TETHERING_WIFI;
+ mTetheringConnector.startTethering(noConfig, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ // Wifi request with SoftApConfiguration - Succeed
+ runAsDeviceOwner((result) -> {
+ final TetheringRequestParcel withConfig = new TetheringRequestParcel();
+ withConfig.tetheringType = TETHERING_WIFI;
+ withConfig.softApConfig = new SoftApConfiguration.Builder().build();
+ mTetheringConnector.startTethering(withConfig, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering).isTetheringSupported();
+ verify(mTethering).isTetheringAllowed();
+ verify(mTethering).startTethering(any(), any(), any());
+ result.assertResult(-1); // No result
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsDeviceOwnerWhenDeviceOwnerBypassNotEnabled((result) -> {
+ mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verify(mTethering).isTetherProvisioningRequired();
+ verifyNoMoreInteractionsForTethering();
+ });
+
runAsTetherPrivileged((result) -> {
mTetheringConnector.startTethering(request, TEST_WRONG_PACKAGE,
TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering, never()).startTethering(
eq(new TetheringRequest(request)), eq(TEST_WRONG_PACKAGE), eq(result));
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
@@ -403,14 +469,14 @@
runAsWriteSettings((result) -> {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runStartTethering(result, request);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -418,6 +484,7 @@
runAsTetheringDisallowed((result) -> {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_UNSUPPORTED);
@@ -447,6 +514,7 @@
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verifyNoMoreInteractionsForTethering();
});
}
@@ -459,6 +527,7 @@
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verifyNoMoreInteractionsForTethering();
}
@@ -495,7 +564,7 @@
runAsNoPermission((result) -> {
mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -508,14 +577,14 @@
runAsWriteSettings((result) -> {
mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runStopTethering(result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -530,6 +599,87 @@
});
}
+ private void verifyHasPermissionForStopTetheringRequest(TetheringRequest request,
+ final TestTetheringResult result) throws Exception {
+ mTetheringConnector.stopTetheringRequest(request, TEST_CALLER_PKG,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering).stopTetheringRequest(any(), any());
+ verify(mTethering).isTetheringSupported();
+ verify(mTethering).isTetheringAllowed();
+ reset(mTethering);
+ }
+
+ private void verifyDoesNotHavePermissionForStopTetheringRequest(TetheringRequest request,
+ final TestTetheringResult result) throws Exception {
+ mTetheringConnector.stopTetheringRequest(request, TEST_CALLER_PKG,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering, never()).stopTetheringRequest(any(), any());
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ reset(mTethering);
+ }
+
+ private void verifyStopTetheringRequestWithTetheringDisallowed(TetheringRequest request,
+ final TestTetheringResult result) throws Exception {
+ mTetheringConnector.stopTetheringRequest(request, TEST_CALLER_PKG,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering, never()).stopTetheringRequest(any(), any());
+ result.assertResult(TETHER_ERROR_UNSUPPORTED);
+ reset(mTethering);
+ }
+
+ @Test
+ public void testStopTetheringRequest() throws Exception {
+ TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
+
+ runAsNoPermission((result) -> {
+ verifyDoesNotHavePermissionForStopTetheringRequest(request, result);
+ });
+
+ runAsTetherPrivileged((result) -> {
+ verifyHasPermissionForStopTetheringRequest(request, result);
+ });
+
+ runAsWriteSettings((result) -> {
+ verifyDoesNotHavePermissionForStopTetheringRequest(request, result);
+ });
+
+ runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
+ verifyHasPermissionForStopTetheringRequest(request, result);
+ });
+
+ runAsTetheringDisallowed((result) -> {
+ verifyStopTetheringRequestWithTetheringDisallowed(request, result);
+ });
+
+ runAsNetworkSettings((result) -> {
+ verifyHasPermissionForStopTetheringRequest(request, result);
+ });
+
+ // Not wifi -> fail
+ runAsDeviceOwner((result) -> {
+ TetheringRequest notWifi = new TetheringRequest.Builder(TETHERING_USB).build();
+ verifyDoesNotHavePermissionForStopTetheringRequest(notWifi, result);
+ });
+
+ // No config -> fail
+ runAsDeviceOwner((result) -> {
+ TetheringRequest noConfig = new TetheringRequest.Builder(TETHERING_WIFI).build();
+ verifyDoesNotHavePermissionForStopTetheringRequest(noConfig, result);
+ });
+
+ // With config -> success
+ TetheringRequest withConfig = new TetheringRequest.Builder(TETHERING_WIFI)
+ .setSoftApConfiguration(new SoftApConfiguration.Builder().build())
+ .build();
+ runAsDeviceOwner((result) -> {
+ verifyHasPermissionForStopTetheringRequest(withConfig, result);
+ });
+
+ runAsDeviceOwnerWhenDeviceOwnerBypassNotEnabled((result) -> {
+ verifyDoesNotHavePermissionForStopTetheringRequest(withConfig, result);
+ });
+ }
+
private void runRequestLatestTetheringEntitlementResult() throws Exception {
final MyResultReceiver result = new MyResultReceiver(null);
mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
@@ -547,7 +697,7 @@
runAsNoPermission((none) -> {
mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -567,14 +717,14 @@
runAsWriteSettings((none) -> {
mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((none) -> {
runRequestLatestTetheringEntitlementResult();
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -668,7 +818,7 @@
public void testStopAllTethering() throws Exception {
runAsNoPermission((result) -> {
mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -680,14 +830,14 @@
runAsWriteSettings((result) -> {
mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runStopAllTethering(result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -703,6 +853,7 @@
private void runIsTetheringSupported(final TestTetheringResult result) throws Exception {
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_NO_ERROR);
@@ -713,7 +864,7 @@
runAsNoPermission((result) -> {
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -726,14 +877,13 @@
runAsWriteSettings((result) -> {
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runIsTetheringSupported(result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -741,6 +891,7 @@
runAsTetheringDisallowed((result) -> {
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_UNSUPPORTED);
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringPermissionsUtilsTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringPermissionsUtilsTest.java
new file mode 100644
index 0000000..57c3eca
--- /dev/null
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringPermissionsUtilsTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.tethering.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TetheringPermissionsUtilsTest {
+ private static final int TEST_UID = 12345;
+ private static final String TEST_PACKAGE = "test.package";
+
+ @Mock Context mContext;
+ @Mock Context mUserContext;
+ @Mock DevicePolicyManager mDevicePolicyManager;
+ @Mock TelephonyManager mTelephonyManager;
+
+ TetheringPermissionsUtils mTetheringPermissionsUtils;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.createContextAsUser(UserHandle.getUserHandleForUid(TEST_UID), 0))
+ .thenReturn(mUserContext);
+ when(mUserContext.getSystemService(DevicePolicyManager.class))
+ .thenReturn(mDevicePolicyManager);
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+ mTetheringPermissionsUtils = new TetheringPermissionsUtils(mContext);
+ }
+
+ @Test
+ public void testIsDeviceOwner() {
+ when(mDevicePolicyManager.isDeviceOwnerApp(TEST_PACKAGE)).thenReturn(false);
+ assertThat(mTetheringPermissionsUtils.isDeviceOwner(TEST_UID, TEST_PACKAGE)).isFalse();
+
+ when(mDevicePolicyManager.isDeviceOwnerApp(TEST_PACKAGE)).thenReturn(true);
+ assertThat(mTetheringPermissionsUtils.isDeviceOwner(TEST_UID, TEST_PACKAGE)).isTrue();
+ }
+}