Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8cd8da30 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topics "voicemail-perms", "telman-perms"

* changes:
  Allow carrier-privileged apps to access voicemail provider.
  Allow carrier privileged apps to access Telephony/Subscription APIs.
parents ffb06805 5861cd58
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -49,7 +49,8 @@ import java.util.List;
 * </ul>
 * </ul>
 *
 *
 * <P> The minimum permission needed to access this content provider is
 * <P> The minimum permission needed to access this content provider is
 * {@link android.Manifest.permission#ADD_VOICEMAIL}
 * {@link android.Manifest.permission#ADD_VOICEMAIL} or carrier privileges (see
 * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
 *
 *
 * <P>Voicemails are inserted by what is called as a "voicemail source"
 * <P>Voicemails are inserted by what is called as a "voicemail source"
 * application, which is responsible for syncing voicemail data between a remote
 * application, which is responsible for syncing voicemail data between a remote
+8 −4
Original line number Original line Diff line number Diff line
@@ -394,8 +394,10 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
                + " callback.asBinder=" + callback.asBinder());
                + " callback.asBinder=" + callback.asBinder());
        }
        }


        // TODO(b/70041899): Find a way to make this work for carrier-privileged callers.
        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
                mContext, callingPackage, "addOnSubscriptionsChangedListener")) {
                mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
                "addOnSubscriptionsChangedListener")) {
            return;
            return;
        }
        }


@@ -686,8 +688,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {


    private boolean canReadPhoneState(String callingPackage, String message) {
    private boolean canReadPhoneState(String callingPackage, String message) {
        try {
        try {
            // TODO(b/70041899): Find a way to make this work for carrier-privileged callers.
            return TelephonyPermissions.checkCallingOrSelfReadPhoneState(
            return TelephonyPermissions.checkCallingOrSelfReadPhoneState(
                    mContext, callingPackage, message);
                    mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, message);
        } catch (SecurityException e) {
        } catch (SecurityException e) {
            return false;
            return false;
        }
        }
@@ -1735,8 +1738,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
        }
        }


        if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
        if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
            if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
            // TODO(b/70041899): Find a way to make this work for carrier-privileged callers.
                    mContext, callingPackage, message)) {
            if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext,
                    SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, message)) {
                return false;
                return false;
            }
            }
        }
        }
+31 −4
Original line number Original line Diff line number Diff line
@@ -25,6 +25,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressAutoDoc;
import android.annotation.SystemApi;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.SystemService;
import android.app.BroadcastOptions;
import android.app.BroadcastOptions;
@@ -42,7 +43,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.Message;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.DisplayMetrics;
import android.util.DisplayMetrics;


import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -59,9 +59,6 @@ import java.util.concurrent.TimeUnit;
/**
/**
 * SubscriptionManager is the application interface to SubscriptionController
 * SubscriptionManager is the application interface to SubscriptionController
 * and provides information about the current Telephony Subscriptions.
 * and provides information about the current Telephony Subscriptions.
 * <p>
 * All SDK public methods require android.Manifest.permission.READ_PHONE_STATE unless otherwise
 * specified.
 */
 */
@SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
@SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
public class SubscriptionManager {
public class SubscriptionManager {
@@ -612,6 +609,8 @@ public class SubscriptionManager {
     * @param listener an instance of {@link OnSubscriptionsChangedListener} with
     * @param listener an instance of {@link OnSubscriptionsChangedListener} with
     *                 onSubscriptionsChanged overridden.
     *                 onSubscriptionsChanged overridden.
     */
     */
    // TODO(b/70041899): Find a way to extend this to carrier-privileged apps.
    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
    public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
    public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
        String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
        String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
        if (DBG) {
        if (DBG) {
@@ -660,9 +659,15 @@ public class SubscriptionManager {
    /**
    /**
     * Get the active SubscriptionInfo with the input subId.
     * Get the active SubscriptionInfo with the input subId.
     *
     *
     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
     * or that the calling app has carrier privileges (see
     * {@link TelephonyManager#hasCarrierPrivileges}).
     *
     * @param subId The unique SubscriptionInfo key in database.
     * @param subId The unique SubscriptionInfo key in database.
     * @return SubscriptionInfo, maybe null if its not active.
     * @return SubscriptionInfo, maybe null if its not active.
     */
     */
    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
    public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
    public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
        if (VDBG) logd("[getActiveSubscriptionInfo]+ subId=" + subId);
        if (VDBG) logd("[getActiveSubscriptionInfo]+ subId=" + subId);
        if (!isValidSubscriptionId(subId)) {
        if (!isValidSubscriptionId(subId)) {
@@ -716,9 +721,16 @@ public class SubscriptionManager {


    /**
    /**
     * Get the active SubscriptionInfo associated with the slotIndex
     * Get the active SubscriptionInfo associated with the slotIndex
     *
     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
     * or that the calling app has carrier privileges (see
     * {@link TelephonyManager#hasCarrierPrivileges}).
     *
     * @param slotIndex the slot which the subscription is inserted
     * @param slotIndex the slot which the subscription is inserted
     * @return SubscriptionInfo, maybe null if its not active
     * @return SubscriptionInfo, maybe null if its not active
     */
     */
    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
    public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex) {
    public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex) {
        if (VDBG) logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex);
        if (VDBG) logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex);
        if (!isValidSlotIndex(slotIndex)) {
        if (!isValidSlotIndex(slotIndex)) {
@@ -770,6 +782,11 @@ public class SubscriptionManager {
     * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted
     * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted
     * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
     * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
     *
     *
     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
     * or that the calling app has carrier privileges (see
     * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, only records accessible
     * to the calling app are returned.
     *
     * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
     * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
     * <ul>
     * <ul>
     * <li>
     * <li>
@@ -786,6 +803,8 @@ public class SubscriptionManager {
     * </li>
     * </li>
     * </ul>
     * </ul>
     */
     */
    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
    public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
    public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
        List<SubscriptionInfo> result = null;
        List<SubscriptionInfo> result = null;


@@ -928,10 +947,18 @@ public class SubscriptionManager {
    }
    }


    /**
    /**
     *
     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
     * or that the calling app has carrier privileges (see
     * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, the count will include
     * only those subscriptions accessible to the caller.
     *
     * @return the current number of active subscriptions. There is no guarantee the value
     * @return the current number of active subscriptions. There is no guarantee the value
     * returned by this method will be the same as the length of the list returned by
     * returned by this method will be the same as the length of the list returned by
     * {@link #getActiveSubscriptionInfoList}.
     * {@link #getActiveSubscriptionInfoList}.
     */
     */
    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
    public int getActiveSubscriptionInfoCount() {
    public int getActiveSubscriptionInfoCount() {
        int result = 0;
        int result = 0;


+197 −147

File changed.

Preview size limit exceeded, changes collapsed.

+64 −19
Original line number Original line Diff line number Diff line
@@ -26,7 +26,8 @@ import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.ITelephony;

import java.util.function.Supplier;


/** Utility class for Telephony permission enforcement. */
/** Utility class for Telephony permission enforcement. */
public final class TelephonyPermissions {
public final class TelephonyPermissions {
@@ -34,6 +35,9 @@ public final class TelephonyPermissions {


    private static final boolean DBG = false;
    private static final boolean DBG = false;


    private static final Supplier<ITelephony> TELEPHONY_SUPPLIER = () ->
            ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));

    private TelephonyPermissions() {}
    private TelephonyPermissions() {}


    /**
    /**
@@ -41,8 +45,8 @@ public final class TelephonyPermissions {
     *
     *
     * <p>This method behaves in one of the following ways:
     * <p>This method behaves in one of the following ways:
     * <ul>
     * <ul>
     *   <li>return true: if the caller has either the READ_PRIVILEGED_PHONE_STATE permission or the
     *   <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the
     *       READ_PHONE_STATE runtime permission.
     *       READ_PHONE_STATE runtime permission, or carrier privileges on the given subId.
     *   <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
     *   <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
     *       apps which support runtime permissions, if the caller does not currently have any of
     *       apps which support runtime permissions, if the caller does not currently have any of
     *       these permissions.
     *       these permissions.
@@ -51,10 +55,20 @@ public final class TelephonyPermissions {
     *       manually (via AppOps). In this case we can't throw as it would break app compatibility,
     *       manually (via AppOps). In this case we can't throw as it would break app compatibility,
     *       so we return false to indicate that the calling function should return dummy data.
     *       so we return false to indicate that the calling function should return dummy data.
     * </ul>
     * </ul>
     *
     * <p>Note: for simplicity, this method always returns false for callers using legacy
     * permissions and who have had READ_PHONE_STATE revoked, even if they are carrier-privileged.
     * Such apps should migrate to runtime permissions or stop requiring READ_PHONE_STATE on P+
     * devices.
     *
     * @param subId the subId of the relevant subscription; used to check carrier privileges. May be
     *              {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} to skip this check for cases
     *              where it isn't relevant (hidden APIs, or APIs which are otherwise okay to leave
     *              inaccesible to carrier-privileged apps).
     */
     */
    public static boolean checkCallingOrSelfReadPhoneState(
    public static boolean checkCallingOrSelfReadPhoneState(
            Context context, String callingPackage, String message) {
            Context context, int subId, String callingPackage, String message) {
        return checkReadPhoneState(context, Binder.getCallingPid(), Binder.getCallingUid(),
        return checkReadPhoneState(context, subId, Binder.getCallingPid(), Binder.getCallingUid(),
                callingPackage, message);
                callingPackage, message);
    }
    }


@@ -63,8 +77,8 @@ public final class TelephonyPermissions {
     *
     *
    * <p>This method behaves in one of the following ways:
    * <p>This method behaves in one of the following ways:
     * <ul>
     * <ul>
     *   <li>return true: if the caller has either the READ_PRIVILEGED_PHONE_STATE permission or the
     *   <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the
     *       READ_PHONE_STATE runtime permission.
     *       READ_PHONE_STATE runtime permission, or carrier privileges on the given subId.
     *   <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
     *   <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
     *       apps which support runtime permissions, if the caller does not currently have any of
     *       apps which support runtime permissions, if the caller does not currently have any of
     *       these permissions.
     *       these permissions.
@@ -73,9 +87,22 @@ public final class TelephonyPermissions {
     *       manually (via AppOps). In this case we can't throw as it would break app compatibility,
     *       manually (via AppOps). In this case we can't throw as it would break app compatibility,
     *       so we return false to indicate that the calling function should return dummy data.
     *       so we return false to indicate that the calling function should return dummy data.
     * </ul>
     * </ul>
     *
     * <p>Note: for simplicity, this method always returns false for callers using legacy
     * permissions and who have had READ_PHONE_STATE revoked, even if they are carrier-privileged.
     * Such apps should migrate to runtime permissions or stop requiring READ_PHONE_STATE on P+
     * devices.
     */
     */
    public static boolean checkReadPhoneState(
    public static boolean checkReadPhoneState(
            Context context, int pid, int uid, String callingPackage, String message) {
            Context context, int subId, int pid, int uid, String callingPackage, String message) {
        return checkReadPhoneState(
                context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage, message);
    }

    @VisibleForTesting
    public static boolean checkReadPhoneState(
            Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
            String callingPackage, String message) {
        try {
        try {
            context.enforcePermission(
            context.enforcePermission(
                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message);
                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message);
@@ -83,8 +110,18 @@ public final class TelephonyPermissions {
            // SKIP checking for run-time permission since caller has PRIVILEGED permission
            // SKIP checking for run-time permission since caller has PRIVILEGED permission
            return true;
            return true;
        } catch (SecurityException privilegedPhoneStateException) {
        } catch (SecurityException privilegedPhoneStateException) {
            try {
                context.enforcePermission(
                context.enforcePermission(
                        android.Manifest.permission.READ_PHONE_STATE, pid, uid, message);
                        android.Manifest.permission.READ_PHONE_STATE, pid, uid, message);
            } catch (SecurityException phoneStateException) {
                // If we don't have the runtime permission, but do have carrier privileges, that
                // suffices for reading phone state.
                if (SubscriptionManager.isValidSubscriptionId(subId)) {
                    enforceCarrierPrivilege(telephonySupplier, subId, uid, message);
                    return true;
                }
                throw phoneStateException;
            }
        }
        }


        // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
        // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
@@ -101,14 +138,16 @@ public final class TelephonyPermissions {
     * default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS can also read phone numbers.
     * default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS can also read phone numbers.
     */
     */
    public static boolean checkCallingOrSelfReadPhoneNumber(
    public static boolean checkCallingOrSelfReadPhoneNumber(
            Context context, String callingPackage, String message) {
            Context context, int subId, String callingPackage, String message) {
        return checkReadPhoneNumber(
        return checkReadPhoneNumber(
                context, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, message);
                context, TELEPHONY_SUPPLIER, subId, Binder.getCallingPid(), Binder.getCallingUid(),
                callingPackage, message);
    }
    }


    @VisibleForTesting
    @VisibleForTesting
    public static boolean checkReadPhoneNumber(
    public static boolean checkReadPhoneNumber(
            Context context, int pid, int uid, String callingPackage, String message) {
            Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
            String callingPackage, String message) {
        // Default SMS app can always read it.
        // Default SMS app can always read it.
        AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        if (appOps.noteOp(AppOpsManager.OP_WRITE_SMS, uid, callingPackage) ==
        if (appOps.noteOp(AppOpsManager.OP_WRITE_SMS, uid, callingPackage) ==
@@ -121,7 +160,8 @@ public final class TelephonyPermissions {


        // First, check if we can read the phone state.
        // First, check if we can read the phone state.
        try {
        try {
            return checkReadPhoneState(context, pid, uid, callingPackage, message);
            return checkReadPhoneState(
                    context, telephonySupplier, subId, pid, uid, callingPackage, message);
        } catch (SecurityException readPhoneStateSecurityException) {
        } catch (SecurityException readPhoneStateSecurityException) {
        }
        }
        // Can be read with READ_SMS too.
        // Can be read with READ_SMS too.
@@ -186,16 +226,21 @@ public final class TelephonyPermissions {
    }
    }


    private static void enforceCarrierPrivilege(int subId, int uid, String message) {
    private static void enforceCarrierPrivilege(int subId, int uid, String message) {
        if (getCarrierPrivilegeStatus(subId, uid) !=
        enforceCarrierPrivilege(TELEPHONY_SUPPLIER, subId, uid, message);
    }

    private static void enforceCarrierPrivilege(
            Supplier<ITelephony> telephonySupplier, int subId, int uid, String message) {
        if (getCarrierPrivilegeStatus(telephonySupplier, subId, uid) !=
                TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
                TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
            if (DBG) Rlog.e(LOG_TAG, "No Carrier Privilege.");
            if (DBG) Rlog.e(LOG_TAG, "No Carrier Privilege.");
            throw new SecurityException(message);
            throw new SecurityException(message);
        }
        }
    }
    }


    private static int getCarrierPrivilegeStatus(int subId, int uid) {
    private static int getCarrierPrivilegeStatus(
        ITelephony telephony =
            Supplier<ITelephony> telephonySupplier, int subId, int uid) {
                ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
        ITelephony telephony = telephonySupplier.get();
        try {
        try {
            if (telephony != null) {
            if (telephony != null) {
                return telephony.getCarrierPrivilegeStatusForUid(subId, uid);
                return telephony.getCarrierPrivilegeStatusForUid(subId, uid);