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

Commit f955e56d authored by Tyler Gunn's avatar Tyler Gunn
Browse files

Require READ_CALL_LOG permission to see phone numbers in phone state.

Incoming and outgoing call phone numbers are visible in the phone state
broadcast and via the PhoneStateListener.  To enhance user privacy, change
to require the READ_CALL_LOG permission in order to receive the call
phone numbers.

This means to see phone numbers:
1. android.intent.action.PHONE_STATE - requires READ_PHONE_STATE and
READ_CALL_LOG permission.
2. PhoneStateListener#onCallStateChanged - now required READ_CALL_LOG
permission.

To support this new behavior, added sendBroadcastAsUserMultiplePermissions
method to context to allow sending the broadcast to all users while
requiring the two permissions.

Bug: 78650469
Test: Created PHONE_STATE broadcast receiver in test app and verified that
when no permissions are granted, the phone number is empty for incoming
and outgoing calls.
Test: Granted Phone state permission to test app and verified that phone
number is not populated.
Test: Granted test app read call log permission and verified that phone
number is populated.
Test: Created PhoneStateListener in test app and verified that when no
permissions are granted, phone number is empty for incoming and outgoing.
calls.
Test: Granted read call log permission to test app and verified that both
the incoming and outgoing numbers are populated.

Change-Id: I857ea00cc58a0abbb77960643f361dd6dd9c8b56
parent fd935d3a
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -1047,6 +1047,22 @@ class ContextImpl extends Context {
        }
    }

    @Override
    public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
            String[] receiverPermissions) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            intent.prepareToLeaveProcess(this);
            ActivityManager.getService().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                    null, false, false, user.getIdentifier());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    @Override
    public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
        warnIfCallingFromSystemProcess();
+27 −0
Original line number Diff line number Diff line
@@ -1974,6 +1974,33 @@ public abstract class Context {
    public abstract void sendBroadcastMultiplePermissions(Intent intent,
            String[] receiverPermissions);

    /**
     * Broadcast the given intent to all interested BroadcastReceivers, allowing
     * an array of required permissions to be enforced.  This call is asynchronous; it returns
     * immediately, and you will continue executing while the receivers are run.  No results are
     * propagated from receivers and receivers can not abort the broadcast. If you want to allow
     * receivers to propagate results or abort the broadcast, you must send an ordered broadcast
     * using {@link #sendOrderedBroadcast(Intent, String)}.
     *
     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
     *
     * @param intent The Intent to broadcast; all receivers matching this
     *               Intent will receive the broadcast.
     * @param user The user to send the broadcast to.
     * @param receiverPermissions Array of names of permissions that a receiver must hold
     *                            in order to receive your broadcast.
     *                            If null or empty, no permissions are required.
     *
     * @see android.content.BroadcastReceiver
     * @see #registerReceiver
     * @see #sendBroadcast(Intent)
     * @see #sendOrderedBroadcast(Intent, String)
     * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
     * @hide
     */
    public abstract void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
            String[] receiverPermissions);

    /**
     * Broadcast the given intent to all interested BroadcastReceivers, allowing
     * an optional required permission to be enforced.  This
+7 −0
Original line number Diff line number Diff line
@@ -455,6 +455,13 @@ public class ContextWrapper extends Context {
        mBase.sendBroadcastMultiplePermissions(intent, receiverPermissions);
    }

    /** @hide */
    @Override
    public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
            String[] receiverPermissions) {
        mBase.sendBroadcastAsUserMultiplePermissions(intent, user, receiverPermissions);
    }

    /** @hide */
    @SystemApi
    @Override
+20 −14
Original line number Diff line number Diff line
@@ -117,10 +117,10 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
            return (onSubscriptionsChangedListenerCallback != null);
        }

        boolean canReadPhoneState() {
        boolean canReadCallLog() {
            try {
                return TelephonyPermissions.checkReadPhoneState(
                        context, subId, callerPid, callerUid, callingPackage, "listen");
                return TelephonyPermissions.checkReadCallLog(
                        context, subId, callerPid, callerUid, callingPackage);
            } catch (SecurityException e) {
                return false;
            }
@@ -667,8 +667,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
    }

    private String getCallIncomingNumber(Record record, int phoneId) {
        // Hide the number if record's process can't currently read phone state.
        return record.canReadPhoneState() ? mCallIncomingNumber[phoneId] : "";
        // Only reveal the incoming number if the record has read call log permission.
        return record.canReadCallLog() ? mCallIncomingNumber[phoneId] : "";
    }

    private Record add(IBinder binder) {
@@ -729,13 +729,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
        }
    }

    public void notifyCallState(int state, String incomingNumber) {
    public void notifyCallState(int state, String phoneNumber) {
        if (!checkNotifyPermission("notifyCallState()")) {
            return;
        }

        if (VDBG) {
            log("notifyCallState: state=" + state + " incomingNumber=" + incomingNumber);
            log("notifyCallState: state=" + state + " phoneNumber=" + phoneNumber);
        }

        synchronized (mRecords) {
@@ -743,8 +743,10 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
                if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) &&
                        (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
                    try {
                        String incomingNumberOrEmpty = r.canReadPhoneState() ? incomingNumber : "";
                        r.callback.onCallStateChanged(state, incomingNumberOrEmpty);
                        // Ensure the listener has read call log permission; if they do not return
                        // an empty phone number.
                        String phoneNumberOrEmpty = r.canReadCallLog() ? phoneNumber : "";
                        r.callback.onCallStateChanged(state, phoneNumberOrEmpty);
                    } catch (RemoteException ex) {
                        mRemoveList.add(r.binder);
                    }
@@ -755,7 +757,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {

        // Called only by Telecomm to communicate call state across different phone accounts. So
        // there is no need to add a valid subId or slotId.
        broadcastCallStateChanged(state, incomingNumber,
        broadcastCallStateChanged(state, phoneNumber,
                SubscriptionManager.INVALID_PHONE_INDEX,
                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
    }
@@ -1571,9 +1573,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
        Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
        intent.putExtra(PhoneConstants.STATE_KEY,
                PhoneConstantConversions.convertCallState(state).toString());
        if (!TextUtils.isEmpty(incomingNumber)) {
            intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
        }

        // If a valid subId was specified, we should fire off a subId-specific state
        // change intent and include the subId.
@@ -1589,13 +1588,20 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
        // Wakeup apps for the (SUBSCRIPTION_)PHONE_STATE broadcast.
        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);

        Intent intentWithPhoneNumber = new Intent(intent);
        if (!TextUtils.isEmpty(incomingNumber)) {
            intentWithPhoneNumber.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
        }
        // Send broadcast twice, once for apps that have PRIVILEGED permission and once for those
        // that have the runtime one
        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
        mContext.sendBroadcastAsUser(intentWithPhoneNumber, UserHandle.ALL,
                android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
                android.Manifest.permission.READ_PHONE_STATE,
                AppOpsManager.OP_READ_PHONE_STATE);
        mContext.sendBroadcastAsUserMultiplePermissions(intentWithPhoneNumber, UserHandle.ALL,
                new String[] { android.Manifest.permission.READ_PHONE_STATE,
                        android.Manifest.permission.READ_CALL_LOG});
    }

    private void broadcastDataConnectionStateChanged(int state,
+6 −0
Original line number Diff line number Diff line
@@ -253,6 +253,12 @@ public class DpmMockContext extends MockContext {
        spiedContext.sendBroadcastMultiplePermissions(intent, receiverPermissions);
    }

    @Override
    public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
            String[] receiverPermissions) {
        spiedContext.sendBroadcastAsUserMultiplePermissions(intent, user, receiverPermissions);
    }

    @Override
    public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
        spiedContext.sendBroadcast(intent, receiverPermission, options);
Loading