diff options
author | 2025-01-21 23:16:06 +0000 | |
---|---|---|
committer | 2025-02-04 15:09:14 -0800 | |
commit | 84128e559cd5aa36e997f7b4ae966d7417a9fd5f (patch) | |
tree | d8ea22037b0b0e7b072d4c26b43d0386f3de44ff /PermissionController/src | |
parent | 6dfdb7d5fe86129285f57f8cc662b395e6d82d68 (diff) |
ShouldShowHealthPermissions: Skip intent check for Wear and split permissions
This bug was caught when no health permissions are granted to a wear app and "Fitness&Wellness" does not show up in AppInfo->Permissions screen.
The root cause is, currently, PermissionController displays HealthConnect UI in AppInfo flow only if
1. Any health permission is granted --> This gives user option to revoke any granted permissions, or
2. Intent filter is declared by the requesting app
[2] is inconsistent with current Health&Fitness requirement about intent filters, where H&F relaxes this intent on Wear devices or if the permissions are from split permissions.
To achieve consistent behavior with H&F, in this CL we change the PermissionController intent filter requirement same with H&F.
Flag: android.permission.flags.replace_body_sensor_permission_enabled
Bug: 390731163
Test: On both Wear and Phone emulator, `atest HealthConnectAppPermissionFragmentTest`
Relnote: Granular health permission phase one
LOW_COVERAGE_REASON=OTHER_FORM_FACTOR
Change-Id: I9480b9673de578ba3edc20f680b5a13a06c72c59
Diffstat (limited to 'PermissionController/src')
-rw-r--r-- | PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java | 83 |
1 files changed, 82 insertions, 1 deletions
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java index 38495f3b3..327142896 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java +++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java @@ -39,6 +39,7 @@ import static android.content.Intent.EXTRA_REASON; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; +import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; @@ -78,6 +79,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.hardware.SensorPrivacyManager; import android.health.connect.HealthConnectManager; +import android.health.connect.HealthPermissions; import android.os.Binder; import android.os.Build; import android.os.Parcelable; @@ -1107,17 +1109,31 @@ public final class Utils { return false; } + // Always show Fitness&Wellness chip on Wear. + if (Flags.replaceBodySensorPermissionEnabled() + && pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) { + return true; + } + // Check in permission is already granted as we should not hide it in the UX at that point. List<String> grantedPermissions = packageInfo.getGrantedPermissions(); for (PermissionInfo permission : permissions) { boolean isCurrentlyGranted = grantedPermissions.contains(permission.name); if (isCurrentlyGranted) { - Log.d(LOG_TAG, "At least one Health permission group permission is granted, " + Log.d( + LOG_TAG, + "At least one Health permission group permission is granted, " + "show permission group entry"); return true; } } + // When none health permission is granted, exempt health permission view usage intent filter + // if all the requested health permissions are from permission splits. + if (isRequestFromSplitHealthPermission(packageInfo)) { + return true; + } + Intent viewUsageIntent = new Intent(Intent.ACTION_VIEW_PERMISSION_USAGE); viewUsageIntent.addCategory(HealthConnectManager.CATEGORY_HEALTH_PERMISSIONS); viewUsageIntent.setPackage(packageInfo.getPackageName()); @@ -1132,6 +1148,71 @@ public final class Utils { } /** + * Returns true if the request is being made as the result of a split health permission from + * BODY_SENSORS call. + */ + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + private static boolean isRequestFromSplitHealthPermission(LightPackageInfo packageInfo) { + // Sdk check to make sure HealthConnectManager.isHealthPermission() is supported. + if (!SdkLevel.isAtLeastU() || !Flags.replaceBodySensorPermissionEnabled()) { + return false; + } + + PermissionControllerApplication app = PermissionControllerApplication.get(); + PackageManager pm = app.getPackageManager(); + String packageName = packageInfo.getPackageName(); + UserHandle user = UserHandle.getUserHandleForUid(packageInfo.getUid()); + Context context = Utils.getUserContext(app, user); + + List<String> requestedHealthPermissions = new ArrayList<>(); + for (String permission : packageInfo.getRequestedPermissions()) { + if (HealthConnectManager.isHealthPermission(context, permission)) { + requestedHealthPermissions.add(permission); + } + } + + // Split permission only applies to READ_HEART_RATE. + if (!requestedHealthPermissions.contains(HealthPermissions.READ_HEART_RATE)) { + return false; + } + + // If there are other health permissions (other than READ_HEALTH_DATA_IN_BACKGROUND) + // don't consider this a pure split-permission request. + if (requestedHealthPermissions.size() > 2) { + return false; + } + + boolean isBackgroundPermissionRequested = + requestedHealthPermissions.contains( + HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND); + // If there are two health permissions declared, make sure the other is + // READ_HEALTH_DATA_IN_BACKGROUND. + if (requestedHealthPermissions.size() == 2 && !isBackgroundPermissionRequested) { + return false; + } + + // If READ_HEALTH_DATA_IN_BACKGROUND is requested, check permission flag to see if is from + // split permission. + if (isBackgroundPermissionRequested) { + int readHealthDataInBackgroundFlag = + pm.getPermissionFlags( + HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND, packageName, user); + if (!isFromSplitPermission(readHealthDataInBackgroundFlag)) { + return false; + } + } + + // Check READ_HEART_RATE permission flag to see if is from split permission. + int readHeartRateFlag = + pm.getPermissionFlags(HealthPermissions.READ_HEART_RATE, packageName, user); + return isFromSplitPermission(readHeartRateFlag); + } + + private static boolean isFromSplitPermission(int permissionFlag) { + return (permissionFlag & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0; + } + + /** * Get a device protected storage based shared preferences. Avoid storing sensitive data in it. * * @param context the context to get the shared preferences |