diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 9a491bc382809..95117862a8da8 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -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(); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index ede7ee4b9a6b7..90a94ee76085b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -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)}. + * + *

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 diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 1867a6d879c72..bae99b85d6b83 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -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 diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 60d11d7e6f4b9..02222cc6f963d 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -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, diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index e2ba4d5f4aa8d..213961c1e1d93 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -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); diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index c16701ba0f351..da75e04b77245 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -443,7 +443,7 @@ public class PhoneStateListener { * Callback invoked when device call state changes. * @param state call state * @param phoneNumber call phone number. If application does not have - * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission or carrier + * {@link android.Manifest.permission#READ_CALL_LOG READ_CALL_LOG} permission or carrier * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be * passed as an argument. * diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index c850004a54d8c..ff135d59fc538 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -334,10 +334,12 @@ public class TelephonyManager { * *

* The {@link #EXTRA_STATE} extra indicates the new call state. - * If the new state is RINGING, a second extra - * {@link #EXTRA_INCOMING_NUMBER} provides the incoming phone number as - * a String. - * + * If a receiving app has {@link android.Manifest.permission#READ_CALL_LOG} permission, a second + * extra {@link #EXTRA_INCOMING_NUMBER} provides the phone number for incoming and outoing calls + * as a String. Note: If the receiving app has + * {@link android.Manifest.permission#READ_CALL_LOG} and + * {@link android.Manifest.permission#READ_PHONE_STATE} permission, it will receive the + * broadcast twice; one with the phone number and another without it. *

* This was a {@link android.content.Context#sendStickyBroadcast sticky} * broadcast in version 1.0, but it is no longer sticky. diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java index a182f2be421fc..bbe38b7f709a9 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java @@ -15,6 +15,9 @@ */ package com.android.internal.telephony; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import android.Manifest; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; @@ -75,7 +78,7 @@ public final class TelephonyPermissions { /** * Check whether the app with the given pid/uid can read phone state. * - *

This method behaves in one of the following ways: + *

This method behaves in one of the following ways: *