Merge "API for launcher User Awareness" into main
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 563ed7d..e9f419e 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.content.IntentSender;
import android.content.LocusId;
+import android.content.pm.LauncherUserInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IOnAppsChangedListener;
import android.content.pm.LauncherActivityInfoInternal;
@@ -62,6 +63,7 @@
in Bundle opts, in UserHandle user);
PendingIntent getActivityLaunchIntent(String callingPackage, in ComponentName component,
in UserHandle user);
+ LauncherUserInfo getLauncherUserInfo(in UserHandle user);
void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage,
String callingFeatureId, in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index dbaa4c9..0cd4358 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -20,6 +20,7 @@
import static android.Manifest.permission.READ_FRAME_BUFFER;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -55,6 +56,7 @@
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.Flags;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -776,6 +778,28 @@
}
/**
+ * Returns information related to a user which is useful for displaying UI elements
+ * to distinguish it from other users (eg, badges). Only system launchers should
+ * call this API.
+ *
+ * @param userHandle user handle of the user for which LauncherUserInfo is requested
+ * @return the LauncherUserInfo object related to the user specified.
+ * @hide
+ */
+ @Nullable
+ @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+ public final LauncherUserInfo getLauncherUserInfo(@NonNull UserHandle userHandle) {
+ if (DEBUG) {
+ Log.i(TAG, "getLauncherUserInfo " + userHandle);
+ }
+ try {
+ return mService.getLauncherUserInfo(userHandle);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
* returns null.
*
diff --git a/core/java/android/content/pm/LauncherUserInfo.aidl b/core/java/android/content/pm/LauncherUserInfo.aidl
new file mode 100644
index 0000000..f875f1e
--- /dev/null
+++ b/core/java/android/content/pm/LauncherUserInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content.pm;
+
+parcelable LauncherUserInfo;
diff --git a/core/java/android/content/pm/LauncherUserInfo.java b/core/java/android/content/pm/LauncherUserInfo.java
new file mode 100644
index 0000000..214c3e4
--- /dev/null
+++ b/core/java/android/content/pm/LauncherUserInfo.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+/**
+ * The LauncherUserInfo object holds information about an Android user that is required to display
+ * the Launcher related UI elements specific to the user (like badges).
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+public final class LauncherUserInfo implements Parcelable {
+
+ private final String mUserType;
+
+ // Serial number for the user, should be same as in the {@link UserInfo} object.
+ private final int mUserSerialNumber;
+
+ /**
+ * 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}
+ * TODO(b/303812736): Make the return type public and update javadoc here once the linked bug
+ * is resolved.
+ *
+ * @return the userType for the user whose LauncherUserInfo this is
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+ @NonNull
+ public String getUserType() {
+ return mUserType;
+ }
+
+ /**
+ * Returns serial number of user as returned by
+ * {@link UserManager#getSerialNumberForUser(UserHandle)}
+ *
+ * @return the serial number associated with the user
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+ public int getUserSerialNumber() {
+ return mUserSerialNumber;
+ }
+
+ private LauncherUserInfo(@NonNull Parcel in) {
+ mUserType = in.readString16NoHelper();
+ mUserSerialNumber = in.readInt();
+ }
+
+ @Override
+ @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString16NoHelper(mUserType);
+ dest.writeInt(mUserSerialNumber);
+ }
+
+ @Override
+ @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+ public int describeContents() {
+ return 0;
+ }
+
+ @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+ public static final @android.annotation.NonNull Creator<LauncherUserInfo> CREATOR =
+ new Creator<LauncherUserInfo>() {
+ @Override
+ public LauncherUserInfo createFromParcel(Parcel in) {
+ return new LauncherUserInfo(in);
+ }
+
+ @Override
+ public LauncherUserInfo[] newArray(int size) {
+ return new LauncherUserInfo[size];
+ }
+ };
+
+ /**
+ * @hide
+ */
+ public static final class Builder {
+ private final String mUserType;
+
+ private final int mUserSerialNumber;
+
+ public Builder(@NonNull String userType, int userSerialNumber) {
+ this.mUserType = userType;
+ this.mUserSerialNumber = userSerialNumber;
+ }
+
+ /**
+ * Builds the LauncherUserInfo object
+ */
+ @NonNull public LauncherUserInfo build() {
+ return new LauncherUserInfo(this.mUserType, this.mUserSerialNumber);
+ }
+
+ } // End builder
+
+ private LauncherUserInfo(@NonNull String userType, int userSerialNumber) {
+ this.mUserType = userType;
+ this.mUserSerialNumber = userSerialNumber;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index a161e8c..1135466 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -65,6 +65,7 @@
import android.content.pm.LauncherActivityInfoInternal;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.LauncherUserInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageManager;
@@ -1377,6 +1378,25 @@
}
@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;
+ }
+ long ident = injectClearCallingIdentity();
+ try {
+ return mUserManagerInternal.getLauncherUserInfo(user.getIdentifier());
+ } finally {
+ injectRestoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void startActivityAsUser(IApplicationThread caller, String callingPackage,
String callingFeatureId, ComponentName component, Rect sourceBounds,
Bundle opts, UserHandle user) throws RemoteException {
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 04cd183..0e7ce2e 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
+import android.content.pm.LauncherUserInfo;
import android.content.pm.UserInfo;
import android.content.pm.UserProperties;
import android.graphics.Bitmap;
@@ -407,6 +408,11 @@
public abstract @NonNull UserInfo[] getUserInfos();
/**
+ * Gets a {@link LauncherUserInfo} for the given {@code userId}, or {@code null} if not found.
+ */
+ public abstract @Nullable LauncherUserInfo getLauncherUserInfo(@UserIdInt int userId);
+
+ /**
* Sets all default cross profile intent filters between {@code parentUserId} and
* {@code profileUserId}.
*/
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 7331bc1..154ee6e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -62,6 +62,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.LauncherUserInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
@@ -7153,6 +7154,24 @@
}
@Override
+ public @Nullable LauncherUserInfo getLauncherUserInfo(@UserIdInt int userId) {
+ UserInfo userInfo;
+ synchronized (mUsersLock) {
+ userInfo = getUserInfoLU(userId);
+ }
+ if (userInfo != null) {
+ final UserTypeDetails userDetails = getUserTypeDetails(userInfo);
+ final LauncherUserInfo uiInfo = new LauncherUserInfo.Builder(
+ userDetails.getName(),
+ userInfo.serialNumber)
+ .build();
+ return uiInfo;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
int state;
synchronized (mUserStates) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 5dfce06..89c6a220 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -974,7 +974,6 @@
assertThrows(SecurityException.class, userProps::getAlwaysVisible);
}
-
// Make sure only max managed profiles can be created
@MediumTest
@Test