diff options
| author | 2018-10-01 16:14:50 -0700 | |
|---|---|---|
| committer | 2018-10-09 13:44:02 -0700 | |
| commit | 6d20d75e9ea1b5df326ca243f966aa9f7328eaa4 (patch) | |
| tree | 64c8f03ea4e39eac4db4d0f6bc8cab58323658ee | |
| parent | 42f4e79d7e87eefe4a9528b646b1ae456a9f23e6 (diff) | |
Protect Device Identifiers behind priv permission and DO/PO checks
Bug: 110099294
Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases \
-t com.android.cts.devicepolicy.DeviceOwnerTest.testDeviceOwnerCanGetDeviceIdentifiers
Test: cts-tradefed run cts -m CtsDevicePolicyManagerTestCases \
-t com.android.cts.devicepolicy.ManagedProfileTest#testGetDeviceIdentifiers
Test: cts-tradefed run cts -m CtsTelephonyTestCases -t android.telephony.cts.TelephonyManagerTest
Test: cts-tradefed run cts -m CtsPermissionTestCases -t android.permission.cts.TelephonyManagerPermissionTest
Change-Id: I3c82c53ec89cd17b34a61166ccc9e9747388efac
11 files changed, 333 insertions, 50 deletions
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 09ab67186266..f8a5f93b7d5b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -5703,6 +5703,37 @@ public class DevicePolicyManager { } /** + * Returns whether the specified package can read the device identifiers. + * + * @param packageName The package name of the app to check for device identifier access. + * @return whether the package can read the device identifiers. + * + * @hide + */ + public boolean checkDeviceIdentifierAccess(String packageName) { + return checkDeviceIdentifierAccessAsUser(packageName, myUserId()); + } + + /** + * @hide + */ + @RequiresPermission(value = android.Manifest.permission.MANAGE_USERS, conditional = true) + public boolean checkDeviceIdentifierAccessAsUser(String packageName, int userId) { + throwIfParentInstance("checkDeviceIdentifierAccessAsUser"); + if (packageName == null) { + return false; + } + if (mService != null) { + try { + return mService.checkDeviceIdentifierAccess(packageName, userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + return false; + } + + /** * Called by a profile owner or device owner to set a default activity that the system selects * to handle intents that match the given {@link IntentFilter}. This activity will remain the * default intent handler even if the set of potential event handlers for the intent filter diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 35ea250828aa..5e454506e9eb 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -153,6 +153,8 @@ interface IDevicePolicyManager { void clearProfileOwner(in ComponentName who); boolean hasUserSetupCompleted(); + boolean checkDeviceIdentifierAccess(in String packageName, int userHandle); + void setDeviceOwnerLockScreenInfo(in ComponentName who, CharSequence deviceOwnerInfo); CharSequence getDeviceOwnerLockScreenInfo(); diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 8681893702b4..26c2033eac51 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -18,8 +18,10 @@ package android.os; import android.Manifest; import android.annotation.RequiresPermission; +import android.annotation.SuppressAutoDoc; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.app.ActivityThread; import android.app.Application; import android.content.Context; import android.text.TextUtils; @@ -127,14 +129,21 @@ public class Build { * <a href="/training/articles/security-key-attestation.html">key attestation</a> to obtain * proof of the device's original identifiers. * + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. Profile owner access is deprecated and will be removed in a future + * release. + * * @return The serial number if specified. */ - @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static String getSerial() { IDeviceIdentifiersPolicyService service = IDeviceIdentifiersPolicyService.Stub .asInterface(ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE)); try { - return service.getSerial(); + Application application = ActivityThread.currentApplication(); + String callingPackage = application != null ? application.getPackageName() : null; + return service.getSerialForPackage(callingPackage); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/os/IDeviceIdentifiersPolicyService.aidl b/core/java/android/os/IDeviceIdentifiersPolicyService.aidl index ac19f2bd5bbc..87d358f50e74 100644 --- a/core/java/android/os/IDeviceIdentifiersPolicyService.aidl +++ b/core/java/android/os/IDeviceIdentifiersPolicyService.aidl @@ -21,4 +21,5 @@ package android.os; */ interface IDeviceIdentifiersPolicyService { String getSerial(); + String getSerialForPackage(in String callingPackage); }
\ No newline at end of file diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java index 4a58f885ff08..2989df83866c 100644 --- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java +++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java @@ -16,6 +16,7 @@ package com.android.bandwidthtest; +import android.app.UiAutomation; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo.State; @@ -74,7 +75,13 @@ public class BandwidthTest extends InstrumentationTestCase { Log.v(LOG_TAG, "Initialized mConnectionUtil"); mUid = Process.myUid(); mTManager = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE); - mDeviceId = mTManager.getDeviceId(); + final UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); + try { + uiAutomation.adoptShellPermissionIdentity(); + mDeviceId = mTManager.getDeviceId(); + } finally { + uiAutomation.dropShellPermissionIdentity(); + } } @Override diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 114c2285c008..4b3504631bfe 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -322,6 +322,7 @@ applications that come with the platform <permission name="android.permission.PACKAGE_USAGE_STATS" /> <permission name="android.permission.READ_FRAME_BUFFER"/> <permission name="android.permission.READ_LOWPAN_CREDENTIAL"/> + <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.REAL_GET_TASKS"/> <permission name="android.permission.REGISTER_CALL_PROVIDER"/> <permission name="android.permission.REGISTER_CONNECTION_MANAGER"/> diff --git a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java index 5f082572db58..3ffc5c53eab6 100644 --- a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java +++ b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java @@ -16,18 +16,15 @@ package com.android.server.os; -import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Binder; import android.os.Build; import android.os.IDeviceIdentifiersPolicyService; -import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; -import android.os.UserHandle; + +import com.android.internal.telephony.TelephonyPermissions; import com.android.server.SystemService; /** @@ -54,15 +51,22 @@ public final class DeviceIdentifiersPolicyService extends SystemService { @Override public @Nullable String getSerial() throws RemoteException { - if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID - && mContext.checkCallingOrSelfPermission( - Manifest.permission.READ_PHONE_STATE) - != PackageManager.PERMISSION_GRANTED - && mContext.checkCallingOrSelfPermission( - Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("getSerial requires READ_PHONE_STATE" - + " or READ_PRIVILEGED_PHONE_STATE permission"); + // Since this invocation is on the server side a null value is used for the + // callingPackage as the server's package name (typically android) should not be used + // for any device / profile owner checks. The majority of requests for the serial number + // should use the getSerialForPackage method with the calling package specified. + if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext, + /* callingPackage */ null, "getSerial")) { + return null; + } + return SystemProperties.get("ro.serialno", Build.UNKNOWN); + } + + @Override + public @Nullable String getSerialForPackage(String callingPackage) throws RemoteException { + if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext, + callingPackage, "getSerial")) { + return null; } return SystemProperties.get("ro.serialno", Build.UNKNOWN); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 66cf48c8ad24..4350596ee63e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -66,4 +66,9 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public long forceSecurityLogs() { return 0; } + + @Override + public boolean checkDeviceIdentifierAccess(String packageName, int userHandle) { + 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 eeb4ad32408c..913b844d8d57 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -7861,6 +7861,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return getApplicationLabel(profileOwner.getPackageName(), userHandle); } + @Override + public boolean checkDeviceIdentifierAccess(String packageName, int userHandle) { + // Allow access to the device owner. + ComponentName deviceOwner = getDeviceOwnerComponent(true); + if (deviceOwner != null && deviceOwner.getPackageName().equals(packageName)) { + return true; + } + // Allow access to the profile owner for the specified user. + ComponentName profileOwner = getProfileOwnerAsUser(userHandle); + if (profileOwner != null && profileOwner.getPackageName().equals(packageName)) { + return true; + } + return false; + } + /** * Canonical name for a given package. */ diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 80b6ead339d0..4eacda346d02 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1268,15 +1268,18 @@ public class TelephonyManager { * Returns the unique device ID, for example, the IMEI for GSM and the MEID * or ESN for CDMA phones. Return null if device ID is not available. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. * * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns * MEID for CDMA. */ @Deprecated - @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceId() { try { ITelephony telephony = getITelephony(); @@ -1294,8 +1297,11 @@ public class TelephonyManager { * Returns the unique device ID of a subscription, for example, the IMEI for * GSM and the MEID for CDMA phones. Return null if device ID is not available. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. * * @param slotIndex of which deviceID is returned * @@ -1303,8 +1309,8 @@ public class TelephonyManager { * MEID for CDMA. */ @Deprecated - @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceId(int slotIndex) { // FIXME this assumes phoneId == slotIndex try { @@ -1323,11 +1329,14 @@ public class TelephonyManager { * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not * available. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. */ - @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getImei() { return getImei(getSlotIndex()); } @@ -1336,13 +1345,16 @@ public class TelephonyManager { * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not * available. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. * * @param slotIndex of which IMEI is returned */ - @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getImei(int slotIndex) { ITelephony telephony = getITelephony(); if (telephony == null) return null; @@ -1386,11 +1398,14 @@ public class TelephonyManager { /** * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. */ - @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getMeid() { return getMeid(getSlotIndex()); } @@ -1398,13 +1413,16 @@ public class TelephonyManager { /** * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the + * device or profile owner. The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. * * @param slotIndex of which MEID is returned */ - @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner. + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getMeid(int slotIndex) { ITelephony telephony = getITelephony(); if (telephony == null) return null; @@ -2888,11 +2906,15 @@ public class TelephonyManager { * Returns the serial number of the SIM, if applicable. Return null if it is * unavailable. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or + * profile owner, or that the calling app has carrier privileges (see {@link + * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getSimSerialNumber() { return getSimSerialNumber(getSubId()); } @@ -2900,11 +2922,18 @@ public class TelephonyManager { /** * Returns the serial number for the given subscription, if applicable. Return null if it is * unavailable. - * <p> + * + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or + * profile owner, or that the calling app has carrier privileges (see {@link + * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. + * * @param subId for which Sim Serial number is returned * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @UnsupportedAppUsage public String getSimSerialNumber(int subId) { try { @@ -3039,11 +3068,15 @@ public class TelephonyManager { * Returns the unique subscriber ID, for example, the IMSI for a GSM phone. * Return null if it is unavailable. * - * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or + * profile owner, or that the calling app has carrier privileges (see {@link + * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getSubscriberId() { return getSubscriberId(getSubId()); } @@ -3053,10 +3086,17 @@ public class TelephonyManager { * for a subscription. * Return null if it is unavailable. * + * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or + * profile owner, or that the calling app has carrier privileges (see {@link + * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the + * device; for more details see <a href="https://developer.android.com/work/managed-profiles"> + * Work profiles</a>. Profile owner access is deprecated and will be removed in a future + * release. + * * @param subId whose subscriber id is returned * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @UnsupportedAppUsage public String getSubscriberId(int subId) { try { diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java index 23ea237a32eb..dac7e04be07a 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java @@ -19,11 +19,16 @@ 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; @@ -135,6 +140,169 @@ public final class TelephonyPermissions { } /** + * 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 or the + * calling package passes a DevicePolicyManager Device Owner / Profile Owner device + * identifier access check, + * <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 or the + * calling package passes a DevicePolicyManager Device Owner / Profile Owner device + * identifier access check, + * <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) { + int pid = Binder.getCallingPid(); + int uid = Binder.getCallingUid(); + // if the device identifier check completes successfully then grant access. + if (checkReadDeviceIdentifiers(context, pid, uid, callingPackage)) { + return true; + } + // else the calling package is not authorized to access the device identifiers; call + // a central method to report the failure based on the target SDK and if the calling package + // has the READ_PHONE_STATE permission or carrier privileges that were previously required + // to access the identifiers. + return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, 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 privleges. + * <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) { + int pid = Binder.getCallingPid(); + int uid = Binder.getCallingUid(); + // if the device identifiers can be read then grant access to the subscriber identifiers + if (checkReadDeviceIdentifiers(context, pid, uid, callingPackage)) { + return true; + } + // If the calling package has carrier privileges then allow access to the subscriber + // identifiers. + if (SubscriptionManager.isValidSubscriptionId(subId) && getCarrierPrivilegeStatus( + TELEPHONY_SUPPLIER, subId, uid) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + return true; + } + return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, 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. + */ + private static boolean checkReadDeviceIdentifiers(Context context, int pid, int uid, + String callingPackage) { + // 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 is null then return now as there's no way to perform the + // DevicePolicyManager device / profile owner checks. + if (callingPackage == null) { + return false; + } + // Allow access to a device / profile owner app. + DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context.getSystemService( + Context.DEVICE_POLICY_SERVICE); + if (devicePolicyManager != null && devicePolicyManager.checkDeviceIdentifierAccessAsUser( + callingPackage, Binder.getCallingUserHandle().getIdentifier())) { + return true; + } + return false; + } + + /** + * 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) { + if (callingPackage != null) { + try { + // if the target SDK is pre-Q then check if the calling package would have + // previously had access to device identifiers. + ApplicationInfo callingPackageInfo = context.getPackageManager().getApplicationInfo( + callingPackage, 0); + 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 (SubscriptionManager.isValidSubscriptionId(subId) + && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, uid) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + return false; + } + } + } catch (PackageManager.NameNotFoundException e) { + // If the application info for the calling package could not be found then default + // to throwing the SecurityException. + } + } + throw new SecurityException(message + ": The user " + uid + " does not have the " + + "READ_PRIVILEGED_PHONE_STATE permission to access the 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. |