Handle uninstall result - Caller wants result back

When the caller requests for a result back, fill an intent with the uninstall status, set the correct activity result code and the activity result. The caller may also opt to get the result via UninstallCompleteCallback, in that case, send the result via the passed callback and finish the activity.

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

Change-Id: I49a6499a57287ee22307b7e99d28882fac6444d9
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 2f371a9..6b3f293 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java
@@ -27,6 +27,7 @@
 import static com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted.ABORT_REASON_USER_NOT_ALLOWED;
 
 import android.Manifest;
+import android.app.Activity;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.app.usage.StorageStats;
@@ -53,8 +54,10 @@
 import androidx.lifecycle.MutableLiveData;
 import com.android.packageinstaller.R;
 import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted;
+import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallFailed;
 import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallReady;
 import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallStage;
+import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallSuccess;
 import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUninstalling;
 import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUserActionRequired;
 import java.io.IOException;
@@ -432,6 +435,32 @@
 
     private void handleUninstallResult(int status, int legacyStatus, @Nullable String message,
         int serviceId) {
+        if (mCallback != null) {
+            // The caller will be informed about the result via a callback
+            mCallback.onUninstallComplete(mTargetPackageName, legacyStatus, message);
+
+            // Since the caller already received the results, just finish the app at this point
+            mUninstallResult.setValue(null);
+            return;
+        }
+
+        boolean returnResult = mIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
+        if (returnResult || mCallingActivity != null) {
+            Intent intent = new Intent();
+            intent.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus);
+
+            if (status == PackageInstaller.STATUS_SUCCESS) {
+                UninstallSuccess.Builder successBuilder = new UninstallSuccess.Builder()
+                    .setResultIntent(intent)
+                    .setActivityResultCode(Activity.RESULT_OK);
+                mUninstallResult.setValue(successBuilder.build());
+            } else {
+                UninstallFailed.Builder failedBuilder = new UninstallFailed.Builder(true)
+                    .setResultIntent(intent)
+                    .setActivityResultCode(Activity.RESULT_FIRST_USER);
+                mUninstallResult.setValue(failedBuilder.build());
+            }
+        }
     }
 
     /**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallFailed.java
new file mode 100644
index 0000000..dd91809
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallFailed.java
@@ -0,0 +1,83 @@
+/*
+ * 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;
+
+import android.app.Activity;
+import android.content.Intent;
+
+public class UninstallFailed extends UninstallStage {
+
+    private final int mStage = UninstallStage.STAGE_FAILED;
+    private final boolean mReturnResult;
+    /**
+     * If the caller wants the result back, the intent will hold the uninstall failure status code
+     * and legacy code.
+     */
+    private final Intent mResultIntent;
+    private final int mActivityResultCode;
+
+    public UninstallFailed(boolean returnResult, Intent resultIntent, int activityResultCode) {
+        mReturnResult = returnResult;
+        mResultIntent = resultIntent;
+        mActivityResultCode = activityResultCode;
+    }
+
+    public boolean returnResult() {
+        return mReturnResult;
+    }
+
+    public Intent getResultIntent() {
+        return mResultIntent;
+    }
+
+    public int getActivityResultCode() {
+        return mActivityResultCode;
+    }
+
+    @Override
+    public int getStageCode() {
+        return mStage;
+    }
+
+    public static class Builder {
+
+        private final boolean mReturnResult;
+        private int mActivityResultCode = Activity.RESULT_CANCELED;
+        /**
+         * See {@link UninstallFailed#mResultIntent}
+         */
+        private Intent mResultIntent = null;
+
+        public Builder(boolean returnResult) {
+            mReturnResult = returnResult;
+        }
+
+        public Builder setResultIntent(Intent intent) {
+            mResultIntent = intent;
+            return this;
+        }
+
+        public Builder setActivityResultCode(int resultCode) {
+            mActivityResultCode = resultCode;
+            return this;
+        }
+
+        public UninstallFailed build() {
+            return new UninstallFailed(mReturnResult, mResultIntent, mActivityResultCode);
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallSuccess.java
new file mode 100644
index 0000000..6d29306
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/uninstallstagedata/UninstallSuccess.java
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import android.content.Intent;
+
+public class UninstallSuccess extends UninstallStage {
+
+    private final int mStage = UninstallStage.STAGE_SUCCESS;
+    private final Intent mResultIntent;
+    private final int mActivityResultCode;
+
+    public UninstallSuccess(Intent resultIntent, int activityResultCode) {
+        mResultIntent = resultIntent;
+        mActivityResultCode = activityResultCode;
+    }
+
+    public Intent getResultIntent() {
+        return mResultIntent;
+    }
+
+    public int getActivityResultCode() {
+        return mActivityResultCode;
+    }
+
+    @Override
+    public int getStageCode() {
+        return mStage;
+    }
+
+    public static class Builder {
+
+        private Intent mResultIntent;
+        private int mActivityResultCode;
+
+        public Builder() {
+        }
+
+        public Builder setResultIntent(Intent intent) {
+            mResultIntent = intent;
+            return this;
+        }
+
+        public Builder setActivityResultCode(int resultCode) {
+            mActivityResultCode = resultCode;
+            return this;
+        }
+
+        public UninstallSuccess build() {
+            return new UninstallSuccess(mResultIntent, mActivityResultCode);
+        }
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.java
index 0c2eb50..2215980 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/UninstallLaunch.java
@@ -31,7 +31,9 @@
 import com.android.packageinstaller.v2.model.UninstallRepository;
 import com.android.packageinstaller.v2.model.UninstallRepository.CallerInfo;
 import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted;
+import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallFailed;
 import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallStage;
+import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallSuccess;
 import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUninstalling;
 import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUserActionRequired;
 import com.android.packageinstaller.v2.ui.fragments.UninstallConfirmationFragment;
@@ -106,6 +108,12 @@
             UninstallUninstallingFragment uninstallingDialog = new UninstallUninstallingFragment(
                 uninstalling);
             showDialogInner(uninstallingDialog);
+        } else if (uninstallStage.getStageCode() == UninstallStage.STAGE_FAILED) {
+            UninstallFailed failed = (UninstallFailed) uninstallStage;
+            setResult(failed.getActivityResultCode(), failed.getResultIntent(), true);
+        } else if (uninstallStage.getStageCode() == UninstallStage.STAGE_SUCCESS) {
+            UninstallSuccess success = (UninstallSuccess) uninstallStage;
+            setResult(success.getActivityResultCode(), success.getResultIntent(), true);
         } else {
             Log.e(TAG, "Invalid stage: " + uninstallStage.getStageCode());
             showDialogInner(null);