Merge "Add DO app bypass for start/stopTethering" into main

This commit is contained in:
Quang Luong 2025-03-19 06:51:22 -07:00 • committed by Gerrit Code Review
commit 038fd312fe
5 changed files with 440 additions and 76 deletions

View file

@ -22,6 +22,7 @@ import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.TETHER_PRIVILEGED; import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; 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_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;
@ -58,6 +59,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.networkstack.apishim.SettingsShimImpl; import com.android.networkstack.apishim.SettingsShimImpl;
import com.android.networkstack.apishim.common.SettingsShim; import com.android.networkstack.apishim.common.SettingsShim;
import com.android.networkstack.tethering.util.TetheringPermissionsUtils;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
@ -72,6 +74,7 @@ public class TetheringService extends Service {
private TetheringConnector mConnector; private TetheringConnector mConnector;
private SettingsShim mSettingsShim; private SettingsShim mSettingsShim;
private TetheringPermissionsUtils mTetheringPermissionsUtils;
@Override @Override
public void onCreate() { public void onCreate() {
@ -81,6 +84,7 @@ public class TetheringService extends Service {
mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this); mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this);
mSettingsShim = SettingsShimImpl.newInstance(); mSettingsShim = SettingsShimImpl.newInstance();
mTetheringPermissionsUtils = new TetheringPermissionsUtils(deps.getContext());
} }
/** /**
@ -109,7 +113,11 @@ public class TetheringService extends Service {
@Override @Override
public void tether(String iface, String callerPkg, String callingAttributionTag, public void tether(String iface, String callerPkg, String callingAttributionTag,
IIntResultListener listener) { IIntResultListener listener) {
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
listener)) {
return;
}
mTethering.legacyTether(iface, listener); mTethering.legacyTether(iface, listener);
} }
@ -117,7 +125,11 @@ public class TetheringService extends Service {
@Override @Override
public void untether(String iface, String callerPkg, String callingAttributionTag, public void untether(String iface, String callerPkg, String callingAttributionTag,
IIntResultListener listener) { IIntResultListener listener) {
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
listener)) {
return;
}
mTethering.legacyUntether(iface, listener); mTethering.legacyUntether(iface, listener);
} }
@ -125,32 +137,45 @@ public class TetheringService extends Service {
@Override @Override
public void setUsbTethering(boolean enable, String callerPkg, String callingAttributionTag, public void setUsbTethering(boolean enable, String callerPkg, String callingAttributionTag,
IIntResultListener listener) { IIntResultListener listener) {
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
listener)) {
return;
}
mTethering.setUsbTethering(enable, listener); mTethering.setUsbTethering(enable, listener);
} }
private boolean isRequestAllowedForDeviceOwner(@NonNull TetheringRequest request) {
return request.getTetheringType() == TETHERING_WIFI
&& request.getSoftApConfiguration() != null;
}
@Override @Override
public void startTethering(TetheringRequestParcel request, String callerPkg, public void startTethering(TetheringRequestParcel requestParcel, String callerPkg,
String callingAttributionTag, IIntResultListener listener) { String callingAttributionTag, IIntResultListener listener) {
boolean onlyAllowPrivileged = request.exemptFromEntitlementCheck TetheringRequest request = new TetheringRequest(requestParcel);
|| request.interfaceName != null; request.setUid(getBinderCallingUid());
if (checkAndNotifyCommonError(callerPkg, request.setPackageName(callerPkg);
callingAttributionTag, boolean onlyAllowPrivileged = request.isExemptFromEntitlementCheck()
onlyAllowPrivileged, || request.getInterfaceName() != null;
listener)) { boolean isDeviceOwnerAllowed = mTethering.isTetheringWithSoftApConfigEnabled()
&& isRequestAllowedForDeviceOwner(request);
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, onlyAllowPrivileged,
isDeviceOwnerAllowed, listener)) {
return; return;
} }
TetheringRequest external = new TetheringRequest(request); mTethering.startTethering(request, callerPkg, listener);
external.setUid(getBinderCallingUid());
external.setPackageName(callerPkg);
mTethering.startTethering(external, callerPkg, listener);
} }
@Override @Override
public void stopTethering(int type, String callerPkg, String callingAttributionTag, public void stopTethering(int type, String callerPkg, String callingAttributionTag,
IIntResultListener listener) { IIntResultListener listener) {
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
listener)) {
return;
}
try { try {
mTethering.stopTethering(type); mTethering.stopTethering(type);
@ -164,9 +189,16 @@ public class TetheringService extends Service {
IIntResultListener listener) { IIntResultListener listener) {
if (request == null) return; if (request == null) return;
if (listener == null) return; if (listener == null) return;
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
request.setUid(getBinderCallingUid()); request.setUid(getBinderCallingUid());
request.setPackageName(callerPkg); 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); mTethering.stopTetheringRequest(request, listener);
} }
@ -187,7 +219,11 @@ public class TetheringService extends Service {
+ " internal-only listener"); + " internal-only listener");
} }
}; };
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
listener)) {
return;
}
mTethering.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi); mTethering.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
} }
@ -223,7 +259,11 @@ public class TetheringService extends Service {
@Override @Override
public void stopAllTethering(String callerPkg, String callingAttributionTag, public void stopAllTethering(String callerPkg, String callingAttributionTag,
IIntResultListener listener) { IIntResultListener listener) {
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
listener)) {
return;
}
try { try {
mTethering.stopAllTethering(); mTethering.stopAllTethering();
@ -234,8 +274,11 @@ public class TetheringService extends Service {
@Override @Override
public void isTetheringSupported(String callerPkg, String callingAttributionTag, public void isTetheringSupported(String callerPkg, String callingAttributionTag,
IIntResultListener listener) { IIntResultListener listener) {
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; boolean isDeviceOwnerAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled();
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
false /* onlyAllowPrivileged */, isDeviceOwnerAppAllowed, listener)) {
return;
}
try { try {
listener.onResult(TETHER_ERROR_NO_ERROR); listener.onResult(TETHER_ERROR_NO_ERROR);
} catch (RemoteException e) { } } catch (RemoteException e) { }
@ -259,24 +302,18 @@ public class TetheringService extends Service {
mTethering.dump(fd, writer, args); mTethering.dump(fd, writer, args);
} }
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, private boolean checkAndNotifyCommonError(final String callerPkg,
final String callingAttributionTag, final boolean onlyAllowPrivileged, final String callingAttributionTag, final boolean onlyAllowPrivileged,
final IIntResultListener listener) { final boolean isDeviceOwnerAppAllowed, final IIntResultListener listener) {
try { try {
if (!checkPackageNameMatchesUid(getBinderCallingUid(), callerPkg)) { final int uid = getBinderCallingUid();
Log.e(TAG, "Package name " + callerPkg + " does not match UID " if (!checkPackageNameMatchesUid(uid, callerPkg)) {
+ getBinderCallingUid()); Log.e(TAG, "Package name " + callerPkg + " does not match UID " + uid);
listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
return true; return true;
} }
if (!hasTetherChangePermission(callerPkg, callingAttributionTag, if (!hasTetherChangePermission(uid, callerPkg, callingAttributionTag,
onlyAllowPrivileged)) { onlyAllowPrivileged, isDeviceOwnerAppAllowed)) {
listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
return true; return true;
} }
@ -308,21 +345,25 @@ public class TetheringService extends Service {
return mService.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED; return mService.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
} }
private boolean hasTetherChangePermission(final String callerPkg, private boolean hasTetherChangePermission(final int uid, final String callerPkg,
final String callingAttributionTag, final boolean onlyAllowPrivileged) { final String callingAttributionTag, final boolean onlyAllowPrivileged,
final boolean isDeviceOwnerAppAllowed) {
if (onlyAllowPrivileged && !hasNetworkStackPermission() if (onlyAllowPrivileged && !hasNetworkStackPermission()
&& !hasNetworkSettingsPermission()) return false; && !hasNetworkSettingsPermission()) return false;
if (hasTetherPrivilegedPermission()) return true; 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 // After TetheringManager moves to public API, prevent third-party apps from being able
// to change tethering with only WRITE_SETTINGS permission. // to change tethering with only WRITE_SETTINGS permission.
if (mTethering.isTetheringWithSoftApConfigEnabled()) return false; if (mTethering.isTetheringWithSoftApConfigEnabled()) return false;
if (mTethering.isTetherProvisioningRequired()) return false; if (mTethering.isTetherProvisioningRequired()) return false;
int uid = getBinderCallingUid();
// If callerPkg's uid is not same as getBinderCallingUid(), // If callerPkg's uid is not same as getBinderCallingUid(),
// checkAndNoteWriteSettingsOperation will return false and the operation will be // checkAndNoteWriteSettingsOperation will return false and the operation will be
// denied. // denied.
@ -386,6 +427,14 @@ public class TetheringService extends Service {
return Binder.getCallingUid(); return Binder.getCallingUid();
} }
/**
* 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. * An injection method for testing.
*/ */

View file

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

View file

@ -25,14 +25,18 @@ import android.content.Intent;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.util.Set;
public class MockTetheringService extends TetheringService { public class MockTetheringService extends TetheringService {
private final Tethering mTethering = mock(Tethering.class); private final Tethering mTethering = mock(Tethering.class);
private final ArrayMap<String, Integer> mMockedPermissions = new ArrayMap<>(); private final ArrayMap<String, Integer> mMockedPermissions = new ArrayMap<>();
private final ArrayMap<String, Integer> mMockedPackageUids = new ArrayMap<>(); private final ArrayMap<String, Integer> mMockedPackageUids = new ArrayMap<>();
private final Set<String> mMockedDeviceOwnerPackages = new ArraySet<>();
private int mMockCallingUid; private int mMockCallingUid;
@Override @Override
@ -74,6 +78,11 @@ public class MockTetheringService extends TetheringService {
return mMockCallingUid; return mMockCallingUid;
} }
@Override
boolean isDeviceOwner(final int uid, final String callerPkg) {
return mMockedDeviceOwnerPackages.contains(callerPkg);
}
public Tethering getTethering() { public Tethering getTethering() {
return mTethering; return mTethering;
} }
@ -118,5 +127,19 @@ public class MockTetheringService extends TetheringService {
public void setCallingUid(int uid) { public void setCallingUid(int uid) {
mMockCallingUid = 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);
}
} }
} }

View file

@ -21,6 +21,7 @@ import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.TETHER_PRIVILEGED; import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.Manifest.permission.WRITE_SETTINGS; import static android.Manifest.permission.WRITE_SETTINGS;
import static android.content.pm.PackageManager.PERMISSION_DENIED; 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_VIRTUAL;
import static android.net.TetheringManager.TETHERING_WIFI; 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;
@ -36,6 +37,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
@ -53,6 +55,7 @@ import android.net.ITetheringEventCallback;
import android.net.TetheringManager; import android.net.TetheringManager;
import android.net.TetheringManager.TetheringRequest; import android.net.TetheringManager.TetheringRequest;
import android.net.TetheringRequestParcel; import android.net.TetheringRequestParcel;
import android.net.wifi.SoftApConfiguration;
import android.os.Bundle; import android.os.Bundle;
import android.os.ConditionVariable; import android.os.ConditionVariable;
import android.os.Handler; import android.os.Handler;
@ -153,43 +156,58 @@ public final class TetheringServiceTest {
} }
private void runAsNoPermission(final TestTetheringCall test) throws Exception { private void runAsNoPermission(final TestTetheringCall test) throws Exception {
runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */, runTetheringCall(test, true /* isTetheringAllowed */,
new String[0]); true /* isTetheringWithSoftApConfigEnabled */, new String[0]);
} }
private void runAsTetherPrivileged(final TestTetheringCall test) throws Exception { private void runAsTetherPrivileged(final TestTetheringCall test) throws Exception {
runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */, runTetheringCall(test, true /* isTetheringAllowed */,
TETHER_PRIVILEGED); true /* isTetheringWithSoftApConfigEnabled */, TETHER_PRIVILEGED);
} }
private void runAsAccessNetworkState(final TestTetheringCall test) throws Exception { private void runAsAccessNetworkState(final TestTetheringCall test) throws Exception {
runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */, runTetheringCall(test, true /* isTetheringAllowed */,
ACCESS_NETWORK_STATE); true /* isTetheringWithSoftApConfigEnabled */, ACCESS_NETWORK_STATE);
} }
private void runAsWriteSettings(final TestTetheringCall test) throws Exception { private void runAsWriteSettings(final TestTetheringCall test) throws Exception {
runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */, runTetheringCall(test, true /* isTetheringAllowed */,
WRITE_SETTINGS); true /* isTetheringWithSoftApConfigEnabled */, WRITE_SETTINGS);
} }
private void runAsWriteSettingsWhenWriteSettingsAllowed( private void runAsWriteSettingsWhenWriteSettingsAllowed(
final TestTetheringCall test) throws Exception { final TestTetheringCall test) throws Exception {
runTetheringCall(test, true /* isTetheringAllowed */, true /* isWriteSettingsAllowed */, runTetheringCall(test, true /* isTetheringAllowed */,
WRITE_SETTINGS); false /* isTetheringWithSoftApConfigEnabled */, WRITE_SETTINGS);
} }
private void runAsTetheringDisallowed(final TestTetheringCall test) throws Exception { private void runAsTetheringDisallowed(final TestTetheringCall test) throws Exception {
runTetheringCall(test, false /* isTetheringAllowed */, false /* isWriteSettingsAllowed */, runTetheringCall(test, false /* isTetheringAllowed */,
TETHER_PRIVILEGED); true /* isTetheringWithSoftApConfigEnabled */, TETHER_PRIVILEGED);
} }
private void runAsNetworkSettings(final TestTetheringCall test) throws Exception { private void runAsNetworkSettings(final TestTetheringCall test) throws Exception {
runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */, runTetheringCall(test, true /* isTetheringAllowed */,
NETWORK_SETTINGS, TETHER_PRIVILEGED); 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, 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 // 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);
@ -200,7 +218,7 @@ public final class TetheringServiceTest {
when(mTethering.isTetheringSupported()).thenReturn(true); when(mTethering.isTetheringSupported()).thenReturn(true);
when(mTethering.isTetheringAllowed()).thenReturn(isTetheringAllowed); when(mTethering.isTetheringAllowed()).thenReturn(isTetheringAllowed);
when(mTethering.isTetheringWithSoftApConfigEnabled()) when(mTethering.isTetheringWithSoftApConfigEnabled())
.thenReturn(!isWriteSettingsAllowed); .thenReturn(isTetheringWithSoftApConfigEnabled);
test.runTetheringCall(new TestTetheringResult()); test.runTetheringCall(new TestTetheringResult());
} finally { } finally {
mUiAutomation.dropShellPermissionIdentity(); mUiAutomation.dropShellPermissionIdentity();
@ -226,7 +244,7 @@ public final class TetheringServiceTest {
runAsNoPermission((result) -> { runAsNoPermission((result) -> {
mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result); result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -239,14 +257,14 @@ public final class TetheringServiceTest {
runAsWriteSettings((result) -> { runAsWriteSettings((result) -> {
mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result); result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> { runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runTether(result); runTether(result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -274,7 +292,7 @@ public final class TetheringServiceTest {
runAsNoPermission((result) -> { runAsNoPermission((result) -> {
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).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -287,14 +305,14 @@ public final class TetheringServiceTest {
runAsWriteSettings((result) -> { runAsWriteSettings((result) -> {
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).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> { runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runUnTether(result); runUnTether(result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -328,7 +346,7 @@ public final class TetheringServiceTest {
runAsNoPermission((result) -> { runAsNoPermission((result) -> {
mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result); TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -341,14 +359,14 @@ public final class TetheringServiceTest {
runAsWriteSettings((result) -> { runAsWriteSettings((result) -> {
mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result); TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> { runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runSetUsbTethering(result); runSetUsbTethering(result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -367,6 +385,7 @@ public final class TetheringServiceTest {
final TetheringRequestParcel request) throws Exception { final TetheringRequestParcel request) throws Exception {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result); result);
verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed(); verify(mTethering).isTetheringAllowed();
verify(mTethering).startTethering( verify(mTethering).startTethering(
@ -381,14 +400,61 @@ public final class TetheringServiceTest {
runAsNoPermission((result) -> { runAsNoPermission((result) -> {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result); result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); 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) -> { runAsTetherPrivileged((result) -> {
mTetheringConnector.startTethering(request, TEST_WRONG_PACKAGE, mTetheringConnector.startTethering(request, TEST_WRONG_PACKAGE,
TEST_ATTRIBUTION_TAG, result); TEST_ATTRIBUTION_TAG, result);
verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering, never()).startTethering( verify(mTethering, never()).startTethering(
eq(new TetheringRequest(request)), eq(TEST_WRONG_PACKAGE), eq(result)); eq(new TetheringRequest(request)), eq(TEST_WRONG_PACKAGE), eq(result));
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
@ -403,14 +469,14 @@ public final class TetheringServiceTest {
runAsWriteSettings((result) -> { runAsWriteSettings((result) -> {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result); result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> { runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runStartTethering(result, request); runStartTethering(result, request);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -418,6 +484,7 @@ public final class TetheringServiceTest {
runAsTetheringDisallowed((result) -> { runAsTetheringDisallowed((result) -> {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result); result);
verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed(); verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_UNSUPPORTED); result.assertResult(TETHER_ERROR_UNSUPPORTED);
@ -447,6 +514,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);
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
} }
@ -459,6 +527,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);
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
} }
@ -495,7 +564,7 @@ public final class TetheringServiceTest {
runAsNoPermission((result) -> { runAsNoPermission((result) -> {
mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result); TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -508,14 +577,14 @@ public final class TetheringServiceTest {
runAsWriteSettings((result) -> { runAsWriteSettings((result) -> {
mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result); TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> { runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runStopTethering(result); runStopTethering(result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -530,6 +599,87 @@ public final class TetheringServiceTest {
}); });
} }
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 { private void runRequestLatestTetheringEntitlementResult() throws Exception {
final MyResultReceiver result = new MyResultReceiver(null); final MyResultReceiver result = new MyResultReceiver(null);
mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result, mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
@ -547,7 +697,7 @@ public final class TetheringServiceTest {
runAsNoPermission((none) -> { runAsNoPermission((none) -> {
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).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -567,14 +717,14 @@ public final class TetheringServiceTest {
runAsWriteSettings((none) -> { runAsWriteSettings((none) -> {
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).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsWriteSettingsWhenWriteSettingsAllowed((none) -> { runAsWriteSettingsWhenWriteSettingsAllowed((none) -> {
runRequestLatestTetheringEntitlementResult(); runRequestLatestTetheringEntitlementResult();
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -668,7 +818,7 @@ public final class TetheringServiceTest {
public void testStopAllTethering() throws Exception { public void testStopAllTethering() throws Exception {
runAsNoPermission((result) -> { runAsNoPermission((result) -> {
mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, 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); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -680,14 +830,14 @@ public final class TetheringServiceTest {
runAsWriteSettings((result) -> { runAsWriteSettings((result) -> {
mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, 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); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> { runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runStopAllTethering(result); runStopAllTethering(result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -703,6 +853,7 @@ public final class TetheringServiceTest {
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, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed(); verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_NO_ERROR); result.assertResult(TETHER_ERROR_NO_ERROR);
@ -713,7 +864,7 @@ public final class TetheringServiceTest {
runAsNoPermission((result) -> { runAsNoPermission((result) -> {
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result); result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -726,14 +877,13 @@ public final class TetheringServiceTest {
runAsWriteSettings((result) -> { runAsWriteSettings((result) -> {
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result); result);
verify(mTethering).isTetheringWithSoftApConfigEnabled(); verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> { runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runIsTetheringSupported(result); runIsTetheringSupported(result);
verify(mTethering).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired(); verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
@ -741,6 +891,7 @@ public final class TetheringServiceTest {
runAsTetheringDisallowed((result) -> { runAsTetheringDisallowed((result) -> {
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result); result);
verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetheringSupported(); verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed(); verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_UNSUPPORTED); result.assertResult(TETHER_ERROR_UNSUPPORTED);

View file

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