Merge changes I7fae1964,Ic7cc9e1d into main

* changes:
  Add carrier-privileged app bypass for start/stopTethering
  Fix CarrierConfigRule#setHoldCarrierPrivilege race
This commit is contained in:
Treehugger Robot 2025-03-21 03:47:22 -07:00 • committed by Gerrit Code Review
commit d5743b9795
7 changed files with 419 additions and 132 deletions

View file

@ -146,7 +146,7 @@ public class TetheringService extends Service {
mTethering.setUsbTethering(enable, listener); mTethering.setUsbTethering(enable, listener);
} }
private boolean isRequestAllowedForDeviceOwner(@NonNull TetheringRequest request) { private boolean isRequestAllowedForDOOrCarrierApp(@NonNull TetheringRequest request) {
return request.getTetheringType() == TETHERING_WIFI return request.getTetheringType() == TETHERING_WIFI
&& request.getSoftApConfiguration() != null; && request.getSoftApConfiguration() != null;
} }
@ -159,10 +159,10 @@ public class TetheringService extends Service {
request.setPackageName(callerPkg); request.setPackageName(callerPkg);
boolean onlyAllowPrivileged = request.isExemptFromEntitlementCheck() boolean onlyAllowPrivileged = request.isExemptFromEntitlementCheck()
|| request.getInterfaceName() != null; || request.getInterfaceName() != null;
boolean isDeviceOwnerAllowed = mTethering.isTetheringWithSoftApConfigEnabled() boolean isDOOrCarrierAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled()
&& isRequestAllowedForDeviceOwner(request); && isRequestAllowedForDOOrCarrierApp(request);
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, onlyAllowPrivileged, if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, onlyAllowPrivileged,
isDeviceOwnerAllowed, listener)) { isDOOrCarrierAppAllowed, listener)) {
return; return;
} }
mTethering.startTethering(request, callerPkg, listener); mTethering.startTethering(request, callerPkg, listener);
@ -191,10 +191,10 @@ public class TetheringService extends Service {
if (listener == null) return; if (listener == null) return;
request.setUid(getBinderCallingUid()); request.setUid(getBinderCallingUid());
request.setPackageName(callerPkg); request.setPackageName(callerPkg);
boolean isDeviceOwnerAllowed = mTethering.isTetheringWithSoftApConfigEnabled() boolean isDOOrCarrierAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled()
&& isRequestAllowedForDeviceOwner(request); && isRequestAllowedForDOOrCarrierApp(request);
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
false /* onlyAllowPrivileged */, isDeviceOwnerAllowed, listener)) { false /* onlyAllowPrivileged */, isDOOrCarrierAppAllowed, listener)) {
return; return;
} }
// Note: Whether tethering is actually stopped or not will depend on whether the request // Note: Whether tethering is actually stopped or not will depend on whether the request
@ -274,9 +274,9 @@ 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) {
boolean isDeviceOwnerAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled(); boolean isDOOrCarrierAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled();
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
false /* onlyAllowPrivileged */, isDeviceOwnerAppAllowed, listener)) { false /* onlyAllowPrivileged */, isDOOrCarrierAppAllowed, listener)) {
return; return;
} }
try { try {
@ -304,7 +304,7 @@ public class TetheringService extends Service {
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 boolean isDeviceOwnerAppAllowed, final IIntResultListener listener) { final boolean isDOOrCarrierAppAllowed, final IIntResultListener listener) {
try { try {
final int uid = getBinderCallingUid(); final int uid = getBinderCallingUid();
if (!checkPackageNameMatchesUid(uid, callerPkg)) { if (!checkPackageNameMatchesUid(uid, callerPkg)) {
@ -313,7 +313,7 @@ public class TetheringService extends Service {
return true; return true;
} }
if (!hasTetherChangePermission(uid, callerPkg, callingAttributionTag, if (!hasTetherChangePermission(uid, callerPkg, callingAttributionTag,
onlyAllowPrivileged, isDeviceOwnerAppAllowed)) { onlyAllowPrivileged, isDOOrCarrierAppAllowed)) {
listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
return true; return true;
} }
@ -347,14 +347,18 @@ public class TetheringService extends Service {
private boolean hasTetherChangePermission(final int uid, 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) { final boolean isDOOrCarrierAppAllowed) {
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. // Allow DO and carrier-privileged apps to change tethering even if they don't have
if (isDeviceOwnerAppAllowed && mService.isDeviceOwner(uid, callerPkg)) { // TETHER_PRIVILEGED.
// TODO: Stop tethering if the app loses DO status or carrier-privileges.
if (isDOOrCarrierAppAllowed
&& (mService.isDeviceOwner(uid, callerPkg)
|| mService.isCarrierPrivileged(callerPkg))) {
return true; return true;
} }
@ -435,6 +439,14 @@ public class TetheringService extends Service {
return mTetheringPermissionsUtils.isDeviceOwner(uid, callerPkg); return mTetheringPermissionsUtils.isDeviceOwner(uid, callerPkg);
} }
/**
* Wrapper for {@link TetheringPermissionsUtils#isCarrierPrivileged(String)}, used for mocks.
*/
@VisibleForTesting
boolean isCarrierPrivileged(final String callerPkg) {
return mTetheringPermissionsUtils.isCarrierPrivileged(callerPkg);
}
/** /**
* An injection method for testing. * An injection method for testing.
*/ */

View file

@ -19,7 +19,9 @@ package com.android.networkstack.tethering.util;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.UserHandle; import android.os.UserHandle;
import android.telephony.TelephonyManager;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -68,4 +70,20 @@ public class TetheringPermissionsUtils {
} }
return devicePolicyManager; return devicePolicyManager;
} }
/**
* Checks if the package name has carrier privileges.
*/
public boolean isCarrierPrivileged(@NonNull final String packageName) {
TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
if (telephonyManager == null) return false;
long ident = Binder.clearCallingIdentity();
try {
return telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
} finally {
Binder.restoreCallingIdentity(ident);
}
}
} }

View file

@ -37,6 +37,7 @@ public class MockTetheringService extends TetheringService {
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 final Set<String> mMockedDeviceOwnerPackages = new ArraySet<>();
private final Set<String> mMockedCarrierPrivilegedPackages = new ArraySet<>();
private int mMockCallingUid; private int mMockCallingUid;
@Override @Override
@ -83,6 +84,11 @@ public class MockTetheringService extends TetheringService {
return mMockedDeviceOwnerPackages.contains(callerPkg); return mMockedDeviceOwnerPackages.contains(callerPkg);
} }
@Override
boolean isCarrierPrivileged(final String callerPkg) {
return mMockedCarrierPrivilegedPackages.contains(callerPkg);
}
public Tethering getTethering() { public Tethering getTethering() {
return mTethering; return mTethering;
} }
@ -141,5 +147,19 @@ public class MockTetheringService extends TetheringService {
public void removeDeviceOwnerPackage(final String packageName) { public void removeDeviceOwnerPackage(final String packageName) {
mMockedDeviceOwnerPackages.remove(packageName); mMockedDeviceOwnerPackages.remove(packageName);
} }
/**
* Add a mocked carrier privileges package
*/
public void addCarrierPrivilegedPackage(final String packageName) {
mMockedCarrierPrivilegedPackages.add(packageName);
}
/**
* Remove a mocked carrier privileges package
*/
public void removeCarrierPrivilegedPackage(final String packageName) {
mMockedCarrierPrivilegedPackages.remove(packageName);
}
} }
} }

View file

@ -90,6 +90,15 @@ public final class TetheringServiceTest {
private static final int TEST_CALLER_UID = 1234; private static final int TEST_CALLER_UID = 1234;
private static final String TEST_ATTRIBUTION_TAG = null; private static final String TEST_ATTRIBUTION_TAG = null;
private static final String TEST_WRONG_PACKAGE = "wrong.package"; private static final String TEST_WRONG_PACKAGE = "wrong.package";
private static final int NO_RESULT = -1;
private static final TetheringRequest USB_REQUEST =
new TetheringRequest.Builder(TETHERING_USB).build();
private static final TetheringRequest WIFI_REQUEST_NO_CONFIG =
new TetheringRequest.Builder(TETHERING_WIFI).build();
private static final TetheringRequest WIFI_REQUEST_WITH_CONFIG =
new TetheringRequest.Builder(TETHERING_WIFI)
.setSoftApConfiguration(new SoftApConfiguration.Builder().build())
.build();
@Mock private ITetheringEventCallback mITetheringEventCallback; @Mock private ITetheringEventCallback mITetheringEventCallback;
@Rule public ServiceTestRule mServiceTestRule; @Rule public ServiceTestRule mServiceTestRule;
private Tethering mTethering; private Tethering mTethering;
@ -100,7 +109,7 @@ public final class TetheringServiceTest {
@Mock private AppOpsManager mAppOps; @Mock private AppOpsManager mAppOps;
private class TestTetheringResult extends IIntResultListener.Stub { private class TestTetheringResult extends IIntResultListener.Stub {
private int mResult = -1; // Default value that does not match any result code. private int mResult = NO_RESULT;
@Override @Override
public void onResult(final int resultCode) { public void onResult(final int resultCode) {
mResult = resultCode; mResult = resultCode;
@ -115,7 +124,7 @@ public final class TetheringServiceTest {
MyResultReceiver(Handler handler) { MyResultReceiver(Handler handler) {
super(handler); super(handler);
} }
private int mResult = -1; // Default value that does not match any result code. private int mResult = NO_RESULT;
@Override @Override
protected void onReceiveResult(int resultCode, Bundle resultData) { protected void onReceiveResult(int resultCode, Bundle resultData) {
mResult = resultCode; mResult = resultCode;
@ -206,6 +215,21 @@ public final class TetheringServiceTest {
mMockConnector.removeDeviceOwnerPackage(TEST_CALLER_PKG); mMockConnector.removeDeviceOwnerPackage(TEST_CALLER_PKG);
} }
private void runAsCarrierPrivileged(final TestTetheringCall test) throws Exception {
mMockConnector.addCarrierPrivilegedPackage(TEST_CALLER_PKG);
runTetheringCall(test, true /* isTetheringAllowed */,
true /* isTetheringWithSoftApConfigEnabled */, new String[0]);
mMockConnector.removeCarrierPrivilegedPackage(TEST_CALLER_PKG);
}
private void runAsCarrierPrivilegedWhenCarrierPrivilegeBypassNotEnabled(
final TestTetheringCall test) throws Exception {
mMockConnector.addCarrierPrivilegedPackage(TEST_CALLER_PKG);
runTetheringCall(test, true /* isTetheringAllowed */,
false /* isTetheringWithSoftApConfigEnabled */, new String[0]);
mMockConnector.removeCarrierPrivilegedPackage(TEST_CALLER_PKG);
}
private void runTetheringCall(final TestTetheringCall test, boolean isTetheringAllowed, private void runTetheringCall(final TestTetheringCall test, boolean isTetheringAllowed,
boolean isTetheringWithSoftApConfigEnabled, 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
@ -381,125 +405,114 @@ public final class TetheringServiceTest {
}); });
} }
private void runStartTethering(final TestTetheringResult result, private void verifyStartTetheringRequestSucceeds(final TetheringRequest request,
final TetheringRequestParcel request) throws Exception { final TestTetheringResult result) throws Exception {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, mTetheringConnector.startTethering(request.getParcel(), TEST_CALLER_PKG,
result); TEST_ATTRIBUTION_TAG, result);
verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled(); verify(mTethering).startTethering(eq(request), eq(TEST_CALLER_PKG), eq(result));
verify(mTethering).isTetheringSupported(); reset(mTethering);
verify(mTethering).isTetheringAllowed(); result.assertResult(NO_RESULT);
verify(mTethering).startTethering( }
eq(new TetheringRequest(request)), eq(TEST_CALLER_PKG), eq(result));
private void verifyStartTetheringRequestFails(final TetheringRequest request,
final TestTetheringResult result, final int resultCode) throws Exception {
mTetheringConnector.startTethering(request.getParcel(), TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result);
verify(mTethering, never()).startTethering(any(), any(), any());
reset(mTethering);
result.assertResult(resultCode);
}
private void verifyStartTetheringRequestWithWrongPackageFails(final TetheringRequest request,
final TestTetheringResult result) throws Exception {
mTetheringConnector.startTethering(request.getParcel(), TEST_WRONG_PACKAGE,
TEST_ATTRIBUTION_TAG, result);
verify(mTethering, never()).startTethering(any(), any(), any());
reset(mTethering);
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
} }
@Test @Test
public void testStartTethering() throws Exception { public void testStartTethering() throws Exception {
final TetheringRequestParcel request = new TetheringRequestParcel();
request.tetheringType = TETHERING_WIFI;
runAsNoPermission((result) -> { runAsNoPermission((result) -> {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, verifyStartTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
result); TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
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) -> { runAsTetherPrivileged((result) -> {
mTetheringConnector.startTethering(request, TEST_WRONG_PACKAGE, verifyStartTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result);
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);
verifyNoMoreInteractionsForTethering();
}); });
runAsTetherPrivileged((result) -> { runAsTetherPrivileged((result) -> {
runStartTethering(result, request); verifyStartTetheringRequestWithWrongPackageFails(WIFI_REQUEST_NO_CONFIG, result);
verifyNoMoreInteractionsForTethering();
}); });
runAsWriteSettings((result) -> { runAsWriteSettings((result) -> {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, verifyStartTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
result); TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
}); });
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> { runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runStartTethering(result, request); verifyStartTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result);
verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
}); });
runAsTetheringDisallowed((result) -> { runAsTetheringDisallowed((result) -> {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, verifyStartTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
result); TETHER_ERROR_UNSUPPORTED);
verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled(); });
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed(); // Not wifi -> fail
result.assertResult(TETHER_ERROR_UNSUPPORTED); runAsDeviceOwner((result) -> {
verifyNoMoreInteractionsForTethering(); verifyStartTetheringRequestFails(USB_REQUEST, result,
TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
// No SoftApConfiguration -> fail
runAsDeviceOwner((result) -> {
verifyStartTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
// With SoftApConfiguration -> success
runAsDeviceOwner((result) -> {
verifyStartTetheringRequestSucceeds(WIFI_REQUEST_WITH_CONFIG, result);
});
runAsDeviceOwnerWhenDeviceOwnerBypassNotEnabled((result) -> {
verifyStartTetheringRequestFails(WIFI_REQUEST_WITH_CONFIG, result,
TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
// Not wifi -> fail
runAsCarrierPrivileged((result) -> {
verifyStartTetheringRequestFails(USB_REQUEST, result,
TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
// No SoftApConfiguration -> fail
runAsCarrierPrivileged((result) -> {
verifyStartTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
// With SoftApConfiguration -> success
runAsCarrierPrivileged((result) -> {
verifyStartTetheringRequestSucceeds(WIFI_REQUEST_WITH_CONFIG, result);
});
runAsCarrierPrivilegedWhenCarrierPrivilegeBypassNotEnabled((result) -> {
verifyStartTetheringRequestFails(WIFI_REQUEST_WITH_CONFIG, result,
TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
}); });
} }
@Test @Test
public void testStartTetheringWithInterfaceSucceeds() throws Exception { public void testStartTetheringWithInterfaceSucceeds() throws Exception {
final TetheringRequestParcel request = new TetheringRequestParcel(); final TetheringRequest request = new TetheringRequest.Builder(TETHERING_VIRTUAL)
request.tetheringType = TETHERING_VIRTUAL; .setInterfaceName("avf_tap_fixed")
request.interfaceName = "avf_tap_fixed"; .build();
runAsNetworkSettings((result) -> { runAsNetworkSettings((result) -> {
runStartTethering(result, request); verifyStartTetheringRequestSucceeds(request, result);
verifyNoMoreInteractionsForTethering(); verifyNoMoreInteractionsForTethering();
}); });
} }
@ -599,84 +612,110 @@ public final class TetheringServiceTest {
}); });
} }
private void verifyHasPermissionForStopTetheringRequest(TetheringRequest request, private void verifyStopTetheringRequestSucceeds(final TetheringRequest request,
final TestTetheringResult result) throws Exception { final TestTetheringResult result) throws Exception {
mTetheringConnector.stopTetheringRequest(request, TEST_CALLER_PKG, mTetheringConnector.stopTetheringRequest(request, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result); TEST_ATTRIBUTION_TAG, result);
verify(mTethering).stopTetheringRequest(any(), any()); verify(mTethering).stopTetheringRequest(any(), any());
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
reset(mTethering); reset(mTethering);
result.assertResult(NO_RESULT);
} }
private void verifyDoesNotHavePermissionForStopTetheringRequest(TetheringRequest request, private void verifyStopTetheringRequestFails(final TetheringRequest request,
final TestTetheringResult result) throws Exception { final TestTetheringResult result, int resultCode) throws Exception {
mTetheringConnector.stopTetheringRequest(request, TEST_CALLER_PKG, mTetheringConnector.stopTetheringRequest(request, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result); TEST_ATTRIBUTION_TAG, result);
verify(mTethering, never()).stopTetheringRequest(any(), any()); verify(mTethering, never()).stopTetheringRequest(any(), any());
reset(mTethering);
result.assertResult(resultCode);
}
private void verifyStopTetheringRequestWithWrongPackageFails(final TetheringRequest request,
final TestTetheringResult result) throws Exception {
mTetheringConnector.stopTetheringRequest(request, TEST_WRONG_PACKAGE,
TEST_ATTRIBUTION_TAG, result);
verify(mTethering, never()).stopTetheringRequest(any(), any());
reset(mTethering);
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); 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 @Test
public void testStopTetheringRequest() throws Exception { public void testStopTetheringRequest() throws Exception {
TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
runAsNoPermission((result) -> { runAsNoPermission((result) -> {
verifyDoesNotHavePermissionForStopTetheringRequest(request, result); verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
}); });
runAsTetherPrivileged((result) -> { runAsTetherPrivileged((result) -> {
verifyHasPermissionForStopTetheringRequest(request, result); verifyStopTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result);
});
runAsTetherPrivileged((result) -> {
verifyStopTetheringRequestWithWrongPackageFails(WIFI_REQUEST_NO_CONFIG, result);
}); });
runAsWriteSettings((result) -> { runAsWriteSettings((result) -> {
verifyDoesNotHavePermissionForStopTetheringRequest(request, result); verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
}); });
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> { runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
verifyHasPermissionForStopTetheringRequest(request, result); // Note: This can't happen in practice since WRITE_SETTINGS is only allowed on V- and
// stopTetheringRequest is only allowed on B+, but we test here for completeness.
verifyStopTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result);
}); });
runAsTetheringDisallowed((result) -> { runAsTetheringDisallowed((result) -> {
verifyStopTetheringRequestWithTetheringDisallowed(request, result); verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
TETHER_ERROR_UNSUPPORTED);
}); });
runAsNetworkSettings((result) -> { runAsNetworkSettings((result) -> {
verifyHasPermissionForStopTetheringRequest(request, result); verifyStopTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result);
}); });
// Not wifi -> fail // Not wifi -> fail
runAsDeviceOwner((result) -> { runAsDeviceOwner((result) -> {
TetheringRequest notWifi = new TetheringRequest.Builder(TETHERING_USB).build(); verifyStopTetheringRequestFails(USB_REQUEST, result,
verifyDoesNotHavePermissionForStopTetheringRequest(notWifi, result); TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
}); });
// No config -> fail // No SoftApConfiguration -> fail
runAsDeviceOwner((result) -> { runAsDeviceOwner((result) -> {
TetheringRequest noConfig = new TetheringRequest.Builder(TETHERING_WIFI).build(); verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
verifyDoesNotHavePermissionForStopTetheringRequest(noConfig, result); TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
}); });
// With config -> success // With SoftApConfiguration -> success
TetheringRequest withConfig = new TetheringRequest.Builder(TETHERING_WIFI)
.setSoftApConfiguration(new SoftApConfiguration.Builder().build())
.build();
runAsDeviceOwner((result) -> { runAsDeviceOwner((result) -> {
verifyHasPermissionForStopTetheringRequest(withConfig, result); verifyStopTetheringRequestSucceeds(WIFI_REQUEST_WITH_CONFIG, result);
}); });
runAsDeviceOwnerWhenDeviceOwnerBypassNotEnabled((result) -> { runAsDeviceOwnerWhenDeviceOwnerBypassNotEnabled((result) -> {
verifyDoesNotHavePermissionForStopTetheringRequest(withConfig, result); verifyStopTetheringRequestFails(WIFI_REQUEST_WITH_CONFIG, result,
TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
// Not wifi -> fail
runAsCarrierPrivileged((result) -> {
verifyStopTetheringRequestFails(USB_REQUEST, result,
TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
// No SoftApConfiguration -> fail
runAsCarrierPrivileged((result) -> {
verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
// With SoftApConfiguration -> success
runAsCarrierPrivileged((result) -> {
verifyStopTetheringRequestSucceeds(WIFI_REQUEST_WITH_CONFIG, result);
});
runAsCarrierPrivilegedWhenCarrierPrivilegeBypassNotEnabled((result) -> {
verifyStopTetheringRequestFails(WIFI_REQUEST_WITH_CONFIG, result,
TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
}); });
} }

View file

@ -67,4 +67,15 @@ public class TetheringPermissionsUtilsTest {
when(mDevicePolicyManager.isDeviceOwnerApp(TEST_PACKAGE)).thenReturn(true); when(mDevicePolicyManager.isDeviceOwnerApp(TEST_PACKAGE)).thenReturn(true);
assertThat(mTetheringPermissionsUtils.isDeviceOwner(TEST_UID, TEST_PACKAGE)).isTrue(); assertThat(mTetheringPermissionsUtils.isDeviceOwner(TEST_UID, TEST_PACKAGE)).isTrue();
} }
@Test
public void testHasCarrierPrivilege() {
when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(TEST_PACKAGE))
.thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
assertThat(mTetheringPermissionsUtils.isCarrierPrivileged(TEST_PACKAGE)).isFalse();
when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(TEST_PACKAGE))
.thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
assertThat(mTetheringPermissionsUtils.isCarrierPrivileged(TEST_PACKAGE)).isTrue();
}
} }

View file

@ -40,6 +40,7 @@ import com.android.modules.utils.build.SdkLevel
import com.android.testutils.runAsShell import com.android.testutils.runAsShell
import com.android.testutils.tryTest import com.android.testutils.tryTest
import java.security.MessageDigest import java.security.MessageDigest
import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
import org.junit.rules.TestRule import org.junit.rules.TestRule
@ -204,6 +205,7 @@ class CarrierConfigRule : TestRule {
} }
return@tryTest return@tryTest
} }
cv.close()
if (hold) { if (hold) {
addConfigOverrides(subId, PersistableBundle().also { addConfigOverrides(subId, PersistableBundle().also {
it.putStringArray(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY, it.putStringArray(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
@ -212,6 +214,9 @@ class CarrierConfigRule : TestRule {
} else { } else {
cleanUpNow() cleanUpNow()
} }
assertTrue(cv.block(CARRIER_CONFIG_CHANGE_TIMEOUT_MS),
"Timed out waiting for CarrierPrivilegesCallback")
assertEquals(cpb.hasPrivilege, hold, "Couldn't set carrier privilege")
} cleanup @JvmSerializableLambda { } cleanup @JvmSerializableLambda {
runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda { runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
tm.unregisterCarrierPrivilegesCallback(cpb) tm.unregisterCarrierPrivilegesCallback(cpb)

View file

@ -36,6 +36,7 @@ import static android.net.TetheringManager.TETHER_ERROR_DUPLICATE_REQUEST;
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
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_UNKNOWN_REQUEST;
import static android.net.cts.util.CtsTetheringUtils.isAnyIfaceMatch; import static android.net.cts.util.CtsTetheringUtils.isAnyIfaceMatch;
import static android.os.Process.INVALID_UID; import static android.os.Process.INVALID_UID;
@ -714,4 +715,185 @@ public class TetheringManagerTest {
assertThrows(UnsupportedOperationException.class, () -> mTM.tether("iface")); assertThrows(UnsupportedOperationException.class, () -> mTM.tether("iface"));
assertThrows(UnsupportedOperationException.class, () -> mTM.untether("iface")); assertThrows(UnsupportedOperationException.class, () -> mTM.untether("iface"));
} }
@Test
public void testCarrierPrivilegedIsTetheringSupported() throws Exception {
assumeTrue(SdkLevel.isAtLeastB());
assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
final TestTetheringEventCallback tetherEventCallback =
mCtsTetheringUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
tetherEventCallback.expectNoTetheringActive();
assertTrue(mTM.isTetheringSupported());
} finally {
mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
}
}
@Test
public void testCarrierPrivilegedStartTetheringNonWifiFails() throws Exception {
assumeTrue(SdkLevel.isAtLeastB());
assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
final TestTetheringEventCallback tetherEventCallback =
mCtsTetheringUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
tetherEventCallback.expectNoTetheringActive();
StartTetheringCallback callback = new StartTetheringCallback();
TetheringRequest request = new TetheringRequest.Builder(TETHERING_USB).build();
mTM.startTethering(request, Runnable::run, callback);
callback.expectTetheringFailed(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
} finally {
mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
}
}
@Test
public void testCarrierPrivilegedStartTetheringWifiWithoutConfigFails() throws Exception {
assumeTrue(SdkLevel.isAtLeastB());
assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
final TestTetheringEventCallback tetherEventCallback =
mCtsTetheringUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
tetherEventCallback.expectNoTetheringActive();
StartTetheringCallback callback = new StartTetheringCallback();
TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
mTM.startTethering(request, Runnable::run, callback);
callback.expectTetheringFailed(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
} finally {
mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
}
}
@Test
public void testCarrierPrivilegedStartTetheringWifiWithConfigSucceeds() throws Exception {
assumeTrue(SdkLevel.isAtLeastB());
assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
final TestTetheringEventCallback tetherEventCallback =
mCtsTetheringUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
tetherEventCallback.expectNoTetheringActive();
SoftApConfiguration softApConfig = createSoftApConfiguration("Carrier-privileged");
mCtsTetheringUtils.startWifiTetheringNoPermissions(tetherEventCallback, softApConfig);
} finally {
mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
}
}
@Test
public void testCarrierPrivilegedStopTetheringNonWifiFails() throws Exception {
assumeTrue(SdkLevel.isAtLeastB());
assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
final TestTetheringEventCallback tetherEventCallback =
mCtsTetheringUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
tetherEventCallback.expectNoTetheringActive();
TetheringRequest request = new TetheringRequest.Builder(TETHERING_USB).build();
CtsTetheringUtils.StopTetheringCallback
callback = new CtsTetheringUtils.StopTetheringCallback();
mTM.stopTethering(request, Runnable::run, callback);
callback.expectStopTetheringFailed(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
} finally {
mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
}
}
@Test
public void testCarrierPrivilegedStopTetheringWifiWithoutConfigFails() throws Exception {
assumeTrue(SdkLevel.isAtLeastB());
assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
final TestTetheringEventCallback tetherEventCallback =
mCtsTetheringUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
tetherEventCallback.expectNoTetheringActive();
TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
CtsTetheringUtils.StopTetheringCallback
callback = new CtsTetheringUtils.StopTetheringCallback();
mTM.stopTethering(request, Runnable::run, callback);
callback.expectStopTetheringFailed(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
} finally {
mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
}
}
@Test
public void testCarrierPrivilegedStopTetheringWifiWithConfigButNoActiveRequestFails()
throws Exception {
assumeTrue(SdkLevel.isAtLeastB());
assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
final TestTetheringEventCallback tetherEventCallback =
mCtsTetheringUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
tetherEventCallback.expectNoTetheringActive();
SoftApConfiguration softApConfig = createSoftApConfiguration("Carrier-privileged");
TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
.setSoftApConfiguration(softApConfig)
.build();
CtsTetheringUtils.StopTetheringCallback
callback = new CtsTetheringUtils.StopTetheringCallback();
mTM.stopTethering(request, Runnable::run, callback);
callback.expectStopTetheringFailed(TETHER_ERROR_UNKNOWN_REQUEST);
} finally {
mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
}
}
@Test
public void testCarrierPrivilegedStopTetheringWifiWithConfigSucceeds() throws Exception {
assumeTrue(SdkLevel.isAtLeastB());
assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
final TestTetheringEventCallback tetherEventCallback =
mCtsTetheringUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
tetherEventCallback.expectNoTetheringActive();
SoftApConfiguration softApConfig = createSoftApConfiguration("Carrier-privileged");
mCtsTetheringUtils.startWifiTetheringNoPermissions(tetherEventCallback, softApConfig);
TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
.setSoftApConfiguration(softApConfig)
.build();
CtsTetheringUtils.StopTetheringCallback
callback = new CtsTetheringUtils.StopTetheringCallback();
mTM.stopTethering(request, Runnable::run, callback);
callback.verifyStopTetheringSucceeded();
} finally {
mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
}
}
} }