Add private profile checks for LauncherApps APIs

Restrict LauncherApps APIs for private profile:
- Caller must hold HOME role (i.e. default launcher)
- Must hold ACCESS_HIDDEN_PROFILES or ACCESS_HIDDEN_PROFILES_FULL
permissions

Remove temporary recents role holder checks.

Test: atest LauncherAppsTest, LauncherAppsForHiddenProfilesTest
Bug: 25851973
Flag: android.multiuser.enable_launcher_apps_hidden_profile_checks
DEVELOPMENT

Change-Id: Icf65b4329bbbd323f521d1f97b5369e7544111c8
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 4b890fa..c7d93bf 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -135,3 +135,10 @@
     description: "Allow the use of a profileApiAvailability user property to exclude HIDDEN profiles in API results"
     bug: "316362775"
 }
+
+flag {
+    name: "enable_launcher_apps_hidden_profile_checks"
+    namespace: "profile_experiences"
+    description: "Enable extra check to limit access to hidden prfiles data in Launcher apps APIs."
+    bug: "321988638"
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 0baaff0..83440ec 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -316,6 +316,8 @@
         <permission name="android.permission.SET_LOW_POWER_STANDBY_PORTS" />
         <permission name="android.permission.MANAGE_ROLLBACKS"/>
         <permission name="android.permission.MANAGE_USB"/>
+        <!-- Permission required to test Launcher Apps APIs for hidden profiles -->
+        <permission name="android.permission.ACCESS_HIDDEN_PROFILES_FULL" />
         <!-- Needed for tests only -->
         <permission name="android.permission.MANAGE_CLOUDSEARCH" />
         <permission name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION" />
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 84ef6e5..ab631c2 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -250,6 +250,8 @@
     <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
     <uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS" />
     <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+    <!-- Permission required to test LauncherApps APIs for hidden profiles -->
+    <uses-permission android:name="android.permission.ACCESS_HIDDEN_PROFILES_FULL" />
     <!-- Shell only holds android.permission.NETWORK_SCAN in order to to enable CTS testing -->
     <uses-permission android:name="android.permission.NETWORK_SCAN" />
     <uses-permission android:name="android.permission.REGISTER_CALL_PROVIDER" />
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 984a629..295528e 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -38,6 +38,7 @@
 
 import static com.android.server.pm.PackageArchiver.isArchivingEnabled;
 
+import android.Manifest;
 import android.annotation.AppIdInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -52,6 +53,7 @@
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyCache;
 import android.app.admin.DevicePolicyManager;
+import android.app.role.RoleManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
@@ -84,7 +86,9 @@
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
 import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
 import android.graphics.Rect;
+import android.multiuser.Flags;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -211,6 +215,7 @@
 
         private final Context mContext;
         private final UserManager mUm;
+        private final RoleManager mRoleManager;
         private final IPackageManager mIPM;
         private final UserManagerInternal mUserManagerInternal;
         private final UsageStatsManagerInternal mUsageStatsManagerInternal;
@@ -247,6 +252,7 @@
             mContext = context;
             mIPM = AppGlobals.getPackageManager();
             mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+            mRoleManager = mContext.getSystemService(RoleManager.class);
             mUserManagerInternal = Objects.requireNonNull(
                     LocalServices.getService(UserManagerInternal.class));
             mUsageStatsManagerInternal = Objects.requireNonNull(
@@ -451,7 +457,6 @@
 
         private boolean canAccessProfile(int callingUid, int callingUserId, int callingPid,
                 int targetUserId, String message) {
-
             if (targetUserId == callingUserId) return true;
             if (injectHasInteractAcrossUsersFullPermission(callingPid, callingUid)) {
                 return true;
@@ -465,6 +470,14 @@
                             + targetUserId + " from " + callingUserId + " not allowed");
                     return false;
                 }
+
+                if (areHiddenApisChecksEnabled()
+                        && mUm.getUserProperties(UserHandle.of(targetUserId))
+                                        .getProfileApiVisibility()
+                                == UserProperties.PROFILE_API_VISIBILITY_HIDDEN
+                        && !canAccessHiddenProfileInjected(callingUid, callingPid)) {
+                    return false;
+                }
             } finally {
                 injectRestoreCallingIdentity(ident);
             }
@@ -473,10 +486,43 @@
                     message, true);
         }
 
+        boolean areHiddenApisChecksEnabled() {
+            return android.os.Flags.allowPrivateProfile()
+                    && Flags.enableLauncherAppsHiddenProfileChecks()
+                    && Flags.enablePermissionToAccessHiddenProfiles();
+        }
+
         private void verifyCallingPackage(String callingPackage) {
             verifyCallingPackage(callingPackage, injectBinderCallingUid());
         }
 
+        boolean canAccessHiddenProfileInjected(int callingUid, int callingPid) {
+            AndroidPackage callingPackage = mPackageManagerInternal.getPackage(callingUid);
+            if (callingPackage == null) {
+                return false;
+            }
+
+            if (!mRoleManager
+                    .getRoleHoldersAsUser(
+                            RoleManager.ROLE_HOME, UserHandle.getUserHandleForUid(callingUid))
+                    .contains(callingPackage.getPackageName())) {
+                return false;
+            }
+
+            if (mContext.checkPermission(
+                            Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL, callingPid, callingUid)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            }
+
+            // TODO(b/321988638): add option to disable with a flag
+            return mContext.checkPermission(
+                            android.Manifest.permission.ACCESS_HIDDEN_PROFILES,
+                            callingPid,
+                            callingUid)
+                    == PackageManager.PERMISSION_GRANTED;
+        }
+
         @VisibleForTesting // We override it in unit tests
         void verifyCallingPackage(String callingPackage, int callerUid) {
             int packageUid = -1;
@@ -1566,11 +1612,6 @@
 
         @Override
         public @Nullable LauncherUserInfo getLauncherUserInfo(@NonNull UserHandle user) {
-            // Only system launchers, which have access to recents should have access to this API.
-            // TODO(b/303803157): Add the new permission check if we decide to have one.
-            if (!mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid())) {
-                throw new SecurityException("Caller is not the recents app");
-            }
             if (!canAccessProfile(user.getIdentifier(),
                     "Can't access LauncherUserInfo for another user")) {
                 return null;
@@ -1585,11 +1626,6 @@
 
         @Override
         public List<String> getPreInstalledSystemPackages(UserHandle user) {
-            // Only system launchers, which have access to recents should have access to this API.
-            // TODO(b/303803157): Update access control for this API to default Launcher app.
-            if (!mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid())) {
-                throw new SecurityException("Caller is not the recents app");
-            }
             if (!canAccessProfile(user.getIdentifier(),
                     "Can't access preinstalled packages for another user")) {
                 return null;
@@ -1610,11 +1646,6 @@
         @Override
         public @Nullable IntentSender getAppMarketActivityIntent(@NonNull String callingPackage,
                 @Nullable String packageName, @NonNull UserHandle user) {
-            // Only system launchers, which have access to recents should have access to this API.
-            // TODO(b/303803157): Update access control for this API to default Launcher app.
-            if (!mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid())) {
-                throw new SecurityException("Caller is not the recents app");
-            }
             if (!canAccessProfile(user.getIdentifier(),
                     "Can't access AppMarketActivity for another user")) {
                 return null;