Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 50b9f34e authored by Anna Zhuravleva's avatar Anna Zhuravleva
Browse files

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
parent 4c1bdc28
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -135,3 +135,10 @@ flag {
    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"
}
+2 −0
Original line number Diff line number Diff line
@@ -316,6 +316,8 @@ applications that come with the platform
        <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" />
+2 −0
Original line number Diff line number Diff line
@@ -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" />
+47 −16
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import static android.content.pm.LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS;

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.IApplicationThread;
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.ShortcutQueryWrapper;
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 @@ public class LauncherAppsService extends SystemService {

        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 @@ public class LauncherAppsService extends SystemService {
            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 @@ public class LauncherAppsService extends SystemService {

        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 @@ public class LauncherAppsService extends SystemService {
                            + 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 @@ public class LauncherAppsService extends SystemService {
                    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 @@ public class LauncherAppsService extends SystemService {

        @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 @@ public class LauncherAppsService extends SystemService {

        @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 @@ public class LauncherAppsService extends SystemService {
        @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;