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,