Generate data for user confirmation snippet

Compute the dialog title, message to show based on whether app is uninstalled from primary user or clone user.

Bug: 182205982
Test: builds successfully
Test: No CTS Tests. Flag to use new app is turned off by default

Change-Id: I5faf670edc98da58678dad6cd25e05eadc16e3c2
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java
index abf8a34..56681d9 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java
@@ -40,9 +40,11 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
+import com.android.packageinstaller.R;
 import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted;
 import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallReady;
 import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallStage;
+import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUserActionRequired;
 import java.util.List;
 
 public class UninstallRepository {
@@ -57,9 +59,11 @@
     private ApplicationInfo mTargetAppInfo;
     private ActivityInfo mTargetActivityInfo;
     private Intent mIntent;
+    private CharSequence mTargetAppLabel;
     private String mTargetPackageName;
     private String mCallingActivity;
     private boolean mUninstallFromAllUsers;
+    private boolean mIsClonedApp;
 
     public UninstallRepository(Context context) {
         mContext = context;
@@ -168,6 +172,96 @@
         return new UninstallReady();
     }
 
+    public UninstallStage generateUninstallDetails() {
+        UninstallUserActionRequired.Builder uarBuilder = new UninstallUserActionRequired.Builder();
+        StringBuilder messageBuilder = new StringBuilder();
+
+        mTargetAppLabel = mTargetAppInfo.loadSafeLabel(mPackageManager);
+
+        // If the Activity label differs from the App label, then make sure the user
+        // knows the Activity belongs to the App being uninstalled.
+        if (mTargetActivityInfo != null) {
+            final CharSequence activityLabel = mTargetActivityInfo.loadSafeLabel(mPackageManager);
+            if (CharSequence.compare(activityLabel, mTargetAppLabel) != 0) {
+                messageBuilder.append(
+                    mContext.getString(R.string.uninstall_activity_text, activityLabel));
+                messageBuilder.append(" ").append(mTargetAppLabel).append(".\n\n");
+            }
+        }
+
+        final boolean isUpdate =
+            (mTargetAppInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+        final UserHandle myUserHandle = Process.myUserHandle();
+        boolean isSingleUser = isSingleUser();
+
+        if (isUpdate) {
+            messageBuilder.append(mContext.getString(
+                isSingleUser ? R.string.uninstall_update_text :
+                    R.string.uninstall_update_text_multiuser));
+        } else if (mUninstallFromAllUsers && !isSingleUser) {
+            messageBuilder.append(mContext.getString(
+                R.string.uninstall_application_text_all_users));
+        } else if (isCloneProfile(mUninstalledUser)) {
+            mIsClonedApp = true;
+            messageBuilder.append(mContext.getString(
+                R.string.uninstall_application_text_current_user_clone_profile));
+        } else if (myUserHandle.equals(UserHandle.SYSTEM)
+            && hasClonedInstance(mTargetAppInfo.packageName)) {
+            messageBuilder.append(mContext.getString(
+                R.string.uninstall_application_text_with_clone_instance, mTargetAppLabel));
+        } else {
+            messageBuilder.append(mContext.getString(R.string.uninstall_application_text));
+        }
+
+        uarBuilder.setMessage(messageBuilder.toString());
+
+        if (mIsClonedApp) {
+            uarBuilder.setTitle(mContext.getString(R.string.cloned_app_label, mTargetAppLabel));
+        } else {
+            uarBuilder.setTitle(mTargetAppLabel.toString());
+        }
+
+        return uarBuilder.build();
+    }
+
+    /**
+     * Returns whether there is only one "full" user on this device.
+     *
+     * <p><b>Note:</b> on devices that use {@link android.os.UserManager#isHeadlessSystemUserMode()
+     * headless system user mode}, the system user is not "full", so it's not be considered in the
+     * calculation.</p>
+     */
+    private boolean isSingleUser() {
+        final int userCount = mUserManager.getUserCount();
+        return userCount == 1 || (UserManager.isHeadlessSystemUserMode() && userCount == 2);
+    }
+
+    private boolean hasClonedInstance(String packageName) {
+        // Check if clone user is present on the device.
+        UserHandle cloneUser = null;
+        List<UserHandle> profiles = mUserManager.getUserProfiles();
+        for (UserHandle userHandle : profiles) {
+            if (!userHandle.equals(UserHandle.SYSTEM) && isCloneProfile(userHandle)) {
+                cloneUser = userHandle;
+                break;
+            }
+        }
+        // Check if another instance of given package exists in clone user profile.
+        try {
+            return cloneUser != null
+                && mPackageManager.getPackageUidAsUser(packageName,
+                PackageManager.PackageInfoFlags.of(0), cloneUser.getIdentifier()) > 0;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    private boolean isCloneProfile(UserHandle userHandle) {
+        UserManager customUserManager = mContext.createContextAsUser(userHandle, 0)
+            .getSystemService(UserManager.class);
+        return customUserManager.isUserOfType(UserManager.USER_TYPE_PROFILE_CLONE);
+    }
+
     public static class CallerInfo {
 
         private final String mActivityName;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUserActionRequired.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUserActionRequired.java
new file mode 100644
index 0000000..3051dc4
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallUserActionRequired.java
@@ -0,0 +1,62 @@
+/*
+ * 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
+ *
+ *      https://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 com.android.packageinstaller.v2.model.uninstallstagedata;
+
+public class UninstallUserActionRequired extends UninstallStage {
+
+    private final int mStage = UninstallStage.STAGE_USER_ACTION_REQUIRED;
+    private final String mTitle;
+    private final String mMessage;
+
+    public UninstallUserActionRequired(String title, String message) {
+        mTitle = title;
+        mMessage = message;
+    }
+
+    public String getTitle() {
+        return mTitle;
+    }
+
+    public String getMessage() {
+        return mMessage;
+    }
+
+    @Override
+    public int getStageCode() {
+        return mStage;
+    }
+
+    public static class Builder {
+
+        private String mTitle;
+        private String mMessage;
+
+        public Builder setTitle(String title) {
+            mTitle = title;
+            return this;
+        }
+
+        public Builder setMessage(String message) {
+            mMessage = message;
+            return this;
+        }
+
+        public UninstallUserActionRequired build() {
+            return new UninstallUserActionRequired(mTitle, mMessage);
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.java
index e524bca..1697832 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/viewmodel/UninstallViewModel.java
@@ -42,6 +42,9 @@
 
     public void preprocessIntent(Intent intent, CallerInfo callerInfo) {
         UninstallStage stage = mRepository.performPreUninstallChecks(intent, callerInfo);
+        if (stage.getStageCode() != UninstallStage.STAGE_ABORTED) {
+            stage = mRepository.generateUninstallDetails();
+        }
         mCurrentUninstallStage.setValue(stage);
     }
 }