diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java index b92cf69280..737041e3cf 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java @@ -146,7 +146,7 @@ public class TetheringService extends Service { mTethering.setUsbTethering(enable, listener); } - private boolean isRequestAllowedForDeviceOwner(@NonNull TetheringRequest request) { + private boolean isRequestAllowedForDOOrCarrierApp(@NonNull TetheringRequest request) { return request.getTetheringType() == TETHERING_WIFI && request.getSoftApConfiguration() != null; } @@ -159,10 +159,10 @@ public class TetheringService extends Service { request.setPackageName(callerPkg); boolean onlyAllowPrivileged = request.isExemptFromEntitlementCheck() || request.getInterfaceName() != null; - boolean isDeviceOwnerAllowed = mTethering.isTetheringWithSoftApConfigEnabled() - && isRequestAllowedForDeviceOwner(request); + boolean isDOOrCarrierAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled() + && isRequestAllowedForDOOrCarrierApp(request); if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, onlyAllowPrivileged, - isDeviceOwnerAllowed, listener)) { + isDOOrCarrierAppAllowed, listener)) { return; } mTethering.startTethering(request, callerPkg, listener); @@ -191,10 +191,10 @@ public class TetheringService extends Service { if (listener == null) return; request.setUid(getBinderCallingUid()); request.setPackageName(callerPkg); - boolean isDeviceOwnerAllowed = mTethering.isTetheringWithSoftApConfigEnabled() - && isRequestAllowedForDeviceOwner(request); + boolean isDOOrCarrierAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled() + && isRequestAllowedForDOOrCarrierApp(request); if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, - false /* onlyAllowPrivileged */, isDeviceOwnerAllowed, listener)) { + false /* onlyAllowPrivileged */, isDOOrCarrierAppAllowed, listener)) { return; } // Note: Whether tethering is actually stopped or not will depend on whether the request @@ -274,9 +274,9 @@ public class TetheringService extends Service { @Override public void isTetheringSupported(String callerPkg, String callingAttributionTag, IIntResultListener listener) { - boolean isDeviceOwnerAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled(); + boolean isDOOrCarrierAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled(); if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, - false /* onlyAllowPrivileged */, isDeviceOwnerAppAllowed, listener)) { + false /* onlyAllowPrivileged */, isDOOrCarrierAppAllowed, listener)) { return; } try { @@ -304,7 +304,7 @@ public class TetheringService extends Service { private boolean checkAndNotifyCommonError(final String callerPkg, final String callingAttributionTag, final boolean onlyAllowPrivileged, - final boolean isDeviceOwnerAppAllowed, final IIntResultListener listener) { + final boolean isDOOrCarrierAppAllowed, final IIntResultListener listener) { try { final int uid = getBinderCallingUid(); if (!checkPackageNameMatchesUid(uid, callerPkg)) { @@ -313,7 +313,7 @@ public class TetheringService extends Service { return true; } if (!hasTetherChangePermission(uid, callerPkg, callingAttributionTag, - onlyAllowPrivileged, isDeviceOwnerAppAllowed)) { + onlyAllowPrivileged, isDOOrCarrierAppAllowed)) { listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); return true; } @@ -347,14 +347,18 @@ public class TetheringService extends Service { private boolean hasTetherChangePermission(final int uid, final String callerPkg, final String callingAttributionTag, final boolean onlyAllowPrivileged, - final boolean isDeviceOwnerAppAllowed) { + final boolean isDOOrCarrierAppAllowed) { 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)) { + // Allow DO and carrier-privileged apps to change tethering even if they don't have + // 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; } @@ -435,6 +439,14 @@ public class TetheringService extends Service { 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. */ diff --git a/Tethering/src/com/android/networkstack/tethering/util/TetheringPermissionsUtils.java b/Tethering/src/com/android/networkstack/tethering/util/TetheringPermissionsUtils.java index 944e8617c0..603fa9ccbd 100644 --- a/Tethering/src/com/android/networkstack/tethering/util/TetheringPermissionsUtils.java +++ b/Tethering/src/com/android/networkstack/tethering/util/TetheringPermissionsUtils.java @@ -19,7 +19,9 @@ package com.android.networkstack.tethering.util; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.pm.PackageManager; +import android.os.Binder; import android.os.UserHandle; +import android.telephony.TelephonyManager; import android.util.Log; import androidx.annotation.NonNull; @@ -68,4 +70,20 @@ public class TetheringPermissionsUtils { } 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); + } + } } 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 a8bd2216ef..01d71983d2 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java @@ -37,6 +37,7 @@ public class MockTetheringService extends TetheringService { private final ArrayMap mMockedPermissions = new ArrayMap<>(); private final ArrayMap mMockedPackageUids = new ArrayMap<>(); private final Set mMockedDeviceOwnerPackages = new ArraySet<>(); + private final Set mMockedCarrierPrivilegedPackages = new ArraySet<>(); private int mMockCallingUid; @Override @@ -83,6 +84,11 @@ public class MockTetheringService extends TetheringService { return mMockedDeviceOwnerPackages.contains(callerPkg); } + @Override + boolean isCarrierPrivileged(final String callerPkg) { + return mMockedCarrierPrivilegedPackages.contains(callerPkg); + } + public Tethering getTethering() { return mTethering; } @@ -141,5 +147,19 @@ public class MockTetheringService extends TetheringService { public void removeDeviceOwnerPackage(final String 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); + } } } 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 87163ef06a..b58fa14007 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java @@ -90,6 +90,15 @@ public final class TetheringServiceTest { private static final int TEST_CALLER_UID = 1234; private static final String TEST_ATTRIBUTION_TAG = null; 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; @Rule public ServiceTestRule mServiceTestRule; private Tethering mTethering; @@ -100,7 +109,7 @@ public final class TetheringServiceTest { @Mock private AppOpsManager mAppOps; 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 public void onResult(final int resultCode) { mResult = resultCode; @@ -115,7 +124,7 @@ public final class TetheringServiceTest { MyResultReceiver(Handler handler) { super(handler); } - private int mResult = -1; // Default value that does not match any result code. + private int mResult = NO_RESULT; @Override protected void onReceiveResult(int resultCode, Bundle resultData) { mResult = resultCode; @@ -206,6 +215,21 @@ public final class TetheringServiceTest { 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, boolean isTetheringWithSoftApConfigEnabled, String... permissions) throws Exception { // 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, - 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( - eq(new TetheringRequest(request)), eq(TEST_CALLER_PKG), eq(result)); + private void verifyStartTetheringRequestSucceeds(final TetheringRequest request, + final TestTetheringResult result) throws Exception { + mTetheringConnector.startTethering(request.getParcel(), TEST_CALLER_PKG, + TEST_ATTRIBUTION_TAG, result); + verify(mTethering).startTethering(eq(request), eq(TEST_CALLER_PKG), eq(result)); + reset(mTethering); + result.assertResult(NO_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 public void testStartTethering() throws Exception { - final TetheringRequestParcel request = new TetheringRequestParcel(); - request.tetheringType = TETHERING_WIFI; - runAsNoPermission((result) -> { - mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - 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(); + verifyStartTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result, + TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); }); 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); - verifyNoMoreInteractionsForTethering(); + verifyStartTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result); }); runAsTetherPrivileged((result) -> { - runStartTethering(result, request); - verifyNoMoreInteractionsForTethering(); + verifyStartTetheringRequestWithWrongPackageFails(WIFI_REQUEST_NO_CONFIG, result); }); runAsWriteSettings((result) -> { - mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, - result); - verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled(); - result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); - verifyNoMoreInteractionsForTethering(); + verifyStartTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result, + TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); }); runAsWriteSettingsWhenWriteSettingsAllowed((result) -> { - runStartTethering(result, request); - verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled(); - verify(mTethering).isTetherProvisioningRequired(); - verifyNoMoreInteractionsForTethering(); + verifyStartTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result); }); 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); - verifyNoMoreInteractionsForTethering(); + verifyStartTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result, + TETHER_ERROR_UNSUPPORTED); + }); + + // Not wifi -> fail + runAsDeviceOwner((result) -> { + 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 public void testStartTetheringWithInterfaceSucceeds() throws Exception { - final TetheringRequestParcel request = new TetheringRequestParcel(); - request.tetheringType = TETHERING_VIRTUAL; - request.interfaceName = "avf_tap_fixed"; - + final TetheringRequest request = new TetheringRequest.Builder(TETHERING_VIRTUAL) + .setInterfaceName("avf_tap_fixed") + .build(); runAsNetworkSettings((result) -> { - runStartTethering(result, request); + verifyStartTetheringRequestSucceeds(request, result); 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 { mTetheringConnector.stopTetheringRequest(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); verify(mTethering).stopTetheringRequest(any(), any()); - verify(mTethering).isTetheringSupported(); - verify(mTethering).isTetheringAllowed(); reset(mTethering); + result.assertResult(NO_RESULT); } - private void verifyDoesNotHavePermissionForStopTetheringRequest(TetheringRequest request, - final TestTetheringResult result) throws Exception { + private void verifyStopTetheringRequestFails(final TetheringRequest request, + final TestTetheringResult result, int resultCode) throws Exception { mTetheringConnector.stopTetheringRequest(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); 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); - 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); + verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result, + TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); }); runAsTetherPrivileged((result) -> { - verifyHasPermissionForStopTetheringRequest(request, result); + verifyStopTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result); + }); + + runAsTetherPrivileged((result) -> { + verifyStopTetheringRequestWithWrongPackageFails(WIFI_REQUEST_NO_CONFIG, result); }); runAsWriteSettings((result) -> { - verifyDoesNotHavePermissionForStopTetheringRequest(request, result); + verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result, + TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); }); 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) -> { - verifyStopTetheringRequestWithTetheringDisallowed(request, result); + verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result, + TETHER_ERROR_UNSUPPORTED); }); runAsNetworkSettings((result) -> { - verifyHasPermissionForStopTetheringRequest(request, result); + verifyStopTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result); }); // Not wifi -> fail runAsDeviceOwner((result) -> { - TetheringRequest notWifi = new TetheringRequest.Builder(TETHERING_USB).build(); - verifyDoesNotHavePermissionForStopTetheringRequest(notWifi, result); + verifyStopTetheringRequestFails(USB_REQUEST, result, + TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); }); - // No config -> fail + // No SoftApConfiguration -> fail runAsDeviceOwner((result) -> { - TetheringRequest noConfig = new TetheringRequest.Builder(TETHERING_WIFI).build(); - verifyDoesNotHavePermissionForStopTetheringRequest(noConfig, result); + verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result, + TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); }); - // With config -> success - TetheringRequest withConfig = new TetheringRequest.Builder(TETHERING_WIFI) - .setSoftApConfiguration(new SoftApConfiguration.Builder().build()) - .build(); + // With SoftApConfiguration -> success runAsDeviceOwner((result) -> { - verifyHasPermissionForStopTetheringRequest(withConfig, result); + verifyStopTetheringRequestSucceeds(WIFI_REQUEST_WITH_CONFIG, 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); }); } 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 index 57c3ecad9f..2b70e39eac 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringPermissionsUtilsTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringPermissionsUtilsTest.java @@ -67,4 +67,15 @@ public class TetheringPermissionsUtilsTest { when(mDevicePolicyManager.isDeviceOwnerApp(TEST_PACKAGE)).thenReturn(true); 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(); + } } diff --git a/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt index c9d2527e4a..076398e2f8 100644 --- a/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt +++ b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt @@ -40,6 +40,7 @@ import com.android.modules.utils.build.SdkLevel import com.android.testutils.runAsShell import com.android.testutils.tryTest import java.security.MessageDigest +import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue import org.junit.rules.TestRule @@ -204,6 +205,7 @@ class CarrierConfigRule : TestRule { } return@tryTest } + cv.close() if (hold) { addConfigOverrides(subId, PersistableBundle().also { it.putStringArray(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY, @@ -212,6 +214,9 @@ class CarrierConfigRule : TestRule { } else { 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 { runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda { tm.unregisterCarrierPrivilegesCallback(cpb) diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java index abe628b0b0..92d58e6133 100644 --- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java +++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java @@ -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_NO_CHANGE_TETHERING_PERMISSION; 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.os.Process.INVALID_UID; @@ -714,4 +715,185 @@ public class TetheringManagerTest { assertThrows(UnsupportedOperationException.class, () -> mTM.tether("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); + } + } }