Loading services/core/java/com/android/server/TelephonyRegistry.java +21 −7 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.stream.Collectors; /** * Since phone process can be restarted, this class provides a centralized place Loading Loading @@ -260,8 +261,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { static final int ENFORCE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST; static final int PRECISE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_PRECISE_CALL_STATE | Loading Loading @@ -822,7 +822,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if ((events & PhoneStateListener .LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) { .LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0 && TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub( r.context, r.callerPid, r.callerUid, r.callingPackage, "listen_active_data_subid_change")) { try { r.callback.onActiveDataSubIdChanged(mActiveDataSubId); } catch (RemoteException ex) { Loading Loading @@ -1753,12 +1756,23 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyActiveDataSubIdChanged: activeDataSubId=" + activeDataSubId); } // Create a copy to prevent the IPC call while checking carrier privilege under the lock. List<Record> copiedRecords; synchronized (mRecords) { copiedRecords = new ArrayList<>(mRecords); } mActiveDataSubId = activeDataSubId; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)) { // Filter the record that does not listen to this change or does not have the permission. copiedRecords = copiedRecords.stream().filter(r -> r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) && TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub( mContext, r.callerPid, r.callerUid, r.callingPackage, "notifyActiveDataSubIdChanged")).collect(Collectors.toCollection(ArrayList::new)); synchronized (mRecords) { for (Record r : copiedRecords) { if (mRecords.contains(r)) { try { r.callback.onActiveDataSubIdChanged(activeDataSubId); } catch (RemoteException ex) { Loading telephony/java/android/telephony/PhoneStateListener.java +5 −2 Original line number Diff line number Diff line Loading @@ -297,8 +297,11 @@ public class PhoneStateListener { * it could be the current active opportunistic subscription in use, or the * subscription user selected as default data subscription in DSDS mode. * * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE * READ_PHONE_STATE} * Requires Permission: No permission is required to listen, but notification requires * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or the calling * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}) * on any active subscription. * * @see #onActiveDataSubscriptionIdChanged */ public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000; Loading telephony/java/com/android/internal/telephony/TelephonyPermissions.java +57 −0 Original line number Diff line number Diff line Loading @@ -127,6 +127,63 @@ public final class TelephonyPermissions { } } // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been // revoked. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); return appOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, uid, callingPackage) == AppOpsManager.MODE_ALLOWED; } /** * Check whether the app with the given pid/uid can read phone state, or has carrier * privileges on any active subscription. * * <p>If the app does not have carrier privilege, this method will return {@code false} instead * of throwing a SecurityException. Therefore, the callers cannot tell the difference * between M+ apps which declare the runtime permission but do not have it, and pre-M apps * which declare the static permission but had access revoked via AppOps. Apps in the former * category expect SecurityExceptions; apps in the latter don't. So this method is suitable for * use only if the behavior in both scenarios is meant to be identical. * * @return {@code true} if the app can read phone state or has carrier privilege; * {@code false} otherwise. */ public static boolean checkReadPhoneStateOnAnyActiveSub( Context context, int pid, int uid, String callingPackage, String message) { return checkReadPhoneStateOnAnyActiveSub(context, TELEPHONY_SUPPLIER, pid, uid, callingPackage, message); } @VisibleForTesting public static boolean checkReadPhoneStateOnAnyActiveSub( Context context, Supplier<ITelephony> telephonySupplier, int pid, int uid, String callingPackage, String message) { try { context.enforcePermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message); // SKIP checking for run-time permission since caller has PRIVILEGED permission return true; } catch (SecurityException privilegedPhoneStateException) { try { context.enforcePermission( android.Manifest.permission.READ_PHONE_STATE, pid, uid, message); } catch (SecurityException phoneStateException) { SubscriptionManager sm = (SubscriptionManager) context.getSystemService( Context.TELEPHONY_SUBSCRIPTION_SERVICE); int[] activeSubIds = sm.getActiveSubscriptionIdList(); for (int activeSubId : activeSubIds) { // If we don't have the runtime permission, but do have carrier privileges, that // suffices for reading phone state. if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { return true; } } return false; } } // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been // revoked. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); Loading Loading
services/core/java/com/android/server/TelephonyRegistry.java +21 −7 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.stream.Collectors; /** * Since phone process can be restarted, this class provides a centralized place Loading Loading @@ -260,8 +261,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { static final int ENFORCE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST; static final int PRECISE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_PRECISE_CALL_STATE | Loading Loading @@ -822,7 +822,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if ((events & PhoneStateListener .LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) { .LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0 && TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub( r.context, r.callerPid, r.callerUid, r.callingPackage, "listen_active_data_subid_change")) { try { r.callback.onActiveDataSubIdChanged(mActiveDataSubId); } catch (RemoteException ex) { Loading Loading @@ -1753,12 +1756,23 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyActiveDataSubIdChanged: activeDataSubId=" + activeDataSubId); } // Create a copy to prevent the IPC call while checking carrier privilege under the lock. List<Record> copiedRecords; synchronized (mRecords) { copiedRecords = new ArrayList<>(mRecords); } mActiveDataSubId = activeDataSubId; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)) { // Filter the record that does not listen to this change or does not have the permission. copiedRecords = copiedRecords.stream().filter(r -> r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) && TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub( mContext, r.callerPid, r.callerUid, r.callingPackage, "notifyActiveDataSubIdChanged")).collect(Collectors.toCollection(ArrayList::new)); synchronized (mRecords) { for (Record r : copiedRecords) { if (mRecords.contains(r)) { try { r.callback.onActiveDataSubIdChanged(activeDataSubId); } catch (RemoteException ex) { Loading
telephony/java/android/telephony/PhoneStateListener.java +5 −2 Original line number Diff line number Diff line Loading @@ -297,8 +297,11 @@ public class PhoneStateListener { * it could be the current active opportunistic subscription in use, or the * subscription user selected as default data subscription in DSDS mode. * * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE * READ_PHONE_STATE} * Requires Permission: No permission is required to listen, but notification requires * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or the calling * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}) * on any active subscription. * * @see #onActiveDataSubscriptionIdChanged */ public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000; Loading
telephony/java/com/android/internal/telephony/TelephonyPermissions.java +57 −0 Original line number Diff line number Diff line Loading @@ -127,6 +127,63 @@ public final class TelephonyPermissions { } } // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been // revoked. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); return appOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, uid, callingPackage) == AppOpsManager.MODE_ALLOWED; } /** * Check whether the app with the given pid/uid can read phone state, or has carrier * privileges on any active subscription. * * <p>If the app does not have carrier privilege, this method will return {@code false} instead * of throwing a SecurityException. Therefore, the callers cannot tell the difference * between M+ apps which declare the runtime permission but do not have it, and pre-M apps * which declare the static permission but had access revoked via AppOps. Apps in the former * category expect SecurityExceptions; apps in the latter don't. So this method is suitable for * use only if the behavior in both scenarios is meant to be identical. * * @return {@code true} if the app can read phone state or has carrier privilege; * {@code false} otherwise. */ public static boolean checkReadPhoneStateOnAnyActiveSub( Context context, int pid, int uid, String callingPackage, String message) { return checkReadPhoneStateOnAnyActiveSub(context, TELEPHONY_SUPPLIER, pid, uid, callingPackage, message); } @VisibleForTesting public static boolean checkReadPhoneStateOnAnyActiveSub( Context context, Supplier<ITelephony> telephonySupplier, int pid, int uid, String callingPackage, String message) { try { context.enforcePermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message); // SKIP checking for run-time permission since caller has PRIVILEGED permission return true; } catch (SecurityException privilegedPhoneStateException) { try { context.enforcePermission( android.Manifest.permission.READ_PHONE_STATE, pid, uid, message); } catch (SecurityException phoneStateException) { SubscriptionManager sm = (SubscriptionManager) context.getSystemService( Context.TELEPHONY_SUBSCRIPTION_SERVICE); int[] activeSubIds = sm.getActiveSubscriptionIdList(); for (int activeSubId : activeSubIds) { // If we don't have the runtime permission, but do have carrier privileges, that // suffices for reading phone state. if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { return true; } } return false; } } // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been // revoked. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); Loading