Merge "Require READ_CALL_LOG permission to see phone numbers in phone state." into pi-dev

This commit is contained in:
Tyler Gunn
2018-05-01 20:27:15 +00:00
committed by Android (Google) Code Review
10 changed files with 135 additions and 21 deletions

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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);

View File

@@ -444,7 +444,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.
*

View File

@@ -334,10 +334,12 @@ public class TelephonyManager {
*
* <p>
* 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.
* <p class="note">
* This was a {@link android.content.Context#sendStickyBroadcast sticky}
* broadcast in version 1.0, but it is no longer sticky.

View File

@@ -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.
*
* <p>This method behaves in one of the following ways:
* <p>This method behaves in one of the following ways:
* <ul>
* <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the
* READ_PHONE_STATE runtime permission, or carrier privileges on the given subId.
@@ -131,6 +134,40 @@ public final class TelephonyPermissions {
AppOpsManager.MODE_ALLOWED;
}
/**
* Check whether the app with the given pid/uid can read the call log.
* @return {@code true} if the specified app has the read call log permission and AppOpp granted
* to it, {@code false} otherwise.
*/
public static boolean checkReadCallLog(
Context context, int subId, int pid, int uid, String callingPackage) {
return checkReadCallLog(
context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage);
}
@VisibleForTesting
public static boolean checkReadCallLog(
Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
String callingPackage) {
if (context.checkPermission(Manifest.permission.READ_CALL_LOG, pid, uid)
!= PERMISSION_GRANTED) {
// If we don't have the runtime permission, but do have carrier privileges, that
// suffices for being able to see the call phone numbers.
if (SubscriptionManager.isValidSubscriptionId(subId)) {
enforceCarrierPrivilege(telephonySupplier, subId, uid, "readCallLog");
return true;
}
return false;
}
// We have READ_CALL_LOG 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_CALL_LOG, uid, callingPackage) ==
AppOpsManager.MODE_ALLOWED;
}
/**
* Returns whether the caller can read phone numbers.
*
@@ -204,7 +241,7 @@ public final class TelephonyPermissions {
public static void enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
Context context, int subId, String message) {
if (context.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) ==
PackageManager.PERMISSION_GRANTED) {
PERMISSION_GRANTED) {
return;
}

View File

@@ -363,6 +363,13 @@ public class MockContext extends Context {
throw new UnsupportedOperationException();
}
/** @hide */
@Override
public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
String[] receiverPermissions) {
throw new UnsupportedOperationException();
}
/** @hide */
@SystemApi
@Override

View File

@@ -174,6 +174,12 @@ public class BroadcastInterceptingContext extends ContextWrapper {
sendBroadcast(intent);
}
@Override
public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
String[] receiverPermissions) {
sendBroadcast(intent);
}
@Override
public void sendBroadcastAsUser(Intent intent, UserHandle user) {
sendBroadcast(intent);