Better handling of backup/restore operations.

When the caller is a backup/restore app (as determined by the BACKUP
permission), then let them set the OWNER_PACKAGE_NAME column to
restore media ownership information.

Also update idle maintenance logic to examine pending install
sessions when deciding if media should be orphaned.  (Consider the
case where a backup/restore app has restored media ownership, but
the actual apps are still waiting to be installed.)

Bug: 141870626, 129848296
Test: atest --test-mapping packages/providers/MediaProvider
Change-Id: I66a0dd6668f3cf236eba5ab939aaeb55b235a91a
diff --git a/src/com/android/providers/media/LocalCallingIdentity.java b/src/com/android/providers/media/LocalCallingIdentity.java
index 0ba8936..e51c5db 100644
--- a/src/com/android/providers/media/LocalCallingIdentity.java
+++ b/src/com/android/providers/media/LocalCallingIdentity.java
@@ -21,15 +21,16 @@
 import static android.app.AppOpsManager.permissionToOp;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 
-import static com.android.providers.media.util.PermissionUtils.checkPermissionReadStorage;
-import static com.android.providers.media.util.PermissionUtils.checkPermissionWriteStorage;
 import static com.android.providers.media.util.PermissionUtils.checkIsLegacyStorageGranted;
+import static com.android.providers.media.util.PermissionUtils.checkPermissionBackup;
 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadAudio;
 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadImages;
+import static com.android.providers.media.util.PermissionUtils.checkPermissionReadStorage;
 import static com.android.providers.media.util.PermissionUtils.checkPermissionReadVideo;
 import static com.android.providers.media.util.PermissionUtils.checkPermissionSystem;
 import static com.android.providers.media.util.PermissionUtils.checkPermissionWriteAudio;
 import static com.android.providers.media.util.PermissionUtils.checkPermissionWriteImages;
+import static com.android.providers.media.util.PermissionUtils.checkPermissionWriteStorage;
 import static com.android.providers.media.util.PermissionUtils.checkPermissionWriteVideo;
 
 import android.annotation.Nullable;
@@ -183,6 +184,7 @@
     public static final int PERMISSION_WRITE_IMAGES = 1 << 8;
     public static final int PERMISSION_IS_LEGACY_READ = 1 << 9;
     public static final int PERMISSION_IS_LEGACY_GRANTED = 1 << 10;
+    public static final int PERMISSION_IS_BACKUP = 1 << 11;
 
     private int hasPermission;
     private int hasPermissionResolved;
@@ -201,6 +203,8 @@
         switch (permission) {
             case PERMISSION_IS_SYSTEM:
                 return isSystemInternal();
+            case PERMISSION_IS_BACKUP:
+                return isBackupInternal();
             case PERMISSION_IS_LEGACY_GRANTED:
                 return isLegacyStorageGranted();
             case PERMISSION_IS_LEGACY_WRITE:
@@ -230,6 +234,10 @@
         return checkPermissionSystem(context, pid, uid, getPackageName());
     }
 
+    private boolean isBackupInternal() {
+        return checkPermissionBackup(context, pid, uid);
+    }
+
     private boolean isLegacyStorageGranted() {
         return checkIsLegacyStorageGranted(context, uid, getPackageName());
     }
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 84fedc5..4ef3726 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -37,6 +37,7 @@
 import static android.provider.MediaStore.getVolumeName;
 import static android.provider.MediaStore.Downloads.isDownload;
 
+import static com.android.providers.media.LocalCallingIdentity.PERMISSION_IS_BACKUP;
 import static com.android.providers.media.LocalCallingIdentity.PERMISSION_IS_LEGACY_GRANTED;
 import static com.android.providers.media.LocalCallingIdentity.PERMISSION_IS_LEGACY_WRITE;
 import static com.android.providers.media.LocalCallingIdentity.PERMISSION_IS_LEGACY_READ;
@@ -74,6 +75,7 @@
 import android.content.OperationApplicationException;
 import android.content.SharedPreferences;
 import android.content.UriMatcher;
+import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PermissionGroupInfo;
@@ -679,6 +681,27 @@
         mCallingIdentity.set(token);
     }
 
+    private boolean isPackageKnown(@NonNull String packageName) {
+        final PackageManager pm = getContext().getPackageManager();
+
+        // First, is the app actually installed?
+        try {
+            pm.getPackageInfo(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+            return true;
+        } catch (NameNotFoundException ignored) {
+        }
+
+        // Second, is the app pending, probably from a backup/restore operation?
+        for (SessionInfo si : pm.getPackageInstaller().getAllSessions()) {
+            if (Objects.equals(packageName, si.getAppPackageName())) {
+                return true;
+            }
+        }
+
+        // I've never met this package in my life
+        return false;
+    }
+
     public void onIdleMaintenance(@NonNull CancellationSignal signal) {
         final long startTime = SystemClock.elapsedRealtime();
 
@@ -709,10 +732,8 @@
             while (c.moveToNext()) {
                 final String packageName = c.getString(0);
                 if (TextUtils.isEmpty(packageName)) continue;
-                try {
-                    getContext().getPackageManager().getPackageInfo(packageName,
-                            PackageManager.MATCH_UNINSTALLED_PACKAGES);
-                } catch (NameNotFoundException e) {
+
+                if (!isPackageKnown(packageName)) {
                     unknownPackages.add(packageName);
                 }
             }
@@ -2451,9 +2472,10 @@
                 initialValues.putNull(ImageColumns.LONGITUDE);
             }
 
-            if (isCallingPackageSystem()) {
-                // When media inserted by ourselves, the best we can do is guess
-                // ownership based on path.
+            if (isCallingPackageSystem() || isCallingPackageBackup()) {
+                // When media inserted by ourselves during a scan, or by a
+                // backup app, the best we can do is guess ownership based on
+                // path when it's not explicitly provided
                 ownerPackageName = initialValues.getAsString(FileColumns.OWNER_PACKAGE_NAME);
                 if (TextUtils.isEmpty(ownerPackageName)) {
                     ownerPackageName = extractPathOwnerPackageName(path);
@@ -6346,6 +6368,11 @@
     }
 
     @Deprecated
+    private boolean isCallingPackageBackup() {
+        return mCallingIdentity.get().hasPermission(PERMISSION_IS_BACKUP);
+    }
+
+    @Deprecated
     private boolean isCallingPackageLegacyWrite() {
         return mCallingIdentity.get().hasPermission(PERMISSION_IS_LEGACY_WRITE);
     }
diff --git a/src/com/android/providers/media/util/PermissionUtils.java b/src/com/android/providers/media/util/PermissionUtils.java
index 5e3fadb..732b0ce 100644
--- a/src/com/android/providers/media/util/PermissionUtils.java
+++ b/src/com/android/providers/media/util/PermissionUtils.java
@@ -16,6 +16,7 @@
 
 package com.android.providers.media.util;
 
+import static android.Manifest.permission.BACKUP;
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
@@ -63,6 +64,10 @@
         return hasFull && hasStorage;
     }
 
+    public static boolean checkPermissionBackup(Context context, int pid, int uid) {
+        return context.checkPermission(BACKUP, pid, uid) == PERMISSION_GRANTED;
+    }
+
     public static boolean checkPermissionWriteStorage(Context context,
             int pid, int uid, String packageName) {
         return checkPermissionAndAppOp(context, pid,