Merge "Add device identifier permission checks to TelephonyPermissions"
am: 1ca89a995b
Change-Id: I7f6d821d6596cf24960b2b136ff03b5e23ca5382
This commit is contained in:
@@ -169,6 +169,7 @@ message Atom {
|
||||
BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported = 167;
|
||||
ProcessStartTime process_start_time = 169;
|
||||
BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171;
|
||||
DeviceIdentifierAccessDenied device_identifier_access_denied = 172;
|
||||
NetworkStackReported network_stack_reported = 182 [(log_from_module) = "network_stack"];
|
||||
}
|
||||
|
||||
@@ -3098,3 +3099,22 @@ message NetworkStackReported {
|
||||
optional android.stats.connectivity.NetworkStackEventData network_stack_event = 2 [(log_mode) = MODE_BYTES];
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs when a package is denied access to a device identifier based on the new access requirements.
|
||||
*
|
||||
* Logged from:
|
||||
* frameworks/base/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
|
||||
*/
|
||||
message DeviceIdentifierAccessDenied {
|
||||
// The name of the package denied access to the requested device identifier.
|
||||
optional string package_name = 1;
|
||||
|
||||
// The name of the device identifier method the package attempted to invoke.
|
||||
optional string method_name = 2;
|
||||
|
||||
// True if the package is preinstalled.
|
||||
optional bool is_preinstalled = 3;
|
||||
|
||||
// True if the package is privileged.
|
||||
optional bool is_priv_app = 4;
|
||||
}
|
||||
@@ -5653,6 +5653,31 @@ public class DevicePolicyManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified package can read the device identifiers.
|
||||
*
|
||||
* @param packageName The package name of the app to check for device identifier access.
|
||||
* @param pid The process id of the package to be checked.
|
||||
* @param uid The uid of the package to be checked.
|
||||
* @return whether the package can read the device identifiers.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public boolean checkDeviceIdentifierAccess(String packageName, int pid, int uid) {
|
||||
throwIfParentInstance("checkDeviceIdentifierAccess");
|
||||
if (packageName == null) {
|
||||
return false;
|
||||
}
|
||||
if (mService != null) {
|
||||
try {
|
||||
return mService.checkDeviceIdentifierAccess(packageName, pid, uid);
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @return the human readable name of the organisation associated with this DPM or {@code null}
|
||||
|
||||
@@ -146,6 +146,7 @@ interface IDevicePolicyManager {
|
||||
int getDeviceOwnerUserId();
|
||||
|
||||
boolean setProfileOwner(in ComponentName who, String ownerName, int userHandle);
|
||||
ComponentName getProfileOwnerAsUser(int userHandle);
|
||||
ComponentName getProfileOwner(int userHandle);
|
||||
String getProfileOwnerName(int userHandle);
|
||||
void setProfileEnabled(in ComponentName who);
|
||||
@@ -153,6 +154,8 @@ interface IDevicePolicyManager {
|
||||
void clearProfileOwner(in ComponentName who);
|
||||
boolean hasUserSetupCompleted();
|
||||
|
||||
boolean checkDeviceIdentifierAccess(in String packageName, int pid, int uid);
|
||||
|
||||
void setDeviceOwnerLockScreenInfo(in ComponentName who, CharSequence deviceOwnerInfo);
|
||||
CharSequence getDeviceOwnerLockScreenInfo();
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package com.android.server.devicepolicy;
|
||||
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.admin.IDevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.os.PersistableBundle;
|
||||
@@ -159,4 +158,9 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
|
||||
@Override
|
||||
public void setDefaultSmsApplication(ComponentName admin, String packageName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkDeviceIdentifierAccess(String packageName, int pid, int uid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
|
||||
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
|
||||
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
|
||||
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
|
||||
|
||||
import static android.provider.Telephony.Carriers.DPC_URI;
|
||||
import static android.provider.Telephony.Carriers.ENFORCE_KEY;
|
||||
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
|
||||
@@ -69,11 +68,10 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent
|
||||
.PROVISIONING_ENTRY_POINT_ADB;
|
||||
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
|
||||
.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
|
||||
|
||||
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
|
||||
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
|
||||
|
||||
|
||||
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
|
||||
.ADMIN_TYPE_DEVICE_OWNER;
|
||||
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
|
||||
.ADMIN_TYPE_PROFILE_OWNER;
|
||||
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
|
||||
|
||||
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
|
||||
@@ -219,11 +217,11 @@ import com.android.internal.util.FastXmlSerializer;
|
||||
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
|
||||
import com.android.internal.util.JournaledFile;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.internal.util.StatLogger;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.LockGuard;
|
||||
import com.android.internal.util.StatLogger;
|
||||
import com.android.server.SystemServerInitThreadPool;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
|
||||
@@ -5188,7 +5186,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
||||
@Override
|
||||
public void enforceCanManageCaCerts(ComponentName who, String callerPackage) {
|
||||
if (who == null) {
|
||||
if (!isCallerDelegate(callerPackage, DELEGATION_CERT_INSTALL)) {
|
||||
if (!isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(),
|
||||
DELEGATION_CERT_INSTALL)) {
|
||||
mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null);
|
||||
}
|
||||
} else {
|
||||
@@ -5364,7 +5363,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
||||
if (UserHandle.getUserId(callerUid) != mOwners.getDeviceOwnerUserId()) {
|
||||
throw new SecurityException("Caller not from device owner user");
|
||||
}
|
||||
if (!isCallerDelegate(callerPackage, DELEGATION_CERT_INSTALL)) {
|
||||
if (!isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(),
|
||||
DELEGATION_CERT_INSTALL)) {
|
||||
throw new SecurityException("Caller with uid " + mInjector.binderGetCallingUid() +
|
||||
"has no permission to generate keys.");
|
||||
}
|
||||
@@ -5766,15 +5766,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
||||
* @param scope the delegation scope to be checked.
|
||||
* @return {@code true} if the calling process is a delegate of {@code scope}.
|
||||
*/
|
||||
private boolean isCallerDelegate(String callerPackage, String scope) {
|
||||
private boolean isCallerDelegate(String callerPackage, int callerUid, String scope) {
|
||||
Preconditions.checkNotNull(callerPackage, "callerPackage is null");
|
||||
if (!Arrays.asList(DELEGATIONS).contains(scope)) {
|
||||
throw new IllegalArgumentException("Unexpected delegation scope: " + scope);
|
||||
}
|
||||
|
||||
// Retrieve the UID and user ID of the calling process.
|
||||
final int callingUid = mInjector.binderGetCallingUid();
|
||||
final int userId = UserHandle.getUserId(callingUid);
|
||||
final int userId = UserHandle.getUserId(callerUid);
|
||||
synchronized (getLockObject()) {
|
||||
// Retrieve user policy data.
|
||||
final DevicePolicyData policy = getUserData(userId);
|
||||
@@ -5787,7 +5786,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
||||
final int uid = mInjector.getPackageManager()
|
||||
.getPackageUidAsUser(callerPackage, userId);
|
||||
// Return true if the caller is actually callerPackage.
|
||||
return uid == callingUid;
|
||||
return uid == callerUid;
|
||||
} catch (NameNotFoundException e) {
|
||||
// Ignore.
|
||||
}
|
||||
@@ -5818,7 +5817,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
||||
getActiveAdminForCallerLocked(who, reqPolicy);
|
||||
}
|
||||
// If no ComponentName is given ensure calling process has scope delegation.
|
||||
} else if (!isCallerDelegate(callerPackage, scope)) {
|
||||
} else if (!isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(), scope)) {
|
||||
throw new SecurityException("Caller with uid " + mInjector.binderGetCallingUid()
|
||||
+ " is not a delegate of scope " + scope + ".");
|
||||
}
|
||||
@@ -7782,6 +7781,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentName getProfileOwnerAsUser(int userHandle) {
|
||||
enforceCrossUsersPermission(userHandle);
|
||||
|
||||
return getProfileOwner(userHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentName getProfileOwner(int userHandle) {
|
||||
if (!mHasFeature) {
|
||||
@@ -7825,6 +7831,68 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
||||
return getApplicationLabel(profileOwner.getPackageName(), userHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkDeviceIdentifierAccess(String packageName, int pid, int uid) {
|
||||
// If the caller is not a system app then it should only be able to check its own device
|
||||
// identifier access.
|
||||
int callingUid = mInjector.binderGetCallingUid();
|
||||
int callingPid = mInjector.binderGetCallingPid();
|
||||
if (UserHandle.getAppId(callingUid) >= Process.FIRST_APPLICATION_UID
|
||||
&& (callingUid != uid || callingPid != pid)) {
|
||||
String message = String.format(
|
||||
"Calling uid %d, pid %d cannot check device identifier access for package %s "
|
||||
+ "(uid=%d, pid=%d)", callingUid, callingPid, packageName, uid, pid);
|
||||
Log.w(LOG_TAG, message);
|
||||
throw new SecurityException(message);
|
||||
}
|
||||
// Verify that the specified packages matches the provided uid.
|
||||
int userId = UserHandle.getUserId(uid);
|
||||
try {
|
||||
ApplicationInfo appInfo = mIPackageManager.getApplicationInfo(packageName, 0, userId);
|
||||
// Since this call goes directly to PackageManagerService a NameNotFoundException is not
|
||||
// thrown but null data can be returned; if the appInfo for the specified package cannot
|
||||
// be found then return false to prevent crashing the app.
|
||||
if (appInfo == null) {
|
||||
Log.w(LOG_TAG,
|
||||
String.format("appInfo could not be found for package %s", packageName));
|
||||
return false;
|
||||
} else if (uid != appInfo.uid) {
|
||||
String message = String.format("Package %s (uid=%d) does not match provided uid %d",
|
||||
packageName, appInfo.uid, uid);
|
||||
Log.w(LOG_TAG, message);
|
||||
throw new SecurityException(message);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
// If an exception is caught obtaining the appInfo just return false to prevent crashing
|
||||
// apps due to an internal error.
|
||||
Log.e(LOG_TAG, "Exception caught obtaining appInfo for package " + packageName, e);
|
||||
return false;
|
||||
}
|
||||
// A device or profile owner must also have the READ_PHONE_STATE permission to access device
|
||||
// identifiers. If the package being checked does not have this permission then deny access.
|
||||
if (mContext.checkPermission(android.Manifest.permission.READ_PHONE_STATE, pid, uid)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow access to the device owner or delegate cert installer.
|
||||
ComponentName deviceOwner = getDeviceOwnerComponent(true);
|
||||
if (deviceOwner != null && (deviceOwner.getPackageName().equals(packageName)
|
||||
|| isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL))) {
|
||||
return true;
|
||||
}
|
||||
// Allow access to the profile owner for the specified user, or delegate cert installer
|
||||
ComponentName profileOwner = getProfileOwnerAsUser(userId);
|
||||
if (profileOwner != null && (profileOwner.getPackageName().equals(packageName)
|
||||
|| isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Log.w(LOG_TAG, String.format("Package %s (uid=%d, pid=%d) cannot access Device IDs",
|
||||
packageName, uid, pid));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonical name for a given package.
|
||||
*/
|
||||
@@ -8266,7 +8334,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|
||||
|
||||
@Override
|
||||
public boolean isCallerApplicationRestrictionsManagingPackage(String callerPackage) {
|
||||
return isCallerDelegate(callerPackage, DELEGATION_APP_RESTRICTIONS);
|
||||
return isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(),
|
||||
DELEGATION_APP_RESTRICTIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,17 +19,28 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
import android.telephony.Rlog;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
import android.util.StatsLog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** Utility class for Telephony permission enforcement. */
|
||||
@@ -41,6 +52,20 @@ public final class TelephonyPermissions {
|
||||
private static final Supplier<ITelephony> TELEPHONY_SUPPLIER = () ->
|
||||
ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
|
||||
|
||||
/**
|
||||
* Whether to disable the new device identifier access restrictions.
|
||||
*/
|
||||
private static final String PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED =
|
||||
"device_identifier_access_restrictions_disabled";
|
||||
|
||||
// Contains a mapping of packages that did not meet the new requirements to access device
|
||||
// identifiers and the methods they were attempting to invoke; used to prevent duplicate
|
||||
// reporting of packages / methods.
|
||||
private static final Map<String, Set<String>> sReportedDeviceIDPackages;
|
||||
static {
|
||||
sReportedDeviceIDPackages = new HashMap<>();
|
||||
}
|
||||
|
||||
private TelephonyPermissions() {}
|
||||
|
||||
/**
|
||||
@@ -112,6 +137,19 @@ public final class TelephonyPermissions {
|
||||
context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the calling packages has carrier privileges for the passing subscription.
|
||||
* @return {@code true} if the caller has carrier privileges, {@false} otherwise.
|
||||
*/
|
||||
public static boolean checkCarrierPrivilegeForSubId(int subId) {
|
||||
if (SubscriptionManager.isValidSubscriptionId(subId)
|
||||
&& getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, Binder.getCallingUid())
|
||||
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static boolean checkReadPhoneState(
|
||||
Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
|
||||
@@ -179,18 +217,9 @@ public final class TelephonyPermissions {
|
||||
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;
|
||||
// If we don't have the runtime permission, but do have carrier privileges, that
|
||||
// suffices for reading phone state.
|
||||
return checkCarrierPrivilegeForAnySubId(context, telephonySupplier, uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +230,182 @@ public final class TelephonyPermissions {
|
||||
AppOpsManager.MODE_ALLOWED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the caller (or self, if not processing an IPC) can read device identifiers.
|
||||
*
|
||||
* <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 calling
|
||||
* package passes a DevicePolicyManager Device Owner / Profile Owner device identifier
|
||||
* access check, or the calling package has carrier privileges.
|
||||
* <li>throw SecurityException: if the caller does not meet any of the requirements and is
|
||||
* targeting Q or is targeting pre-Q and does not have the READ_PHONE_STATE permission.
|
||||
* <li>return false: if the caller is targeting pre-Q and does have the READ_PHONE_STATE
|
||||
* permission. In this case the caller would expect to have access to the device
|
||||
* identifiers so false is returned instead of throwing a SecurityException to indicate
|
||||
* the calling function should return dummy data.
|
||||
* </ul>
|
||||
*/
|
||||
public static boolean checkCallingOrSelfReadDeviceIdentifiers(Context context,
|
||||
String callingPackage, String message) {
|
||||
return checkCallingOrSelfReadDeviceIdentifiers(context,
|
||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the caller (or self, if not processing an IPC) can read device identifiers.
|
||||
*
|
||||
* <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 calling
|
||||
* package passes a DevicePolicyManager Device Owner / Profile Owner device identifier
|
||||
* access check, or the calling package has carrier privileges.
|
||||
* <li>throw SecurityException: if the caller does not meet any of the requirements and is
|
||||
* targeting Q or is targeting pre-Q and does not have the READ_PHONE_STATE permission
|
||||
* or carrier privileges.
|
||||
* <li>return false: if the caller is targeting pre-Q and does have the READ_PHONE_STATE
|
||||
* permission or carrier privileges. In this case the caller would expect to have access
|
||||
* to the device identifiers so false is returned instead of throwing a SecurityException
|
||||
* to indicate the calling function should return dummy data.
|
||||
* </ul>
|
||||
*/
|
||||
public static boolean checkCallingOrSelfReadDeviceIdentifiers(Context context, int subId,
|
||||
String callingPackage, String message) {
|
||||
return checkReadDeviceIdentifiers(context, TELEPHONY_SUPPLIER, subId,
|
||||
Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the caller (or self, if not processing an IPC) can read subscriber identifiers.
|
||||
*
|
||||
* <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 calling
|
||||
* package passes a DevicePolicyManager Device Owner / Profile Owner device identifier
|
||||
* access check, or the calling package has carrier privileges.
|
||||
* <li>throw SecurityException: if the caller does not meet any of the requirements and is
|
||||
* targeting Q or is targeting pre-Q and does not have the READ_PHONE_STATE permission.
|
||||
* <li>return false: if the caller is targeting pre-Q and does have the READ_PHONE_STATE
|
||||
* permission. In this case the caller would expect to have access to the device
|
||||
* identifiers so false is returned instead of throwing a SecurityException to indicate
|
||||
* the calling function should return dummy data.
|
||||
* </ul>
|
||||
*/
|
||||
public static boolean checkCallingOrSelfReadSubscriberIdentifiers(Context context, int subId,
|
||||
String callingPackage, String message) {
|
||||
return checkReadDeviceIdentifiers(context, TELEPHONY_SUPPLIER, subId,
|
||||
Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the app with the given pid/uid can read device identifiers.
|
||||
*
|
||||
* @returns true if the caller has the READ_PRIVILEGED_PHONE_STATE permission or the calling
|
||||
* package passes a DevicePolicyManager Device Owner / Profile Owner device identifier access
|
||||
* check.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static boolean checkReadDeviceIdentifiers(Context context,
|
||||
Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
|
||||
String callingPackage, String message) {
|
||||
// Allow system and root access to the device identifiers.
|
||||
final int appId = UserHandle.getAppId(uid);
|
||||
if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID) {
|
||||
return true;
|
||||
}
|
||||
// Allow access to packages that have the READ_PRIVILEGED_PHONE_STATE permission.
|
||||
if (context.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid,
|
||||
uid) == PackageManager.PERMISSION_GRANTED) {
|
||||
return true;
|
||||
}
|
||||
// If the calling package has carrier privileges for any subscription then allow access.
|
||||
if (checkCarrierPrivilegeForAnySubId(context, telephonySupplier, uid)) {
|
||||
return true;
|
||||
}
|
||||
// if the calling package is not null then perform the DevicePolicyManager device /
|
||||
// profile owner and Appop checks.
|
||||
if (callingPackage != null) {
|
||||
// Allow access to a device / profile owner app.
|
||||
DevicePolicyManager devicePolicyManager =
|
||||
(DevicePolicyManager) context.getSystemService(
|
||||
Context.DEVICE_POLICY_SERVICE);
|
||||
if (devicePolicyManager != null && devicePolicyManager.checkDeviceIdentifierAccess(
|
||||
callingPackage, pid, uid)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,
|
||||
message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports a failure when the app with the given pid/uid cannot access the requested identifier.
|
||||
*
|
||||
* @returns false if the caller is targeting pre-Q and does have the READ_PHONE_STATE
|
||||
* permission or carrier privileges.
|
||||
* @throws SecurityException if the caller does not meet any of the requirements for the
|
||||
* requested identifier and is targeting Q or is targeting pre-Q
|
||||
* and does not have the READ_PHONE_STATE permission or carrier
|
||||
* privileges.
|
||||
*/
|
||||
private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
|
||||
int uid, String callingPackage, String message) {
|
||||
boolean isPreinstalled = false;
|
||||
boolean isPrivApp = false;
|
||||
ApplicationInfo callingPackageInfo = null;
|
||||
try {
|
||||
callingPackageInfo = context.getPackageManager().getApplicationInfoAsUser(
|
||||
callingPackage, 0, UserHandle.getUserId(uid));
|
||||
if (callingPackageInfo != null) {
|
||||
if (callingPackageInfo.isSystemApp()) {
|
||||
isPreinstalled = true;
|
||||
if (callingPackageInfo.isPrivilegedApp()) {
|
||||
isPrivApp = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// If the application info for the calling package could not be found then assume the
|
||||
// calling app is a non-preinstalled app to detect any issues with the check
|
||||
Log.e(LOG_TAG, "Exception caught obtaining package info for package " + callingPackage,
|
||||
e);
|
||||
}
|
||||
// The current package should only be reported in StatsLog if it has not previously been
|
||||
// reported for the currently invoked device identifier method.
|
||||
boolean packageReported = sReportedDeviceIDPackages.containsKey(callingPackage);
|
||||
if (!packageReported || !sReportedDeviceIDPackages.get(callingPackage).contains(
|
||||
message)) {
|
||||
Set invokedMethods;
|
||||
if (!packageReported) {
|
||||
invokedMethods = new HashSet<String>();
|
||||
sReportedDeviceIDPackages.put(callingPackage, invokedMethods);
|
||||
} else {
|
||||
invokedMethods = sReportedDeviceIDPackages.get(callingPackage);
|
||||
}
|
||||
invokedMethods.add(message);
|
||||
StatsLog.write(StatsLog.DEVICE_IDENTIFIER_ACCESS_DENIED, callingPackage, message,
|
||||
isPreinstalled, isPrivApp);
|
||||
}
|
||||
Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message
|
||||
+ ":isPreinstalled=" + isPreinstalled + ":isPrivApp=" + isPrivApp);
|
||||
// if the target SDK is pre-Q then check if the calling package would have previously
|
||||
// had access to device identifiers.
|
||||
if (callingPackageInfo != null && (
|
||||
callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q)) {
|
||||
if (context.checkPermission(
|
||||
android.Manifest.permission.READ_PHONE_STATE,
|
||||
pid,
|
||||
uid) == PackageManager.PERMISSION_GRANTED) {
|
||||
return false;
|
||||
}
|
||||
if (checkCarrierPrivilegeForSubId(subId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
throw new SecurityException(message + ": The user " + uid
|
||||
+ " does not meet the requirements to access device identifiers.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -383,6 +588,26 @@ public final class TelephonyPermissions {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the provided uid has carrier privileges for any active subscription ID.
|
||||
*/
|
||||
private static boolean checkCarrierPrivilegeForAnySubId(Context context,
|
||||
Supplier<ITelephony> telephonySupplier, int uid) {
|
||||
SubscriptionManager sm = (SubscriptionManager) context.getSystemService(
|
||||
Context.TELEPHONY_SUBSCRIPTION_SERVICE);
|
||||
int[] activeSubIds = sm.getActiveSubscriptionIdList();
|
||||
if (activeSubIds != null) {
|
||||
for (int activeSubId : activeSubIds) {
|
||||
if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid)
|
||||
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static int getCarrierPrivilegeStatus(
|
||||
Supplier<ITelephony> telephonySupplier, int subId, int uid) {
|
||||
ITelephony telephony = telephonySupplier.get();
|
||||
|
||||
Reference in New Issue
Block a user