Add additional checks on received uninstall request

Checks if the caller has uninstall permission and appOp, and if the app being uninstalled actually exists.

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

Change-Id: Ia19802845c049979cee745835d720a4f9b666aef
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 3a0e1e3..abf8a34 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.java
@@ -16,18 +16,26 @@
 
 package com.android.packageinstaller.v2.model;
 
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static com.android.packageinstaller.v2.model.PackageUtil.getMaxTargetSdkVersionForUid;
+import static com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid;
+import static com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted;
 import static com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted.ABORT_REASON_APP_UNAVAILABLE;
 import static com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted.ABORT_REASON_GENERIC_ERROR;
 import static com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted.ABORT_REASON_USER_NOT_ALLOWED;
 
+import android.Manifest;
+import android.app.AppOpsManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.UninstallCompleteCallback;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -41,10 +49,12 @@
 
     private static final String TAG = UninstallRepository.class.getSimpleName();
     private final Context mContext;
+    private final AppOpsManager mAppOpsManager;
     private final PackageManager mPackageManager;
     private final UserManager mUserManager;
     public UserHandle mUninstalledUser;
     public UninstallCompleteCallback mCallback;
+    private ApplicationInfo mTargetAppInfo;
     private ActivityInfo mTargetActivityInfo;
     private Intent mIntent;
     private String mTargetPackageName;
@@ -53,6 +63,7 @@
 
     public UninstallRepository(Context context) {
         mContext = context;
+        mAppOpsManager = context.getSystemService(AppOpsManager.class);
         mPackageManager = context.getPackageManager();
         mUserManager = context.getSystemService(UserManager.class);
     }
@@ -69,6 +80,30 @@
             // TODO: should we give any indication to the user?
         }
 
+        String callingPackage = getPackageNameForUid(mContext, callingUid, null);
+        if (callingPackage == null) {
+            Log.e(TAG, "Package not found for originating uid " + callingUid);
+            return new UninstallAborted(ABORT_REASON_GENERIC_ERROR);
+        } else {
+            if (mAppOpsManager.noteOpNoThrow(
+                AppOpsManager.OPSTR_REQUEST_DELETE_PACKAGES, callingUid, callingPackage)
+                != MODE_ALLOWED) {
+                Log.e(TAG, "Install from uid " + callingUid + " disallowed by AppOps");
+                return new UninstallAborted(ABORT_REASON_GENERIC_ERROR);
+            }
+        }
+
+        if (getMaxTargetSdkVersionForUid(mContext, callingUid) >= Build.VERSION_CODES.P
+            && !isPermissionGranted(mContext, Manifest.permission.REQUEST_DELETE_PACKAGES,
+            callingUid)
+            && !isPermissionGranted(mContext, Manifest.permission.DELETE_PACKAGES, callingUid)) {
+            Log.e(TAG, "Uid " + callingUid + " does not have "
+                + Manifest.permission.REQUEST_DELETE_PACKAGES + " or "
+                + Manifest.permission.DELETE_PACKAGES);
+
+            return new UninstallAborted(ABORT_REASON_GENERIC_ERROR);
+        }
+
         // Get intent information.
         // We expect an intent with URI of the form package:<packageName>#<className>
         // className is optional; if specified, it is the activity the user chose to uninstall
@@ -105,6 +140,18 @@
         mCallback = intent.getParcelableExtra(PackageInstaller.EXTRA_CALLBACK,
             PackageManager.UninstallCompleteCallback.class);
 
+        try {
+            mTargetAppInfo = mPackageManager.getApplicationInfo(mTargetPackageName,
+                PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_ANY_USER));
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Unable to get packageName");
+        }
+
+        if (mTargetAppInfo == null) {
+            Log.e(TAG, "Invalid packageName: " + mTargetPackageName);
+            return new UninstallAborted(ABORT_REASON_APP_UNAVAILABLE);
+        }
+
         // The class name may have been specified (e.g. when deleting an app from all apps)
         final String className = packageUri.getFragment();
         if (className != null) {