/* * Copyright (C) 2021 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.compatibility.common.util; import static android.telephony.TelephonyManager.CarrierPrivilegesCallback; import static com.android.compatibility.common.util.SystemUtil.runShellCommand; import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; import static org.junit.Assert.fail; import android.Manifest; import android.annotation.TargetApi; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; import java.security.MessageDigest; import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * Utility to execute a code block with carrier privileges, or as the carrier service. * *
The utility methods contained in this class will release carrier privileges once the specified * task is completed. * *
This utility class explicitly does not support cases where the calling application is SIM * privileged; in that case, the rescinding of carrier privileges will time out and fail. * *
Example: * *
* CarrierPrivilegeUtils.withCarrierPrivileges(c, subId, () -> telephonyManager.setFoo(bar)); * CarrierPrivilegeUtils.asCarrierService(c, subId, () -> telephonyManager.setFoo(bar)); ** * @see TelephonyManager#hasCarrierPrivileges() */ @TargetApi(Build.VERSION_CODES.TIRAMISU) public final class CarrierPrivilegeUtils { private static final String TAG = CarrierPrivilegeUtils.class.getSimpleName(); private static class CarrierPrivilegeChangeMonitor implements AutoCloseable { private final CountDownLatch mLatch = new CountDownLatch(1); private final Context mContext; private final boolean mIsShell; private final TelephonyManager mTelephonyManager; private final CarrierPrivilegesCallback mCarrierPrivilegesCallback; /** * Construct a {@link CarrierPrivilegesCallback} to monitor carrier privileges change. * * @param c context * @param subId subscriptionId to listen to * @param gainCarrierPrivileges true if wait to grant carrier privileges, false if wait to * revoke * @param overrideCarrierServicePackage {@code true} if this should wait for an override to * take effect, {@code false} if this should wait for the override to be cleared * @param isShell true if the caller is Shell */ CarrierPrivilegeChangeMonitor( Context c, int subId, boolean gainCarrierPrivileges, boolean overrideCarrierServicePackage, boolean isShell) { mContext = c; mIsShell = isShell; mTelephonyManager = mContext.getSystemService( TelephonyManager.class).createForSubscriptionId(subId); Objects.requireNonNull(mTelephonyManager); final int slotIndex = SubscriptionManager.getSlotIndex(subId); mCarrierPrivilegesCallback = new CarrierPrivilegesCallback() { /* * onCarrierServiceChanged() returns a @Nullable carrierServicePackageName, * and TM#getCarrierServicePackageNameForLogicalSlot() requires * using shell permission identity to get READ_PRIVILEGED_PHONE_STATE, which * could clobber actual CTS package values. As such, we have to track both * the carrier service package name, and that it has truly been set * (including being set to null). The associated onCarrierServiceChanged * callback will always be fired upon registration in the enclosing * CarrierPrivilegeChangeMonitor constructor. */ private boolean mHasReceivedCarrierServicePackageName = false; private String mCarrierServicePackage = null; @Override public void onCarrierPrivilegesChanged( Set
Unless refcounted, a nested call will clear privileges on the outer call. */ private static class NestedCallChecker implements AutoCloseable { private static final AtomicBoolean sCheckBit = new AtomicBoolean(); private NestedCallChecker() { if (!sCheckBit.compareAndSet(false /* expected */, true /* update */)) { fail("Nested CarrierPrivilegeUtils calls are not supported"); } } @Override public void close() { sCheckBit.set(false); } } /** Runs the provided action with the calling package granted carrier privileges. */ public static void withCarrierPrivileges(Context c, int subId, ThrowingRunnable action) throws Exception { try (NestedCallChecker checker = new NestedCallChecker()) { changeCarrierPrivileges( c, subId, true /* gainCarrierPrivileges */, false /* overrideCarrierServicePackage */, false /* isShell */); action.run(); } finally { changeCarrierPrivileges( c, subId, false /* gainCarrierPrivileges */, false /* overrideCarrierServicePackage */, false /* isShell */); } } /** * Runs the provided action with the calling package granted carrier privileges. * *
This variant of the method does NOT acquire shell identity to prevent overriding current
* shell permissions. The caller is expected to hold the READ_PRIVILEGED_PHONE_STATE permission.
*/
public static void withCarrierPrivilegesForShell(Context c, int subId, ThrowingRunnable action)
throws Exception {
try (NestedCallChecker checker = new NestedCallChecker()) {
changeCarrierPrivileges(
c,
subId,
true /* gainCarrierPrivileges */,
false /* overrideCarrierServicePackage */,
true /* isShell */);
action.run();
} finally {
changeCarrierPrivileges(
c,
subId,
false /* gainCarrierPrivileges */,
false /* overrideCarrierServicePackage */,
true /* isShell */);
}
}
/** Runs the provided action with the calling package granted carrier privileges. */
public static This will also run the action with carrier privileges, which is a necessary condition to
* be a carrier service.
*/
public static void asCarrierService(Context c, int subId, ThrowingRunnable action)
throws Exception {
try (NestedCallChecker checker = new NestedCallChecker()) {
changeCarrierPrivileges(
c,
subId,
true /* gainCarrierPrivileges */,
true /* overrideCarrierServicePackage */,
false /* isShell */);
action.run();
} finally {
changeCarrierPrivileges(
c,
subId,
false /* gainCarrierPrivileges */,
false /* overrideCarrierServicePackage */,
false /* isShell */);
}
}
/**
* Runs the provided action with the calling package set as the Carrier Service.
*
* This will also run the action with carrier privileges, which is a necessary condition to
* be a carrier service.
*
* This variant of the method does NOT acquire shell identity to prevent overriding current
* shell permissions. The caller is expected to hold the READ_PRIVILEGED_PHONE_STATE permission.
*/
public static void asCarrierServiceForShell(Context c, int subId, ThrowingRunnable action)
throws Exception {
try (NestedCallChecker checker = new NestedCallChecker()) {
changeCarrierPrivileges(
c,
subId,
true /* gainCarrierPrivileges */,
true /* overrideCarrierServicePackage */,
true /* isShell */);
action.run();
} finally {
changeCarrierPrivileges(
c,
subId,
false /* gainCarrierPrivileges */,
false /* overrideCarrierServicePackage */,
true /* isShell */);
}
}
/**
* Runs the provided action with the calling package set as the Carrier Service.
*
* This will also run the action with carrier privileges, which is a necessary condition to
* be a carrier service.
*/
public static