Merge "Add support for LauncherUersInfo config" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index faecbf1..7f74a62 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12735,6 +12735,7 @@
     method public abstract void onPackagesUnavailable(String[], android.os.UserHandle, boolean);
     method public void onPackagesUnsuspended(String[], android.os.UserHandle);
     method public void onShortcutsChanged(@NonNull String, @NonNull java.util.List<android.content.pm.ShortcutInfo>, @NonNull android.os.UserHandle);
+    method @FlaggedApi("android.multiuser.add_launcher_user_config") public void onUserConfigChanged(@NonNull android.content.pm.LauncherUserInfo);
   }
 
   public static final class LauncherApps.PinItemRequest implements android.os.Parcelable {
@@ -12770,10 +12771,12 @@
 
   @FlaggedApi("android.os.allow_private_profile") public final class LauncherUserInfo implements android.os.Parcelable {
     method @FlaggedApi("android.os.allow_private_profile") public int describeContents();
+    method @FlaggedApi("android.multiuser.add_launcher_user_config") @NonNull public android.os.Bundle getUserConfig();
     method @FlaggedApi("android.os.allow_private_profile") public int getUserSerialNumber();
     method @FlaggedApi("android.os.allow_private_profile") @NonNull public String getUserType();
     method @FlaggedApi("android.os.allow_private_profile") public void writeToParcel(@NonNull android.os.Parcel, int);
     field @FlaggedApi("android.os.allow_private_profile") @NonNull public static final android.os.Parcelable.Creator<android.content.pm.LauncherUserInfo> CREATOR;
+    field @FlaggedApi("android.multiuser.add_launcher_user_config") public static final String PRIVATE_SPACE_ENTRYPOINT_HIDDEN = "private_space_entrypoint_hidden";
   }
 
   public final class ModuleInfo implements android.os.Parcelable {
diff --git a/core/java/android/content/pm/IOnAppsChangedListener.aidl b/core/java/android/content/pm/IOnAppsChangedListener.aidl
index 830cbe0..ade58c4 100644
--- a/core/java/android/content/pm/IOnAppsChangedListener.aidl
+++ b/core/java/android/content/pm/IOnAppsChangedListener.aidl
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.content.pm.LauncherUserInfo;
 import android.content.pm.ParceledListSlice;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -34,4 +35,5 @@
     void onPackagesUnsuspended(in UserHandle user, in String[] packageNames);
     void onShortcutChanged(in UserHandle user, String packageName, in ParceledListSlice shortcuts);
     void onPackageLoadingProgressChanged(in UserHandle user, String packageName, float progress);
+    void onUserConfigChanged(in LauncherUserInfo launcherUserInfo);
 }
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 26f919f..26b8356 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -182,6 +182,8 @@
      */
     public static final int FLAG_CACHE_PEOPLE_TILE_SHORTCUTS = 2;
 
+    private static final String LAUNCHER_USER_INFO_EXTRA_KEY = "launcher_user_info";
+
     /** @hide */
     @IntDef(flag = false, prefix = { "FLAG_CACHE_" }, value = {
             FLAG_CACHE_NOTIFICATION_SHORTCUTS,
@@ -349,6 +351,19 @@
          */
         public void onPackageLoadingProgressChanged(@NonNull String packageName,
                 @NonNull UserHandle user, float progress) {}
+
+        /**
+         * Indicates {@link LauncherUserInfo} configs for a user have changed. The new
+         * {@link LauncherUserInfo} is given as a parameter.
+         *
+         * {@link LauncherUserInfo#getUserConfig} to get the updated user configs.
+         *
+         * @param launcherUserInfo The LauncherUserInfo of the user/profile whose configs have
+         *                         changed.
+         */
+        @FlaggedApi(android.multiuser.Flags.FLAG_ADD_LAUNCHER_USER_CONFIG)
+        public void onUserConfigChanged(@NonNull LauncherUserInfo launcherUserInfo) {
+        }
     }
 
     /**
@@ -2168,6 +2183,21 @@
                 }
             }
         }
+
+        public void onUserConfigChanged(LauncherUserInfo launcherUserInfo) {
+            if (DEBUG) {
+                if (Flags.allowPrivateProfile()
+                        && android.multiuser.Flags.addLauncherUserConfig()) {
+                    Log.d(TAG, "OnUserConfigChanged for user type " + launcherUserInfo.getUserType()
+                            + ", new userConfig: " + launcherUserInfo.getUserConfig());
+                }
+            }
+            synchronized (LauncherApps.this) {
+                for (CallbackMessageHandler callback : mCallbacks) {
+                    callback.postOnUserConfigChanged(launcherUserInfo);
+                }
+            }
+        }
     };
 
     /**
@@ -2224,6 +2254,7 @@
         private static final int MSG_UNSUSPENDED = 7;
         private static final int MSG_SHORTCUT_CHANGED = 8;
         private static final int MSG_LOADING_PROGRESS_CHANGED = 9;
+        private static final int MSG_USER_CONFIG_CHANGED = 10;
 
         private final LauncherApps.Callback mCallback;
 
@@ -2278,6 +2309,14 @@
                     mCallback.onPackageLoadingProgressChanged(info.packageName, info.user,
                             info.mLoadingProgress);
                     break;
+                case MSG_USER_CONFIG_CHANGED:
+                    if (Flags.allowPrivateProfile()
+                            && android.multiuser.Flags.addLauncherUserConfig()) {
+                        mCallback.onUserConfigChanged(Objects.requireNonNull(
+                                info.launcherExtras.getParcelable(LAUNCHER_USER_INFO_EXTRA_KEY,
+                                        LauncherUserInfo.class)));
+                    }
+                    break;
             }
         }
 
@@ -2353,6 +2392,13 @@
             info.mLoadingProgress = progress;
             obtainMessage(MSG_LOADING_PROGRESS_CHANGED, info).sendToTarget();
         }
+
+        public void postOnUserConfigChanged(LauncherUserInfo launcherUserInfo) {
+            CallbackInfo info = new CallbackInfo();
+            info.launcherExtras = new Bundle();
+            info.launcherExtras.putParcelable(LAUNCHER_USER_INFO_EXTRA_KEY, launcherUserInfo);
+            obtainMessage(MSG_USER_CONFIG_CHANGED, info).sendToTarget();
+        }
     }
 
     /**
diff --git a/core/java/android/content/pm/LauncherUserInfo.java b/core/java/android/content/pm/LauncherUserInfo.java
index 8426f54..574af59 100644
--- a/core/java/android/content/pm/LauncherUserInfo.java
+++ b/core/java/android/content/pm/LauncherUserInfo.java
@@ -18,6 +18,7 @@
 
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
+import android.os.Bundle;
 import android.os.Flags;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -31,11 +32,25 @@
 @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
 public final class LauncherUserInfo implements Parcelable {
 
+    /**
+     * A boolean extra indicating whether the private space entrypoint should be hidden when locked.
+     *
+     * @see #getUserConfig
+     */
+    @FlaggedApi(android.multiuser.Flags.FLAG_ADD_LAUNCHER_USER_CONFIG)
+    public static final String PRIVATE_SPACE_ENTRYPOINT_HIDDEN =
+            "private_space_entrypoint_hidden";
+
     private final String mUserType;
 
     // Serial number for the user, should be same as in the {@link UserInfo} object.
     private final int mUserSerialNumber;
 
+    // Additional configs for the user, e.g., whether to hide the private space entrypoint when
+    // locked.
+    private final Bundle mUserConfig;
+
+
     /**
      * Returns type of the user as defined in {@link UserManager}. e.g.,
      * {@link UserManager.USER_TYPE_PROFILE_MANAGED} or {@link UserManager.USER_TYPE_PROFILE_ClONE}
@@ -50,6 +65,17 @@
     }
 
     /**
+     * Returns additional configs for this launcher user
+     *
+     * @see #PRIVATE_SPACE_ENTRYPOINT_HIDDEN
+     */
+    @FlaggedApi(android.multiuser.Flags.FLAG_ADD_LAUNCHER_USER_CONFIG)
+    @NonNull
+    public Bundle getUserConfig() {
+        return mUserConfig;
+    }
+
+    /**
      * Returns serial number of user as returned by
      * {@link UserManager#getSerialNumberForUser(UserHandle)}
      *
@@ -63,6 +89,7 @@
     private LauncherUserInfo(@NonNull Parcel in) {
         mUserType = in.readString16NoHelper();
         mUserSerialNumber = in.readInt();
+        mUserConfig = in.readBundle(Bundle.class.getClassLoader());
     }
 
     @Override
@@ -70,6 +97,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString16NoHelper(mUserType);
         dest.writeInt(mUserSerialNumber);
+        dest.writeBundle(mUserConfig);
     }
 
     @Override
@@ -99,23 +127,36 @@
         private final String mUserType;
 
         private final int mUserSerialNumber;
+        private final Bundle mUserConfig;
+
+
+        @FlaggedApi(android.multiuser.Flags.FLAG_ADD_LAUNCHER_USER_CONFIG)
+        public Builder(@NonNull String userType, int userSerialNumber, @NonNull Bundle config) {
+            this.mUserType = userType;
+            this.mUserSerialNumber = userSerialNumber;
+            this.mUserConfig = config;
+        }
 
         public Builder(@NonNull String userType, int userSerialNumber) {
             this.mUserType = userType;
             this.mUserSerialNumber = userSerialNumber;
+            this.mUserConfig = new Bundle();
         }
 
         /**
          * Builds the LauncherUserInfo object
          */
-        @NonNull public LauncherUserInfo build() {
-            return new LauncherUserInfo(this.mUserType, this.mUserSerialNumber);
+        @NonNull
+        public LauncherUserInfo build() {
+            return new LauncherUserInfo(this.mUserType, this.mUserSerialNumber, this.mUserConfig);
         }
 
     } // End builder
 
-    private LauncherUserInfo(@NonNull  String userType, int userSerialNumber) {
+    private LauncherUserInfo(@NonNull String userType, int userSerialNumber,
+            @NonNull Bundle config) {
         this.mUserType = userType;
         this.mUserSerialNumber = userSerialNumber;
+        this.mUserConfig = config;
     }
 }
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 528bde8..3d89ce1 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -543,3 +543,10 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+    name: "add_launcher_user_config"
+    namespace: "profile_experiences"
+    description: "Add support for LauncherUserInfo configs"
+    bug: "346553745"
+}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 5653da0..2c09423 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -88,6 +88,7 @@
 import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
 import android.content.pm.UserInfo;
 import android.content.pm.UserProperties;
+import android.database.ContentObserver;
 import android.graphics.Rect;
 import android.multiuser.Flags;
 import android.net.Uri;
@@ -95,6 +96,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IInterface;
+import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteCallbackList;
@@ -249,6 +251,7 @@
         private PackageInstallerService mPackageInstallerService;
 
         final LauncherAppsServiceInternal mInternal;
+        private SecureSettingsObserver mSecureSettingsObserver;
 
         @NonNull
         private final RemoteCallbackList<IDumpCallback> mDumpCallbacks =
@@ -278,6 +281,7 @@
             mCallbackHandler = BackgroundThread.getHandler();
             mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
             mInternal = new LocalService();
+            registerSettingsObserver();
         }
 
         @VisibleForTesting
@@ -2312,6 +2316,13 @@
             }
         }
 
+        void registerSettingsObserver() {
+            if (Flags.addLauncherUserConfig()) {
+                mSecureSettingsObserver = new SecureSettingsObserver();
+                mSecureSettingsObserver.register();
+            }
+        }
+
         public static class ShortcutChangeHandler implements LauncherApps.ShortcutChangeCallback {
             private final UserManagerInternal mUserManagerInternal;
 
@@ -2837,5 +2848,84 @@
                         shortcutId, sourceBounds, startActivityOptions, targetUserId);
             }
         }
+
+        class SecureSettingsObserver extends ContentObserver {
+
+            SecureSettingsObserver() {
+                super(new Handler(Looper.getMainLooper()));
+            }
+
+            @Override
+            public void onChange(boolean selfChange, Uri uri) {
+                super.onChange(selfChange, uri);
+                if (uri.equals(
+                        Settings.Secure.getUriFor(Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT))) {
+
+                    // This setting key only apply to private profile at the moment
+                    UserHandle privateProfile = getPrivateProfile();
+                    if (privateProfile.getIdentifier() == UserHandle.USER_NULL) {
+                        return;
+                    }
+
+                    final int n = mListeners.beginBroadcast();
+                    try {
+                        for (int i = 0; i < n; i++) {
+                            final IOnAppsChangedListener listener =
+                                    mListeners.getBroadcastItem(i);
+                            final BroadcastCookie cookie =
+                                    (BroadcastCookie) mListeners.getBroadcastCookie(
+                                            i);
+                            if (!isEnabledProfileOf(cookie, privateProfile,
+                                    "onSecureSettingsChange")) {
+                                Log.d(TAG, "onSecureSettingsChange: Skipping - profile not enabled"
+                                        + " or not accessible for package=" + cookie.packageName
+                                        + ", packageUid=" + cookie.callingUid);
+                            } else {
+                                try {
+                                    Log.d(TAG,
+                                            "onUserConfigChanged: triggering onUserConfigChanged");
+                                    listener.onUserConfigChanged(
+                                            mUserManagerInternal.getLauncherUserInfo(
+                                                    privateProfile.getIdentifier()));
+                                } catch (RemoteException re) {
+                                    Slog.d(TAG, "onUserConfigChanged: Callback failed ", re);
+                                }
+                            }
+                        }
+                    } finally {
+                        mListeners.finishBroadcast();
+                    }
+                }
+            }
+
+            public void register() {
+                UserHandle privateProfile = getPrivateProfile();
+                int parentUserId;
+                if (privateProfile.getIdentifier() == UserHandle.USER_NULL) {
+                    // No private space available, register the observer for the current user
+                    parentUserId = mContext.getUserId();
+                } else {
+                    parentUserId = mUserManagerInternal.getProfileParentId(
+                            privateProfile.getIdentifier());
+                }
+                mContext.getContentResolver().registerContentObserver(
+                        Settings.Secure.getUriFor(Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT),
+                        true, this, parentUserId);
+            }
+
+            public void unregister() {
+                mContext.getContentResolver().unregisterContentObserver(this);
+            }
+
+            private UserHandle getPrivateProfile() {
+                UserInfo[] userInfos = mUserManagerInternal.getUserInfos();
+                for (UserInfo u : userInfos) {
+                    if (u.isPrivateProfile()) {
+                        return UserHandle.of(u.id);
+                    }
+                }
+                return UserHandle.of(UserHandle.USER_NULL);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4986594..06e29c2 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -21,6 +21,7 @@
 import static android.content.Intent.EXTRA_USER_ID;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.LauncherUserInfo.PRIVATE_SPACE_ENTRYPOINT_HIDDEN;
 import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
 import static android.content.pm.PackageManager.FEATURE_EMBEDDED;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
@@ -32,6 +33,7 @@
 import static android.os.UserManager.USER_OPERATION_ERROR_UNKNOWN;
 import static android.os.UserManager.USER_OPERATION_ERROR_USER_RESTRICTED;
 import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
+import static android.provider.Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT;
 
 import static com.android.internal.app.SetScreenLockDialogActivity.EXTRA_ORIGIN_USER_ID;
 import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_DISABLE_QUIET_MODE;
@@ -7946,11 +7948,25 @@
             }
             if (userInfo != null) {
                 final UserTypeDetails userDetails = getUserTypeDetails(userInfo);
-                final LauncherUserInfo uiInfo = new LauncherUserInfo.Builder(
-                        userDetails.getName(),
-                        userInfo.serialNumber)
-                        .build();
-                return uiInfo;
+
+                if (Flags.addLauncherUserConfig()) {
+                    Bundle config = new Bundle();
+                    if (userInfo.isPrivateProfile()) {
+                        try {
+                            int parentId = getProfileParentIdUnchecked(userId);
+                            config.putBoolean(PRIVATE_SPACE_ENTRYPOINT_HIDDEN,
+                                    Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                                            HIDE_PRIVATESPACE_ENTRY_POINT, parentId) == 1);
+                        } catch (Settings.SettingNotFoundException e) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+                    return new LauncherUserInfo.Builder(userDetails.getName(),
+                            userInfo.serialNumber, config).build();
+                }
+
+                return new LauncherUserInfo.Builder(userDetails.getName(),
+                        userInfo.serialNumber).build();
             } else {
                 return null;
             }