Add device identifier permission checks to TelephonyPermissions
Bug: 131916175
Fixes: 136305129
Test: atest PhoneSubInfoControllerTest
Change-Id: Icca98347ab76b7c3468b95756a33a758a3abde37
Merged-In: I3c82c53ec89cd17b34a61166ccc9e9747388efac
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 6506df2..2ef2c21 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -166,6 +166,7 @@
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"];
}
@@ -3054,3 +3055,22 @@
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;
+}
\ No newline at end of file
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e2322f3e..79a23d6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5654,6 +5654,31 @@
}
/**
+ * 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}
* if one is not set.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 0e95e639..d74943a 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -146,6 +146,7 @@
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 @@
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();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 1c9782f..f2560b3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -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 @@
@Override
public void setDefaultSmsApplication(ComponentName admin, String packageName) {
}
+
+ @Override
+ public boolean checkDeviceIdentifierAccess(String packageName, int pid, int uid) {
+ return false;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d3b25fd..d1128af 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -60,7 +60,6 @@
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 @@
.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.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 @@
@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 @@
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 @@
* @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 @@
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 @@
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 + ".");
}
@@ -7783,6 +7782,13 @@
}
@Override
+ public ComponentName getProfileOwnerAsUser(int userHandle) {
+ enforceCrossUsersPermission(userHandle);
+
+ return getProfileOwner(userHandle);
+ }
+
+ @Override
public ComponentName getProfileOwner(int userHandle) {
if (!mHasFeature) {
return null;
@@ -7825,6 +7831,68 @@
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 @@
@Override
public boolean isCallerApplicationRestrictionsManagingPackage(String callerPackage) {
- return isCallerDelegate(callerPackage, DELEGATION_APP_RESTRICTIONS);
+ return isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(),
+ DELEGATION_APP_RESTRICTIONS);
}
@Override
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 12422c67..6557886 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -791,6 +791,14 @@
public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET;
/**
+ * IMSI (International Mobile Subscriber Identity).
+ * <P>Type: TEXT </P>
+ * @hide
+ */
+ //TODO: add @SystemApi
+ public static final String IMSI = "imsi";
+
+ /**
* Broadcast Action: The user has changed one of the default subs related to
* data, phone calls, or sms</p>
*
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 73d49dd..2d66427 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -19,17 +19,28 @@
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 @@
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 @@
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 @@
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);
}
}
@@ -202,6 +231,182 @@
}
/**
+ * 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
* to it, {@code false} otherwise.
@@ -383,6 +588,26 @@
}
}
+ /**
+ * 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();