Enforce READ_PHONE_STATE for APIs involving call state

For API version 31+, ensure that READ_PHONE_STATE is checked on
APIs that retrieve/notify the call state of the device.

Bug: 157233955
Test: atest CtsTelecomTestCases2 CtsTelephony2TestCases
Change-Id: I9f8674a3075d3e0f75ee4f41eefce328c0fa6b91
This commit is contained in:
Brad Ebinger
2021-03-23 21:01:51 +00:00
parent 1d70937fe7
commit a8366aeae4
9 changed files with 122 additions and 20 deletions

View File

@@ -40190,6 +40190,7 @@ package android.telecom {
field public static final int DURATION_MEDIUM = 2; // 0x2
field public static final int DURATION_SHORT = 1; // 0x1
field public static final int DURATION_VERY_SHORT = 0; // 0x0
field public static final long ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION = 157233955L; // 0x95f3323L
field public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
field public static final String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
@@ -41796,7 +41797,7 @@ package android.telephony {
method @Deprecated public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
method @Deprecated public void onCallForwardingIndicatorChanged(boolean);
method @Deprecated public void onCallStateChanged(int, String);
method @Deprecated @RequiresPermission(value=android.Manifest.permission.READ_PHONE_STATE, conditional=true) public void onCallStateChanged(int, String);
method @Deprecated public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
method @Deprecated public void onCellLocationChanged(android.telephony.CellLocation);
method @Deprecated public void onDataActivity(int);
@@ -42342,7 +42343,7 @@ package android.telephony {
}
public static interface TelephonyCallback.CallStateListener {
method public void onCallStateChanged(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallStateChanged(int);
}
public static interface TelephonyCallback.CarrierNetworkListener {
@@ -42432,7 +42433,8 @@ package android.telephony {
method public int getActiveModemCount();
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCallComposerStatus();
method public int getCallState();
method @Deprecated @RequiresPermission(value=android.Manifest.permission.READ_PHONE_STATE, conditional=true) public int getCallState();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getCallStateForSubscription();
method public int getCardIdForDefaultEuicc();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig();
method public int getCarrierIdFromSimMccMnc();

View File

@@ -10880,7 +10880,7 @@ package android.telecom {
method public java.util.List<android.telecom.PhoneAccount> getAllPhoneAccounts();
method public int getAllPhoneAccountsCount();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean);
method public int getCallState();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}, conditional=true) public int getCallState();
method public android.telecom.PhoneAccountHandle getConnectionManager();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCurrentTtyMode();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultDialerPackage(@NonNull android.os.UserHandle);

View File

@@ -703,6 +703,10 @@ public class PhoneStateListener {
* calling {@link TelephonyManager#getCallState()} from within this callback may return a
* different state than the callback reports.
*
* Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
* targeting API level 31+.
*
* @param state call state
* @param phoneNumber call phone number. If application does not have
* {@link android.Manifest.permission#READ_CALL_LOG READ_CALL_LOG} permission or carrier
@@ -712,6 +716,7 @@ public class PhoneStateListener {
* @deprecated Use {@link TelephonyCallback.CallStateListener} instead.
*/
@Deprecated
@RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
public void onCallStateChanged(@CallState int state, String phoneNumber) {
// default implementation empty
}

View File

@@ -752,6 +752,7 @@ public class TelephonyCallback {
*
* @param state the current call state
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public void onCallStateChanged(@Annotation.CallState int state);
}

View File

@@ -44,6 +44,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.telecom.TelecomManager;
import android.telephony.Annotation;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SrvccState;
@@ -215,6 +216,18 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
TelephonyCallback.PHONE_STATE_LISTENER_LIMIT_CHANGE_ID, uid));
}
/**
* See {@link TelecomManager#ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION} for more
* information.
* @noinspection ConstantConditions
*/
public boolean isCallStateReadPhoneStateEnforcedInPlatformCompat(String packageName,
UserHandle userHandle) {
return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION, packageName,
userHandle));
}
}
private final Context mContext;
@@ -2947,6 +2960,19 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
// Only check READ_PHONE_STATE for CALL_STATE_CHANGED for API 31+.
if (mConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(callingPackage,
Binder.getCallingUserHandle())) {
if (events.contains(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED)
|| events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED)) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
mContext, subId, callingPackage, callingFeatureId, message)) {
throw new SecurityException("CALL_STATE_CHANGED event requires "
+ "READ_PHONE_STATE");
}
}
}
if (isPrecisePhoneStatePermissionRequired(events)) {
// check if calling app has either permission READ_PRECISE_PHONE_STATE
// or with carrier privileges

View File

@@ -25,6 +25,8 @@ import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -1004,6 +1006,17 @@ public class TelecomManager {
PRESENTATION_PAYPHONE})
public @interface Presentation {}
/**
* Enable READ_PHONE_STATE protection on APIs querying and notifying call state, such as
* {@code TelecomManager#getCallState}, {@link TelephonyManager#getCallStateForSubscription()},
* and {@link android.telephony.TelephonyCallback.CallStateListener}.
*/
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
// this magic number is a bug ID
public static final long ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION = 157233955L;
private static final String TAG = "TelecomManager";
@@ -1758,21 +1771,23 @@ public class TelecomManager {
* {@link TelephonyManager#CALL_STATE_OFFHOOK}
* {@link TelephonyManager#CALL_STATE_IDLE}
*
* Note that this API does not require the
* {@link android.Manifest.permission#READ_PHONE_STATE} permission. This is intentional, to
* preserve the behavior of {@link TelephonyManager#getCallState()}, which also did not require
* the permission.
*
* Takes into consideration both managed and self-managed calls.
* <p>
* Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
* targeting API level 31+.
*
* @hide
*/
@RequiresPermission(anyOf = {READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PHONE_STATE}, conditional = true)
@SystemApi
public @CallState int getCallState() {
ITelecomService service = getTelecomService();
if (service != null) {
try {
return service.getCallState();
return service.getCallStateUsingPackage(mContext.getPackageName(),
mContext.getAttributionTag());
} catch (RemoteException e) {
Log.d(TAG, "RemoteException calling getCallState().", e);
}

View File

@@ -195,10 +195,17 @@ interface ITelecomService {
/**
* @see TelecomServiceImpl#getCallState
* Note: only kept around to not break app compat, however this will throw a SecurityException
* on API 31+.
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
int getCallState();
/**
* @see TelecomServiceImpl#getCallState
*/
int getCallStateUsingPackage(String callingPackage, String callingFeatureId);
/**
* @see TelecomServiceImpl#endCall
*/

View File

@@ -5702,9 +5702,20 @@ public class TelephonyManager {
* Note: The call state returned via this method may differ from what is reported by
* {@link PhoneStateListener#onCallStateChanged(int, String)}, as that callback only considers
* Telephony (mobile) calls.
* <p>
* Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
* targeting API level 31+.
*
* @return the current call state.
* @deprecated Use {@link #getCallStateForSubscription} to retrieve the call state for a
* specific telephony subscription (which allows carrier privileged apps),
* {@link TelephonyCallback.CallStateListener} for real-time call state updates, or
* {@link TelecomManager#isInCall()}, which supplies an aggregate "in call" state for the entire
* device.
*/
@RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
@Deprecated
public @CallState int getCallState() {
if (mContext != null) {
TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
@@ -5715,20 +5726,49 @@ public class TelephonyManager {
return CALL_STATE_IDLE;
}
/**
* Retrieve the call state for a specific subscription that was specified when this
* TelephonyManager instance was created.
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or that the calling
* application has carrier privileges (see {@link #hasCarrierPrivileges}).
* @see TelephonyManager#createForSubscriptionId(int)
* @see TelephonyManager#createForPhoneAccountHandle(PhoneAccountHandle)
* @return The call state of the subscription associated with this TelephonyManager instance.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public @CallState int getCallStateForSubscription() {
return getCallState(getSubId());
}
/**
* Returns the Telephony call state for calls on a specific subscription.
* <p>
* Note: This method considers ONLY telephony/mobile calls, where {@link #getCallState()}
* considers the state of calls from other {@link android.telecom.ConnectionService}
* implementations.
* <p>
* Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
* targeting API level 31+ or that the calling application has carrier privileges
* (see {@link #hasCarrierPrivileges()}).
*
* @param subId the subscription to check call state for.
* @hide
*/
@UnsupportedAppUsage
@RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
public @CallState int getCallState(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
return getCallStateForSlot(phoneId);
ITelephony telephony = getITelephony();
if (telephony == null) {
return CALL_STATE_IDLE;
}
try {
return telephony.getCallStateForSubscription(subId, mContext.getPackageName(),
mContext.getAttributionTag());
} catch (RemoteException e) {
return CALL_STATE_IDLE;
}
}
/**
@@ -5745,22 +5785,28 @@ public class TelephonyManager {
* Note: This method considers ONLY telephony/mobile calls, where {@link #getCallState()}
* considers the state of calls from other {@link android.telecom.ConnectionService}
* implementations.
* <p>
* Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
* targeting API level 31+ or that the calling application has carrier privileges
* (see {@link #hasCarrierPrivileges()}).
*
* @param slotIndex the SIM slot index to check call state for.
* @hide
*/
@RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
public @CallState int getCallStateForSlot(int slotIndex) {
try {
int[] subId = SubscriptionManager.getSubId(slotIndex);
ITelephony telephony = getITelephony();
if (telephony == null)
if (telephony == null || subId == null || subId.length == 0) {
return CALL_STATE_IDLE;
return telephony.getCallStateForSlot(slotIndex);
} catch (RemoteException ex) {
}
return telephony.getCallStateForSubscription(subId[0], mContext.getPackageName(),
mContext.getAttributionTag());
} catch (RemoteException | NullPointerException ex) {
// the phone process is restarting.
return CALL_STATE_IDLE;
} catch (NullPointerException ex) {
// the phone process is restarting.
return CALL_STATE_IDLE;
}
}

View File

@@ -298,9 +298,9 @@ interface ITelephony {
int getCallState();
/**
* Returns the call state for a slot.
* Returns the call state for a specific subscriiption.
*/
int getCallStateForSlot(int slotIndex);
int getCallStateForSubscription(int subId, String callingPackage, String featureId);
/**
* Replaced by getDataActivityForSubId.