summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt8
-rw-r--r--api/removed.txt18
-rwxr-xr-xapi/system-current.txt5
-rw-r--r--api/test-current.txt11
-rw-r--r--core/java/android/app/DownloadManager.java4
-rw-r--r--core/java/android/content/pm/PackageManager.java10
-rw-r--r--core/java/android/os/Environment.java18
-rw-r--r--core/java/android/os/storage/StorageManager.java35
-rw-r--r--core/java/android/os/storage/StorageVolume.java46
-rw-r--r--core/java/android/os/storage/VolumeRecord.java25
-rw-r--r--core/java/android/provider/BaseColumns.java4
-rw-r--r--core/java/android/provider/MediaStore.java697
-rw-r--r--core/java/com/android/internal/content/FileSystemProvider.java6
-rw-r--r--media/java/android/media/MediaScannerConnection.java3
-rw-r--r--media/java/android/media/RingtoneManager.java2
-rw-r--r--media/java/android/media/ThumbnailUtils.java13
-rwxr-xr-xmedia/java/android/mtp/MtpDatabase.java16
-rw-r--r--media/java/android/mtp/MtpStorage.java6
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/RingtoneOverlayService.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java38
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java42
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java8
23 files changed, 359 insertions, 661 deletions
diff --git a/api/current.txt b/api/current.txt
index 6ddd847435f4..7c0a134063c1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36025,7 +36025,8 @@ package android.os.storage {
method @WorkerThread public long getCacheSizeBytes(@NonNull java.util.UUID) throws java.io.IOException;
method public String getMountedObbPath(String);
method @NonNull public android.os.storage.StorageVolume getPrimaryStorageVolume();
- method @Nullable public android.os.storage.StorageVolume getStorageVolume(java.io.File);
+ method @NonNull public java.util.List<android.os.storage.StorageVolume> getRecentStorageVolumes();
+ method @Nullable public android.os.storage.StorageVolume getStorageVolume(@NonNull java.io.File);
method @NonNull public android.os.storage.StorageVolume getStorageVolume(@NonNull android.net.Uri);
method @NonNull public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
method @NonNull public java.util.UUID getUuidForPath(@NonNull java.io.File) throws java.io.IOException;
@@ -36051,6 +36052,8 @@ package android.os.storage {
method @NonNull public android.content.Intent createOpenDocumentTreeIntent();
method public int describeContents();
method public String getDescription(android.content.Context);
+ method @Nullable public java.io.File getDirectory();
+ method @Nullable public String getMediaStoreVolumeName();
method public String getState();
method @Nullable public String getUuid();
method public boolean isEmulated();
@@ -38721,6 +38724,7 @@ package android.provider {
method @NonNull public static java.util.Set<java.lang.String> getExternalVolumeNames(@NonNull android.content.Context);
method public static android.net.Uri getMediaScannerUri();
method @Nullable public static android.net.Uri getMediaUri(@NonNull android.content.Context, @NonNull android.net.Uri);
+ method @NonNull public static java.util.Set<java.lang.String> getRecentExternalVolumeNames(@NonNull android.content.Context);
method public static boolean getRequireOriginal(@NonNull android.net.Uri);
method @NonNull public static String getVersion(@NonNull android.content.Context);
method @NonNull public static String getVersion(@NonNull android.content.Context, @NonNull String);
@@ -38986,6 +38990,7 @@ package android.provider {
method @Deprecated public static void cancelThumbnailRequest(android.content.ContentResolver, long);
method @Deprecated public static void cancelThumbnailRequest(android.content.ContentResolver, long, long);
method @Deprecated public static android.net.Uri getContentUri(String);
+ method @Deprecated @NonNull public static android.util.Size getKindSize(int);
method @Deprecated public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
method @Deprecated public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
method @Deprecated public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]);
@@ -39068,6 +39073,7 @@ package android.provider {
method @Deprecated public static void cancelThumbnailRequest(android.content.ContentResolver, long);
method @Deprecated public static void cancelThumbnailRequest(android.content.ContentResolver, long, long);
method @Deprecated public static android.net.Uri getContentUri(String);
+ method @Deprecated @NonNull public static android.util.Size getKindSize(int);
method @Deprecated public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
method @Deprecated public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
field @Deprecated public static final String DATA = "_data";
diff --git a/api/removed.txt b/api/removed.txt
index 4e8e325bce15..8b30d0a5cf39 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -436,10 +436,8 @@ package android.provider {
}
public final class MediaStore {
- method @Deprecated @NonNull public static android.net.Uri createPending(@NonNull android.content.Context, @NonNull android.provider.MediaStore.PendingParams);
method @Deprecated @NonNull public static java.util.Set<java.lang.String> getAllVolumeNames(@NonNull android.content.Context);
method @Deprecated public static boolean getIncludePending(@NonNull android.net.Uri);
- method @Deprecated @NonNull public static android.provider.MediaStore.PendingSession openPending(@NonNull android.content.Context, @NonNull android.net.Uri);
method @Deprecated @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri);
method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri);
method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long);
@@ -473,22 +471,6 @@ package android.provider {
field @Deprecated public static final String GROUP_ID = "group_id";
}
- @Deprecated public static class MediaStore.PendingParams {
- ctor public MediaStore.PendingParams(@NonNull android.net.Uri, @NonNull String, @NonNull String);
- method public void setDownloadUri(@Nullable android.net.Uri);
- method public void setRefererUri(@Nullable android.net.Uri);
- method public void setRelativePath(@Nullable String);
- }
-
- @Deprecated public static class MediaStore.PendingSession implements java.lang.AutoCloseable {
- method public void abandon();
- method public void close();
- method public void notifyProgress(@IntRange(from=0, to=100) int);
- method @NonNull public android.os.ParcelFileDescriptor open() throws java.io.FileNotFoundException;
- method @NonNull public java.io.OutputStream openOutputStream() throws java.io.FileNotFoundException;
- method @NonNull public android.net.Uri publish();
- }
-
public static interface MediaStore.Video.VideoColumns extends android.provider.MediaStore.MediaColumns {
field public static final String ALBUM = "album";
field public static final String ARTIST = "artist";
diff --git a/api/system-current.txt b/api/system-current.txt
index 8d55a33faedd..8943a155d199 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6365,6 +6365,7 @@ package android.os {
}
public class Environment {
+ method @NonNull public static java.util.Collection<java.io.File> getInternalMediaDirectories();
method @NonNull public static java.io.File getOdmDirectory();
method @NonNull public static java.io.File getOemDirectory();
method @NonNull public static java.io.File getProductDirectory();
@@ -7253,8 +7254,8 @@ package android.provider {
}
public final class MediaStore {
- method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) public static java.util.Set<java.lang.String> getRecentExternalVolumeNames(@NonNull android.content.Context);
- method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) public static java.util.Collection<java.io.File> getVolumeScanPaths(@NonNull String) throws java.io.FileNotFoundException;
+ method @NonNull public static android.net.Uri scanFile(@NonNull android.content.ContentResolver, @NonNull java.io.File);
+ method public static void scanVolume(@NonNull android.content.ContentResolver, @NonNull String);
}
public abstract class SearchIndexableData {
diff --git a/api/test-current.txt b/api/test-current.txt
index 1c6bce05b8f8..5381376d7568 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2467,14 +2467,9 @@ package android.provider {
}
public final class MediaStore {
- method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static void deleteContributedMedia(android.content.Context, String, android.os.UserHandle) throws java.io.IOException;
- method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static long getContributedMediaSize(android.content.Context, String, android.os.UserHandle) throws java.io.IOException;
- method @NonNull public static java.io.File getVolumePath(@NonNull String) throws java.io.FileNotFoundException;
- method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) public static java.util.Collection<java.io.File> getVolumeScanPaths(@NonNull String) throws java.io.FileNotFoundException;
- method public static android.net.Uri scanFile(android.content.Context, java.io.File);
- method public static android.net.Uri scanFileFromShell(android.content.Context, java.io.File);
- method public static void scanVolume(android.content.Context, java.io.File);
- method public static void waitForIdle(android.content.Context);
+ method @NonNull public static android.net.Uri scanFile(@NonNull android.content.ContentResolver, @NonNull java.io.File);
+ method public static void scanVolume(@NonNull android.content.ContentResolver, @NonNull String);
+ method public static void waitForIdle(@NonNull android.content.ContentResolver);
}
public final class Settings {
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 80c9ba2a9c48..eb50581f3319 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -1314,8 +1314,8 @@ public class DownloadManager {
// TODO: DownloadProvider.update() should take care of updating corresponding
// MediaProvider entries.
- MediaStore.scanFile(context, before);
- MediaStore.scanFile(context, after);
+ MediaStore.scanFile(mResolver, before);
+ MediaStore.scanFile(mResolver, after);
final ContentValues values = new ContentValues();
values.put(Downloads.Impl.COLUMN_TITLE, displayName);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 94af5416aa8d..0d1f4046e70e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1513,16 +1513,6 @@ public abstract class PackageManager {
public static final int DELETE_DONT_KILL_APP = 0x00000008;
/**
- * Flag parameter for {@link #deletePackage} to indicate that any
- * contributed media should also be deleted during this uninstall. The
- * meaning of "contributed" means it won't automatically be deleted when the
- * app is uninstalled.
- *
- * @hide
- */
- public static final int DELETE_CONTRIBUTED_MEDIA = 0x00000010;
-
- /**
* Flag parameter for {@link #deletePackage} to indicate that package deletion
* should be chatty.
*
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index a92237b9f17f..61da5e67e57b 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -34,6 +34,8 @@ import android.text.TextUtils;
import android.util.Log;
import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.LinkedList;
/**
@@ -528,6 +530,22 @@ public class Environment {
}
/**
+ * Return locations where media files (such as ringtones, notification
+ * sounds, or alarm sounds) may be located on internal storage. These are
+ * typically indexed under {@link MediaStore#VOLUME_INTERNAL}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static @NonNull Collection<File> getInternalMediaDirectories() {
+ final ArrayList<File> res = new ArrayList<>();
+ res.add(new File(Environment.getRootDirectory(), "media"));
+ res.add(new File(Environment.getOemDirectory(), "media"));
+ res.add(new File(Environment.getProductDirectory(), "media"));
+ return res;
+ }
+
+ /**
* Return the primary shared/external storage directory. This directory may
* not currently be accessible if it has been mounted by the user on their
* computer, has been removed from the device, or some other problem has
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 62603fee1137..2e9f27e74544 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -264,6 +264,8 @@ public class StorageManager {
public static final int FLAG_REAL_STATE = 1 << 9;
/** {@hide} */
public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10;
+ /** {@hide} */
+ public static final int FLAG_INCLUDE_RECENT = 1 << 11;
/** {@hide} */
public static final int FSTRIM_FLAG_DEEP = IVold.FSTRIM_FLAG_DEEP_TRIM;
@@ -1125,7 +1127,7 @@ public class StorageManager {
* Return the {@link StorageVolume} that contains the given file, or
* {@code null} if none.
*/
- public @Nullable StorageVolume getStorageVolume(File file) {
+ public @Nullable StorageVolume getStorageVolume(@NonNull File file) {
return getStorageVolume(getVolumeList(), file);
}
@@ -1140,7 +1142,7 @@ public class StorageManager {
return getPrimaryStorageVolume();
default:
for (StorageVolume vol : getStorageVolumes()) {
- if (Objects.equals(vol.getNormalizedUuid(), volumeName)) {
+ if (Objects.equals(vol.getMediaStoreVolumeName(), volumeName)) {
return vol;
}
}
@@ -1201,12 +1203,13 @@ public class StorageManager {
}
/**
- * Return the list of shared/external storage volumes available to the
- * current user. This includes both the primary shared storage device and
- * any attached external volumes including SD cards and USB drives.
- *
- * @see Environment#getExternalStorageDirectory()
- * @see StorageVolume#createAccessIntent(String)
+ * Return the list of shared/external storage volumes currently available to
+ * the calling user.
+ * <p>
+ * These storage volumes are actively attached to the device, but may be in
+ * any mount state, as returned by {@link StorageVolume#getState()}. Returns
+ * both the primary shared storage device and any attached external volumes,
+ * including SD cards and USB drives.
*/
public @NonNull List<StorageVolume> getStorageVolumes() {
final ArrayList<StorageVolume> res = new ArrayList<>();
@@ -1216,6 +1219,22 @@ public class StorageManager {
}
/**
+ * Return the list of shared/external storage volumes both currently and
+ * recently available to the calling user.
+ * <p>
+ * Recently available storage volumes are likely to reappear in the future,
+ * so apps are encouraged to preserve any indexed metadata related to these
+ * volumes to optimize user experiences.
+ */
+ public @NonNull List<StorageVolume> getRecentStorageVolumes() {
+ final ArrayList<StorageVolume> res = new ArrayList<>();
+ Collections.addAll(res,
+ getVolumeList(mContext.getUserId(),
+ FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE | FLAG_INCLUDE_RECENT));
+ return res;
+ }
+
+ /**
* Return the primary shared/external storage volume available to the
* current user. This volume is the same storage device returned by
* {@link Environment#getExternalStorageDirectory()} and
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index aefe8430f9de..560d6171d5ee 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -29,6 +29,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.provider.DocumentsContract;
+import android.provider.MediaStore;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -173,7 +174,7 @@ public final class StorageVolume implements Parcelable {
* @return the mount path
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@link StorageVolume#getDirectory()}")
@TestApi
public String getPath() {
return mPath.toString();
@@ -190,12 +191,35 @@ public final class StorageVolume implements Parcelable {
}
/** {@hide} */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@link StorageVolume#getDirectory()}")
public File getPathFile() {
return mPath;
}
/**
+ * Returns the directory where this volume is currently mounted.
+ * <p>
+ * Direct filesystem access via this path has significant emulation
+ * overhead, and apps are instead strongly encouraged to interact with media
+ * on storage volumes via the {@link MediaStore} APIs.
+ * <p>
+ * This directory does not give apps any additional access beyond what they
+ * already have via {@link MediaStore}.
+ *
+ * @return directory where this volume is mounted, or {@code null} if the
+ * volume is not currently mounted.
+ */
+ public @Nullable File getDirectory() {
+ switch (mState) {
+ case Environment.MEDIA_MOUNTED:
+ case Environment.MEDIA_MOUNTED_READ_ONLY:
+ return mPath;
+ default:
+ return null;
+ }
+ }
+
+ /**
* Returns a user-visible description of the volume.
*
* @return the volume description
@@ -265,6 +289,24 @@ public final class StorageVolume implements Parcelable {
return mFsUuid;
}
+ /**
+ * Return the volume name that can be used to interact with this storage
+ * device through {@link MediaStore}.
+ *
+ * @return opaque volume name, or {@code null} if this volume is not indexed
+ * by {@link MediaStore}.
+ * @see android.provider.MediaStore.Audio.Media#getContentUri(String)
+ * @see android.provider.MediaStore.Video.Media#getContentUri(String)
+ * @see android.provider.MediaStore.Images.Media#getContentUri(String)
+ */
+ public @Nullable String getMediaStoreVolumeName() {
+ if (isPrimary()) {
+ return MediaStore.VOLUME_EXTERNAL_PRIMARY;
+ } else {
+ return getNormalizedUuid();
+ }
+ }
+
/** {@hide} */
public static @Nullable String normalizeUuid(@Nullable String fsUuid) {
return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null;
diff --git a/core/java/android/os/storage/VolumeRecord.java b/core/java/android/os/storage/VolumeRecord.java
index 1a794ebf2a59..99b45d60c319 100644
--- a/core/java/android/os/storage/VolumeRecord.java
+++ b/core/java/android/os/storage/VolumeRecord.java
@@ -17,14 +17,18 @@
package android.os.storage;
import android.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.util.DebugUtils;
import android.util.TimeUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import java.io.File;
import java.util.Locale;
import java.util.Objects;
@@ -92,6 +96,27 @@ public class VolumeRecord implements Parcelable {
return (userFlags & USER_FLAG_SNOOZED) != 0;
}
+ public StorageVolume buildStorageVolume(Context context) {
+ final String id = "unknown:" + fsUuid;
+ final File userPath = new File("/dev/null");
+ final File internalPath = new File("/dev/null");
+ final boolean primary = false;
+ final boolean removable = true;
+ final boolean emulated = false;
+ final boolean allowMassStorage = false;
+ final long maxFileSize = 0;
+ final UserHandle user = new UserHandle(UserHandle.USER_NULL);
+ final String envState = Environment.MEDIA_UNKNOWN;
+
+ String description = nickname;
+ if (description == null) {
+ description = context.getString(android.R.string.unknownName);
+ }
+
+ return new StorageVolume(id, userPath, internalPath, description, primary, removable,
+ emulated, allowMassStorage, maxFileSize, user, fsUuid, envState);
+ }
+
public void dump(IndentingPrintWriter pw) {
pw.println("VolumeRecord:");
pw.increaseIndent();
diff --git a/core/java/android/provider/BaseColumns.java b/core/java/android/provider/BaseColumns.java
index 00c9e72df880..b216e2b7ed8d 100644
--- a/core/java/android/provider/BaseColumns.java
+++ b/core/java/android/provider/BaseColumns.java
@@ -16,13 +16,11 @@
package android.provider;
-import android.database.Cursor;
-
public interface BaseColumns {
/**
* The unique ID for a row.
*/
- @Column(Cursor.FIELD_TYPE_INTEGER)
+ // @Column(Cursor.FIELD_TYPE_INTEGER)
public static final String _ID = "_id";
/**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 701ba91a8daf..63204d36f396 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -21,17 +21,15 @@ import android.annotation.CurrentTimeMillisLong;
import android.annotation.CurrentTimeSecondsLong;
import android.annotation.DurationMillisLong;
import android.annotation.IntDef;
-import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
-import android.app.AppGlobals;
import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ContentProviderClient;
@@ -45,39 +43,28 @@ import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageDecoder;
-import android.graphics.Point;
import android.graphics.PostProcessor;
import android.media.ExifInterface;
-import android.media.MediaFile;
import android.media.MediaFormat;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Environment;
-import android.os.FileUtils;
import android.os.OperationCanceledException;
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
-import android.os.storage.VolumeInfo;
-import android.os.storage.VolumeRecord;
-import android.service.media.CameraPrewarmService;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
+import android.util.Size;
import libcore.util.HexEncoding;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -162,9 +149,6 @@ public final class MediaStore {
/** {@hide} */
public static final String SCAN_VOLUME_CALL = "scan_volume";
/** {@hide} */
- public static final String SUICIDE_CALL = "suicide";
-
- /** {@hide} */
public static final String CREATE_WRITE_REQUEST_CALL = "create_write_request";
/** {@hide} */
public static final String CREATE_TRASH_REQUEST_CALL = "create_trash_request";
@@ -173,42 +157,19 @@ public final class MediaStore {
/** {@hide} */
public static final String CREATE_DELETE_REQUEST_CALL = "create_delete_request";
- /**
- * Extra used with {@link #SCAN_FILE_CALL} or {@link #SCAN_VOLUME_CALL} to indicate that
- * the file path originated from shell.
- *
- * {@hide}
- */
- public static final String EXTRA_ORIGINATED_FROM_SHELL =
- "android.intent.extra.originated_from_shell";
-
- /**
- * The method name used by the media scanner and mtp to tell the media provider to
- * rescan and reclassify that have become unhidden because of renaming folders or
- * removing nomedia files
- * @hide
- */
- @Deprecated
- public static final String UNHIDE_CALL = "unhide";
-
- /**
- * The method name used by the media scanner service to reload all localized ringtone titles due
- * to a locale change.
- * @hide
- */
- public static final String RETRANSLATE_CALL = "update_titles";
/** {@hide} */
public static final String GET_VERSION_CALL = "get_version";
+
/** {@hide} */
public static final String GET_DOCUMENT_URI_CALL = "get_document_uri";
/** {@hide} */
public static final String GET_MEDIA_URI_CALL = "get_media_uri";
/** {@hide} */
- public static final String GET_CONTRIBUTED_MEDIA_CALL = "get_contributed_media";
+ public static final String EXTRA_URI = "uri";
/** {@hide} */
- public static final String DELETE_CONTRIBUTED_MEDIA_CALL = "delete_contributed_media";
+ public static final String EXTRA_URI_PERMISSIONS = "uriPermissions";
/** {@hide} */
public static final String EXTRA_CLIP_DATA = "clip_data";
@@ -391,10 +352,10 @@ public final class MediaStore {
* service.
* <p>
* This meta-data should reference the fully qualified class name of the prewarm service
- * extending {@link CameraPrewarmService}.
+ * extending {@code CameraPrewarmService}.
* <p>
* The prewarm service will get bound and receive a prewarm signal
- * {@link CameraPrewarmService#onPrewarm()} when a camera launch intent fire might be imminent.
+ * {@code CameraPrewarmService#onPrewarm()} when a camera launch intent fire might be imminent.
* An application implementing a prewarm service should do the absolute minimum amount of work
* to initialize the camera in order to reduce startup time in likely case that shortly after a
* camera launch intent would be sent.
@@ -775,197 +736,6 @@ public final class MediaStore {
}
/**
- * Create a new pending media item using the given parameters. Pending items
- * are expected to have a short lifetime, and owners should either
- * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a
- * pending item within a few hours after first creating it.
- *
- * @return token which can be passed to {@link #openPending(Context, Uri)}
- * to work with this pending item.
- * @see MediaColumns#IS_PENDING
- * @see MediaStore#setIncludePending(Uri)
- * @see MediaStore#createPending(Context, PendingParams)
- * @removed
- */
- @Deprecated
- public static @NonNull Uri createPending(@NonNull Context context,
- @NonNull PendingParams params) {
- return context.getContentResolver().insert(params.insertUri, params.insertValues);
- }
-
- /**
- * Open a pending media item to make progress on it. You can open a pending
- * item multiple times before finally calling either
- * {@link PendingSession#publish()} or {@link PendingSession#abandon()}.
- *
- * @param uri token which was previously returned from
- * {@link #createPending(Context, PendingParams)}.
- * @removed
- */
- @Deprecated
- public static @NonNull PendingSession openPending(@NonNull Context context, @NonNull Uri uri) {
- return new PendingSession(context, uri);
- }
-
- /**
- * Parameters that describe a pending media item.
- *
- * @removed
- */
- @Deprecated
- public static class PendingParams {
- /** {@hide} */
- public final Uri insertUri;
- /** {@hide} */
- public final ContentValues insertValues;
-
- /**
- * Create parameters that describe a pending media item.
- *
- * @param insertUri the {@code content://} Uri where this pending item
- * should be inserted when finally published. For example, to
- * publish an image, use
- * {@link MediaStore.Images.Media#getContentUri(String)}.
- */
- public PendingParams(@NonNull Uri insertUri, @NonNull String displayName,
- @NonNull String mimeType) {
- this.insertUri = Objects.requireNonNull(insertUri);
- final long now = System.currentTimeMillis() / 1000;
- this.insertValues = new ContentValues();
- this.insertValues.put(MediaColumns.DISPLAY_NAME, Objects.requireNonNull(displayName));
- this.insertValues.put(MediaColumns.MIME_TYPE, Objects.requireNonNull(mimeType));
- this.insertValues.put(MediaColumns.DATE_ADDED, now);
- this.insertValues.put(MediaColumns.DATE_MODIFIED, now);
- this.insertValues.put(MediaColumns.IS_PENDING, 1);
- this.insertValues.put(MediaColumns.DATE_EXPIRES,
- (System.currentTimeMillis() + DateUtils.DAY_IN_MILLIS) / 1000);
- }
-
- public void setRelativePath(@Nullable String relativePath) {
- if (relativePath == null) {
- this.insertValues.remove(MediaColumns.RELATIVE_PATH);
- } else {
- this.insertValues.put(MediaColumns.RELATIVE_PATH, relativePath);
- }
- }
-
- /**
- * Optionally set the Uri from where the file has been downloaded. This is used
- * for files being added to {@link Downloads} table.
- *
- * @see DownloadColumns#DOWNLOAD_URI
- */
- public void setDownloadUri(@Nullable Uri downloadUri) {
- if (downloadUri == null) {
- this.insertValues.remove(DownloadColumns.DOWNLOAD_URI);
- } else {
- this.insertValues.put(DownloadColumns.DOWNLOAD_URI, downloadUri.toString());
- }
- }
-
- /**
- * Optionally set the Uri indicating HTTP referer of the file. This is used for
- * files being added to {@link Downloads} table.
- *
- * @see DownloadColumns#REFERER_URI
- */
- public void setRefererUri(@Nullable Uri refererUri) {
- if (refererUri == null) {
- this.insertValues.remove(DownloadColumns.REFERER_URI);
- } else {
- this.insertValues.put(DownloadColumns.REFERER_URI, refererUri.toString());
- }
- }
- }
-
- /**
- * Session actively working on a pending media item. Pending items are
- * expected to have a short lifetime, and owners should either
- * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a
- * pending item within a few hours after first creating it.
- *
- * @removed
- */
- @Deprecated
- public static class PendingSession implements AutoCloseable {
- /** {@hide} */
- private final Context mContext;
- /** {@hide} */
- private final Uri mUri;
-
- /** {@hide} */
- public PendingSession(Context context, Uri uri) {
- mContext = Objects.requireNonNull(context);
- mUri = Objects.requireNonNull(uri);
- }
-
- /**
- * Open the underlying file representing this media item. When a media
- * item is successfully completed, you should
- * {@link ParcelFileDescriptor#close()} and then {@link #publish()} it.
- *
- * @see #notifyProgress(int)
- */
- public @NonNull ParcelFileDescriptor open() throws FileNotFoundException {
- return mContext.getContentResolver().openFileDescriptor(mUri, "rw");
- }
-
- /**
- * Open the underlying file representing this media item. When a media
- * item is successfully completed, you should
- * {@link OutputStream#close()} and then {@link #publish()} it.
- *
- * @see #notifyProgress(int)
- */
- public @NonNull OutputStream openOutputStream() throws FileNotFoundException {
- return mContext.getContentResolver().openOutputStream(mUri);
- }
-
- /**
- * Notify of current progress on this pending media item. Gallery
- * applications may choose to surface progress information of this
- * pending item.
- *
- * @param progress a percentage between 0 and 100.
- */
- public void notifyProgress(@IntRange(from = 0, to = 100) int progress) {
- final Uri withProgress = mUri.buildUpon()
- .appendQueryParameter(PARAM_PROGRESS, Integer.toString(progress)).build();
- mContext.getContentResolver().notifyChange(withProgress, null, 0);
- }
-
- /**
- * When this media item is successfully completed, call this method to
- * publish and make the final item visible to the user.
- *
- * @return the final {@code content://} Uri representing the newly
- * published media.
- */
- public @NonNull Uri publish() {
- final ContentValues values = new ContentValues();
- values.put(MediaColumns.IS_PENDING, 0);
- values.putNull(MediaColumns.DATE_EXPIRES);
- mContext.getContentResolver().update(mUri, values, null, null);
- return mUri;
- }
-
- /**
- * When this media item has failed to be completed, call this method to
- * destroy the pending item record and any data related to it.
- */
- public void abandon() {
- mContext.getContentResolver().delete(mUri, null, null);
- }
-
- @Override
- public void close() {
- // No resources to close, but at least we can inform people that no
- // progress is being actively made.
- notifyProgress(-1);
- }
- }
-
- /**
* Mark the given item as being "trashed", meaning it should be deleted at
* some point in the future. This is a more gentle operation than simply
* calling {@link ContentResolver#delete(Uri, String, String[])}, which
@@ -1701,34 +1471,22 @@ public final class MediaStore {
return ContentUris.withAppendedId(getContentUri(volumeName), rowId);
}
- /**
- * For use only by the MTP implementation.
- * @hide
- */
+ /** {@hide} */
@UnsupportedAppUsage
- public static Uri getMtpObjectsUri(String volumeName) {
- return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("object").build();
+ public static Uri getMtpObjectsUri(@NonNull String volumeName) {
+ return MediaStore.Files.getContentUri(volumeName);
}
- /**
- * For use only by the MTP implementation.
- * @hide
- */
+ /** {@hide} */
@UnsupportedAppUsage
- public static final Uri getMtpObjectsUri(String volumeName,
- long fileId) {
- return ContentUris.withAppendedId(getMtpObjectsUri(volumeName), fileId);
+ public static final Uri getMtpObjectsUri(@NonNull String volumeName, long fileId) {
+ return MediaStore.Files.getContentUri(volumeName, fileId);
}
- /**
- * Used to implement the MTP GetObjectReferences and SetObjectReferences commands.
- * @hide
- */
+ /** {@hide} */
@UnsupportedAppUsage
- public static final Uri getMtpReferencesUri(String volumeName,
- long fileId) {
- return getMtpObjectsUri(volumeName, fileId).buildUpon().appendPath("references")
- .build();
+ public static final Uri getMtpReferencesUri(@NonNull String volumeName, long fileId) {
+ return MediaStore.Files.getContentUri(volumeName, fileId);
}
/**
@@ -1851,9 +1609,21 @@ public final class MediaStore {
public static final int FULL_SCREEN_KIND = 2;
public static final int MICRO_KIND = 3;
- public static final Point MINI_SIZE = new Point(512, 384);
- public static final Point FULL_SCREEN_SIZE = new Point(1024, 786);
- public static final Point MICRO_SIZE = new Point(96, 96);
+ public static final Size MINI_SIZE = new Size(512, 384);
+ public static final Size FULL_SCREEN_SIZE = new Size(1024, 786);
+ public static final Size MICRO_SIZE = new Size(96, 96);
+
+ public static @NonNull Size getKindSize(int kind) {
+ if (kind == ThumbnailConstants.MICRO_KIND) {
+ return ThumbnailConstants.MICRO_SIZE;
+ } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) {
+ return ThumbnailConstants.FULL_SCREEN_SIZE;
+ } else if (kind == ThumbnailConstants.MINI_KIND) {
+ return ThumbnailConstants.MINI_SIZE;
+ } else {
+ throw new IllegalArgumentException("Unsupported kind: " + kind);
+ }
+ }
}
/**
@@ -1957,22 +1727,22 @@ public final class MediaStore {
}
}
- /** {@hide} */
+ /**
+ * @deprecated since this method doesn't have a {@link Context}, we can't
+ * find the actual {@link StorageVolume} for the given path, so
+ * only a vague guess is returned. Callers should use
+ * {@link StorageManager#getStorageVolume(File)} instead.
+ * @hide
+ */
+ @Deprecated
public static @NonNull String getVolumeName(@NonNull File path) {
- if (FileUtils.contains(Environment.getStorageDirectory(), path)) {
- final StorageManager sm = AppGlobals.getInitialApplication()
- .getSystemService(StorageManager.class);
- final StorageVolume sv = sm.getStorageVolume(path);
- if (sv != null) {
- if (sv.isPrimary()) {
- return VOLUME_EXTERNAL_PRIMARY;
- } else {
- return checkArgumentVolumeName(sv.getNormalizedUuid());
- }
- }
- throw new IllegalStateException("Unknown volume at " + path);
+ // Ideally we'd find the relevant StorageVolume, but we don't have a
+ // Context to obtain it from, so the best we can do is assume
+ if (path.getAbsolutePath()
+ .startsWith(Environment.getStorageDirectory().getAbsolutePath())) {
+ return MediaStore.VOLUME_EXTERNAL;
} else {
- return VOLUME_INTERNAL;
+ return MediaStore.VOLUME_INTERNAL;
}
}
@@ -1985,7 +1755,7 @@ public final class MediaStore {
/**
* Currently outstanding thumbnail requests that can be cancelled.
*/
- @GuardedBy("sPending")
+ // @GuardedBy("sPending")
private static ArrayMap<Uri, CancellationSignal> sPending = new ArrayMap<>();
/**
@@ -1997,16 +1767,7 @@ public final class MediaStore {
@Deprecated
static @Nullable Bitmap getThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri,
int kind, @Nullable BitmapFactory.Options opts) {
- final Point size;
- if (kind == ThumbnailConstants.MICRO_KIND) {
- size = ThumbnailConstants.MICRO_SIZE;
- } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) {
- size = ThumbnailConstants.FULL_SCREEN_SIZE;
- } else if (kind == ThumbnailConstants.MINI_KIND) {
- size = ThumbnailConstants.MINI_SIZE;
- } else {
- throw new IllegalArgumentException("Unsupported kind: " + kind);
- }
+ final Size size = ThumbnailConstants.getKindSize(kind);
CancellationSignal signal = null;
synchronized (sPending) {
@@ -2018,7 +1779,7 @@ public final class MediaStore {
}
try {
- return cr.loadThumbnail(uri, Point.convert(size), signal);
+ return cr.loadThumbnail(uri, size, signal);
} catch (IOException e) {
Log.w(TAG, "Failed to obtain thumbnail for " + uri, e);
return null;
@@ -2215,26 +1976,14 @@ public final class MediaStore {
@Deprecated
public static final String insertImage(ContentResolver cr, String imagePath,
String name, String description) throws FileNotFoundException {
- final File file = new File(imagePath);
- final String mimeType = MediaFile.getMimeTypeForFile(imagePath);
-
- if (TextUtils.isEmpty(name)) name = "Image";
- final PendingParams params = new PendingParams(
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI, name, mimeType);
-
- final Context context = AppGlobals.getInitialApplication();
- final Uri pendingUri = createPending(context, params);
- try (PendingSession session = openPending(context, pendingUri)) {
- try (InputStream in = new FileInputStream(file);
- OutputStream out = session.openOutputStream()) {
- FileUtils.copy(in, out);
- }
- return session.publish().toString();
- } catch (Exception e) {
- Log.w(TAG, "Failed to insert image", e);
- context.getContentResolver().delete(pendingUri, null, null);
- return null;
+ final Bitmap source;
+ try {
+ source = ImageDecoder
+ .decodeBitmap(ImageDecoder.createSource(new File(imagePath)));
+ } catch (IOException e) {
+ throw new FileNotFoundException(e.getMessage());
}
+ return insertImage(cr, source, name, description);
}
/**
@@ -2251,22 +2000,34 @@ public final class MediaStore {
* control over lifecycle.
*/
@Deprecated
- public static final String insertImage(ContentResolver cr, Bitmap source,
- String title, String description) {
+ public static final String insertImage(ContentResolver cr, Bitmap source, String title,
+ String description) {
if (TextUtils.isEmpty(title)) title = "Image";
- final PendingParams params = new PendingParams(
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI, title, "image/jpeg");
- final Context context = AppGlobals.getInitialApplication();
- final Uri pendingUri = createPending(context, params);
- try (PendingSession session = openPending(context, pendingUri)) {
- try (OutputStream out = session.openOutputStream()) {
+ final long now = System.currentTimeMillis();
+ final ContentValues values = new ContentValues();
+ values.put(MediaColumns.DISPLAY_NAME, title);
+ values.put(MediaColumns.MIME_TYPE, "image/jpeg");
+ values.put(MediaColumns.DATE_ADDED, now / 1000);
+ values.put(MediaColumns.DATE_MODIFIED, now / 1000);
+ values.put(MediaColumns.DATE_EXPIRES, (now + DateUtils.DAY_IN_MILLIS) / 1000);
+ values.put(MediaColumns.IS_PENDING, 1);
+
+ final Uri uri = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+ try {
+ try (OutputStream out = cr.openOutputStream(uri)) {
source.compress(Bitmap.CompressFormat.JPEG, 90, out);
}
- return session.publish().toString();
+
+ // Everything went well above, publish it!
+ values.clear();
+ values.put(MediaColumns.IS_PENDING, 0);
+ values.putNull(MediaColumns.DATE_EXPIRES);
+ cr.update(uri, values, null, null);
+ return uri.toString();
} catch (Exception e) {
Log.w(TAG, "Failed to insert image", e);
- context.getContentResolver().delete(pendingUri, null, null);
+ cr.delete(uri, null, null);
return null;
}
}
@@ -2526,6 +2287,14 @@ public final class MediaStore {
public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
/**
+ * Return the typical {@link Size} (in pixels) used internally when
+ * the given thumbnail kind is requested.
+ */
+ public static @NonNull Size getKindSize(int kind) {
+ return ThumbnailConstants.getKindSize(kind);
+ }
+
+ /**
* The blob raw data of thumbnail
*
* @deprecated this column never existed internally, and could never
@@ -3780,6 +3549,14 @@ public final class MediaStore {
public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
/**
+ * Return the typical {@link Size} (in pixels) used internally when
+ * the given thumbnail kind is requested.
+ */
+ public static @NonNull Size getKindSize(int kind) {
+ return ThumbnailConstants.getKindSize(kind);
+ }
+
+ /**
* The width of the thumbnal
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
@@ -3811,54 +3588,44 @@ public final class MediaStore {
*/
public static @NonNull Set<String> getExternalVolumeNames(@NonNull Context context) {
final StorageManager sm = context.getSystemService(StorageManager.class);
- final Set<String> volumeNames = new ArraySet<>();
- for (VolumeInfo vi : sm.getVolumes()) {
- if (vi.isVisibleForUser(UserHandle.myUserId()) && vi.isMountedReadable()) {
- if (vi.isPrimary()) {
- volumeNames.add(VOLUME_EXTERNAL_PRIMARY);
- } else {
- volumeNames.add(vi.getNormalizedFsUuid());
+ final Set<String> res = new ArraySet<>();
+ for (StorageVolume sv : sm.getStorageVolumes()) {
+ switch (sv.getState()) {
+ case Environment.MEDIA_MOUNTED:
+ case Environment.MEDIA_MOUNTED_READ_ONLY: {
+ final String volumeName = sv.getMediaStoreVolumeName();
+ if (volumeName != null) {
+ res.add(volumeName);
+ }
+ break;
}
}
}
- return volumeNames;
+ return res;
}
/**
- * Return list of all specific volume names that have recently been part of
+ * Return list of all recent volume names that have been part of
* {@link #VOLUME_EXTERNAL}.
* <p>
- * This includes both currently mounted volumes <em>and</em> recently
- * mounted (but currently unmounted) volumes. Any indexed metadata for these
- * volumes is preserved to optimize the speed of remounting at a later time.
- *
- * @hide
+ * These volume names are not currently mounted, but they're likely to
+ * reappear in the future, so apps are encouraged to preserve any indexed
+ * metadata related to these volumes to optimize user experiences.
+ * <p>
+ * Each specific volume name can be passed to APIs like
+ * {@link MediaStore.Images.Media#getContentUri(String)} to interact with
+ * media on that storage device.
*/
- @SystemApi
- @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE)
public static @NonNull Set<String> getRecentExternalVolumeNames(@NonNull Context context) {
final StorageManager sm = context.getSystemService(StorageManager.class);
-
- // We always have primary storage
- final Set<String> volumeNames = new ArraySet<>();
- volumeNames.add(VOLUME_EXTERNAL_PRIMARY);
-
- final long lastWeek = System.currentTimeMillis() - DateUtils.WEEK_IN_MILLIS;
- for (VolumeRecord rec : sm.getVolumeRecords()) {
- // Skip volumes without valid UUIDs
- if (TextUtils.isEmpty(rec.fsUuid)) continue;
-
- final VolumeInfo vi = sm.findVolumeByUuid(rec.fsUuid);
- if (vi != null && vi.isVisibleForUser(UserHandle.myUserId())
- && vi.isMountedReadable()) {
- // We're mounted right now
- volumeNames.add(rec.getNormalizedFsUuid());
- } else if (rec.lastSeenMillis > 0 && rec.lastSeenMillis < lastWeek) {
- // We're not mounted right now, but we've been seen recently
- volumeNames.add(rec.getNormalizedFsUuid());
+ final Set<String> res = new ArraySet<>();
+ for (StorageVolume sv : sm.getRecentStorageVolumes()) {
+ final String volumeName = sv.getMediaStoreVolumeName();
+ if (volumeName != null) {
+ res.add(volumeName);
}
}
- return volumeNames;
+ return res;
}
/**
@@ -3911,97 +3678,6 @@ public final class MediaStore {
}
/**
- * Return path where the given specific volume is mounted. Not valid for
- * {@link #VOLUME_INTERNAL} or {@link #VOLUME_EXTERNAL}, since those are
- * broad collections that cover many paths.
- *
- * @hide
- */
- @TestApi
- public static @NonNull File getVolumePath(@NonNull String volumeName)
- throws FileNotFoundException {
- final StorageManager sm = AppGlobals.getInitialApplication()
- .getSystemService(StorageManager.class);
- return getVolumePath(sm.getVolumes(), volumeName);
- }
-
- /** {@hide} */
- public static @NonNull File getVolumePath(@NonNull List<VolumeInfo> volumes,
- @NonNull String volumeName) throws FileNotFoundException {
- if (TextUtils.isEmpty(volumeName)) {
- throw new IllegalArgumentException();
- }
-
- switch (volumeName) {
- case VOLUME_INTERNAL:
- case VOLUME_EXTERNAL:
- throw new FileNotFoundException(volumeName + " has no associated path");
- }
-
- final boolean wantPrimary = VOLUME_EXTERNAL_PRIMARY.equals(volumeName);
- for (VolumeInfo volume : volumes) {
- final boolean matchPrimary = wantPrimary
- && volume.isPrimary();
- final boolean matchSecondary = !wantPrimary
- && Objects.equals(volume.getNormalizedFsUuid(), volumeName);
- if (matchPrimary || matchSecondary) {
- final File path = volume.getPathForUser(UserHandle.myUserId());
- if (path != null) {
- return path;
- }
- }
- }
- throw new FileNotFoundException("Failed to find path for " + volumeName);
- }
-
- /**
- * Return paths that should be scanned for the given volume.
- *
- * @hide
- */
- @TestApi
- @SystemApi
- @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE)
- public static @NonNull Collection<File> getVolumeScanPaths(@NonNull String volumeName)
- throws FileNotFoundException {
- if (TextUtils.isEmpty(volumeName)) {
- throw new IllegalArgumentException();
- }
-
- final Context context = AppGlobals.getInitialApplication();
- final UserManager um = context.getSystemService(UserManager.class);
-
- final ArrayList<File> res = new ArrayList<>();
- if (VOLUME_INTERNAL.equals(volumeName)) {
- addCanonicalFile(res, new File(Environment.getRootDirectory(), "media"));
- addCanonicalFile(res, new File(Environment.getOemDirectory(), "media"));
- addCanonicalFile(res, new File(Environment.getProductDirectory(), "media"));
- } else if (VOLUME_EXTERNAL.equals(volumeName)) {
- for (String exactVolume : getExternalVolumeNames(context)) {
- addCanonicalFile(res, getVolumePath(exactVolume));
- }
- if (um.isDemoUser()) {
- addCanonicalFile(res, Environment.getDataPreloadsMediaDirectory());
- }
- } else {
- addCanonicalFile(res, getVolumePath(volumeName));
- if (VOLUME_EXTERNAL_PRIMARY.equals(volumeName) && um.isDemoUser()) {
- addCanonicalFile(res, Environment.getDataPreloadsMediaDirectory());
- }
- }
- return res;
- }
-
- private static void addCanonicalFile(List<File> list, File file) {
- try {
- list.add(file.getCanonicalFile());
- } catch (IOException e) {
- Log.w(TAG, "Failed to resolve " + file + ": " + e);
- list.add(file);
- }
- }
-
- /**
* Uri for querying the state of the media scanner.
*/
public static Uri getMediaScannerUri() {
@@ -4084,10 +3760,10 @@ public final class MediaStore {
try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
final Bundle in = new Bundle();
- in.putParcelable(DocumentsContract.EXTRA_URI, mediaUri);
- in.putParcelableList(DocumentsContract.EXTRA_URI_PERMISSIONS, uriPermissions);
+ in.putParcelable(EXTRA_URI, mediaUri);
+ in.putParcelableArrayList(EXTRA_URI_PERMISSIONS, new ArrayList<>(uriPermissions));
final Bundle out = client.call(GET_DOCUMENT_URI_CALL, null, in);
- return out.getParcelable(DocumentsContract.EXTRA_URI);
+ return out.getParcelable(EXTRA_URI);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -4114,134 +3790,43 @@ public final class MediaStore {
try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
final Bundle in = new Bundle();
- in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
- in.putParcelableList(DocumentsContract.EXTRA_URI_PERMISSIONS, uriPermissions);
+ in.putParcelable(EXTRA_URI, documentUri);
+ in.putParcelableArrayList(EXTRA_URI_PERMISSIONS, new ArrayList<>(uriPermissions));
final Bundle out = client.call(GET_MEDIA_URI_CALL, null, in);
- return out.getParcelable(DocumentsContract.EXTRA_URI);
+ return out.getParcelable(EXTRA_URI);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
+ /** @hide */
+ @TestApi
+ public static void waitForIdle(@NonNull ContentResolver resolver) {
+ resolver.call(AUTHORITY, WAIT_FOR_IDLE_CALL, null, null);
+ }
+
/**
- * Calculate size of media contributed by given package under the calling
- * user. The meaning of "contributed" means it won't automatically be
- * deleted when the app is uninstalled.
+ * Perform a blocking scan of the given {@link File}, returning the
+ * {@link Uri} of the scanned file.
*
* @hide
*/
+ @SystemApi
@TestApi
- @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA)
- public static @BytesLong long getContributedMediaSize(Context context, String packageName,
- UserHandle user) throws IOException {
- final UserManager um = context.getSystemService(UserManager.class);
- if (um.isUserUnlocked(user) && um.isUserRunning(user)) {
- try {
- final ContentResolver resolver = context
- .createPackageContextAsUser(packageName, 0, user).getContentResolver();
- final Bundle in = new Bundle();
- in.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
- final Bundle out = resolver.call(AUTHORITY, GET_CONTRIBUTED_MEDIA_CALL, null, in);
- return out.getLong(Intent.EXTRA_INDEX);
- } catch (Exception e) {
- throw new IOException(e);
- }
- } else {
- throw new IOException("User " + user + " must be unlocked and running");
- }
+ @SuppressLint("StreamFiles")
+ public static @NonNull Uri scanFile(@NonNull ContentResolver resolver, @NonNull File file) {
+ final Bundle out = resolver.call(AUTHORITY, SCAN_FILE_CALL, file.getAbsolutePath(), null);
+ return out.getParcelable(Intent.EXTRA_STREAM);
}
/**
- * Delete all media contributed by given package under the calling user. The
- * meaning of "contributed" means it won't automatically be deleted when the
- * app is uninstalled.
+ * Perform a blocking scan of the given storage volume.
*
* @hide
*/
+ @SystemApi
@TestApi
- @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA)
- public static void deleteContributedMedia(Context context, String packageName,
- UserHandle user) throws IOException {
- final UserManager um = context.getSystemService(UserManager.class);
- if (um.isUserUnlocked(user) && um.isUserRunning(user)) {
- try {
- final ContentResolver resolver = context
- .createPackageContextAsUser(packageName, 0, user).getContentResolver();
- final Bundle in = new Bundle();
- in.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
- resolver.call(AUTHORITY, DELETE_CONTRIBUTED_MEDIA_CALL, null, in);
- } catch (Exception e) {
- throw new IOException(e);
- }
- } else {
- throw new IOException("User " + user + " must be unlocked and running");
- }
- }
-
- /** @hide */
- @TestApi
- public static void waitForIdle(Context context) {
- final ContentResolver resolver = context.getContentResolver();
- try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
- client.call(WAIT_FOR_IDLE_CALL, null, null);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- /** @hide */
- public static void suicide(Context context) {
- final ContentResolver resolver = context.getContentResolver();
- try (ContentProviderClient client = resolver
- .acquireUnstableContentProviderClient(AUTHORITY)) {
- client.call(SUICIDE_CALL, null, null);
- } catch (Exception ignored) {
- }
- }
-
- /** @hide */
- @TestApi
- public static Uri scanFile(Context context, File file) {
- return scan(context, SCAN_FILE_CALL, file, false);
- }
-
- /** @hide */
- @TestApi
- public static Uri scanFileFromShell(Context context, File file) {
- return scan(context, SCAN_FILE_CALL, file, true);
- }
-
- /** @hide */
- @TestApi
- public static void scanVolume(Context context, File file) {
- scan(context, SCAN_VOLUME_CALL, file, false);
- }
-
- /** @hide */
- public static Uri scanFile(ContentProviderClient client, File file) {
- return scan(client, SCAN_FILE_CALL, file, false);
- }
-
- /** @hide */
- private static Uri scan(Context context, String method, File file,
- boolean originatedFromShell) {
- final ContentResolver resolver = context.getContentResolver();
- try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
- return scan(client, method, file, originatedFromShell);
- }
- }
-
- /** @hide */
- private static Uri scan(ContentProviderClient client, String method, File file,
- boolean originatedFromShell) {
- try {
- final Bundle in = new Bundle();
- in.putParcelable(Intent.EXTRA_STREAM, Uri.fromFile(file));
- in.putBoolean(EXTRA_ORIGINATED_FROM_SHELL, originatedFromShell);
- final Bundle out = client.call(method, null, in);
- return out.getParcelable(Intent.EXTRA_STREAM);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
+ public static void scanVolume(@NonNull ContentResolver resolver, @NonNull String volumeName) {
+ resolver.call(AUTHORITY, SCAN_VOLUME_CALL, volumeName, null);
}
}
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index f5708a5c89af..dec9ae701fb2 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -259,7 +259,7 @@ public abstract class FileSystemProvider extends DocumentsProvider {
throw new IllegalStateException("Failed to touch " + file + ": " + e);
}
}
- MediaStore.scanFile(getContext(), file);
+ MediaStore.scanFile(getContext().getContentResolver(), file);
return childId;
}
@@ -316,10 +316,10 @@ public abstract class FileSystemProvider extends DocumentsProvider {
private void moveInMediaStore(@Nullable File oldVisibleFile, @Nullable File newVisibleFile) {
if (oldVisibleFile != null) {
- MediaStore.scanFile(getContext(), oldVisibleFile);
+ MediaStore.scanFile(getContext().getContentResolver(), oldVisibleFile);
}
if (newVisibleFile != null) {
- MediaStore.scanFile(getContext(), newVisibleFile);
+ MediaStore.scanFile(getContext().getContentResolver(), newVisibleFile);
}
}
diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java
index 515f6a8ce8a2..40e90731f2a2 100644
--- a/media/java/android/media/MediaScannerConnection.java
+++ b/media/java/android/media/MediaScannerConnection.java
@@ -19,6 +19,7 @@ package android.media;
import android.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.ContentProviderClient;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.ServiceConnection;
import android.net.Uri;
@@ -197,7 +198,7 @@ public class MediaScannerConnection implements ServiceConnection {
private static Uri scanFileQuietly(ContentProviderClient client, File file) {
Uri uri = null;
try {
- uri = MediaStore.scanFile(client, file.getCanonicalFile());
+ uri = MediaStore.scanFile(ContentResolver.wrap(client), file.getCanonicalFile());
Log.d(TAG, "Scanned " + file + " to " + uri);
} catch (Exception e) {
Log.w(TAG, "Failed to scan " + file + ": " + e);
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 9064e6891be6..a1e159174f95 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -908,7 +908,7 @@ public class RingtoneManager {
}
// Tell MediaScanner about the new file. Wait for it to assign a {@link Uri}.
- return MediaStore.scanFile(mContext, outFile);
+ return MediaStore.scanFile(mContext.getContentResolver(), outFile);
}
private static final String getExternalDirectoryForType(final int type) {
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index a315c1eefb52..6705b0cb79f0 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -33,14 +33,13 @@ import android.graphics.ImageDecoder;
import android.graphics.ImageDecoder.ImageInfo;
import android.graphics.ImageDecoder.Source;
import android.graphics.Matrix;
-import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.CancellationSignal;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.provider.MediaStore.ThumbnailConstants;
+import android.provider.MediaStore;
import android.util.Log;
import android.util.Size;
@@ -77,15 +76,7 @@ public class ThumbnailUtils {
public static final int OPTIONS_RECYCLE_INPUT = 0x2;
private static Size convertKind(int kind) {
- if (kind == ThumbnailConstants.MICRO_KIND) {
- return Point.convert(ThumbnailConstants.MICRO_SIZE);
- } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) {
- return Point.convert(ThumbnailConstants.FULL_SCREEN_SIZE);
- } else if (kind == ThumbnailConstants.MINI_KIND) {
- return Point.convert(ThumbnailConstants.MINI_SIZE);
- } else {
- throw new IllegalArgumentException("Unsupported kind: " + kind);
- }
+ return MediaStore.Images.Thumbnails.getKindSize(kind);
}
private static class Resizer implements ImageDecoder.OnHeaderDecodedListener {
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 0f402eb0bf42..f3c071a06eba 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -16,8 +16,10 @@
package android.mtp;
+import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.ContentProviderClient;
+import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
@@ -422,13 +424,13 @@ public class MtpDatabase implements AutoCloseable {
}
// Add the new file to MediaProvider
if (succeeded) {
- MediaStore.scanFile(mContext, obj.getPath().toFile());
+ MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile());
}
}
@VisibleForNative
private void rescanFile(String path, int handle, int format) {
- MediaStore.scanFile(mContext, new File(path));
+ MediaStore.scanFile(mContext.getContentResolver(), new File(path));
}
@VisibleForNative
@@ -587,13 +589,13 @@ public class MtpDatabase implements AutoCloseable {
if (obj.isDir()) {
// for directories, check if renamed from something hidden to something non-hidden
if (oldPath.getFileName().startsWith(".") && !newPath.startsWith(".")) {
- MediaStore.scanFile(mContext, newPath.toFile());
+ MediaStore.scanFile(mContext.getContentResolver(), newPath.toFile());
}
} else {
// for files, check if renamed from .nomedia to something else
if (oldPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)
&& !newPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)) {
- MediaStore.scanFile(mContext, newPath.getParent().toFile());
+ MediaStore.scanFile(mContext.getContentResolver(), newPath.getParent().toFile());
}
}
return MtpConstants.RESPONSE_OK;
@@ -662,7 +664,7 @@ public class MtpDatabase implements AutoCloseable {
mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
} else {
// Old parent doesn't exist - add the object
- MediaStore.scanFile(mContext, path.toFile());
+ MediaStore.scanFile(mContext.getContentResolver(), path.toFile());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in mMediaProvider.update", e);
@@ -689,7 +691,7 @@ public class MtpDatabase implements AutoCloseable {
if (!success) {
return;
}
- MediaStore.scanFile(mContext, obj.getPath().toFile());
+ MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile());
}
@VisibleForNative
@@ -909,7 +911,7 @@ public class MtpDatabase implements AutoCloseable {
String[] whereArgs = new String[]{path.toString()};
if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) > 0) {
if (!isDir && path.toString().toLowerCase(Locale.US).endsWith(NO_MEDIA)) {
- MediaStore.scanFile(mContext, path.getParent().toFile());
+ MediaStore.scanFile(mContext.getContentResolver(), path.getParent().toFile());
}
} else {
Log.i(TAG, "Mediaprovider didn't delete " + path);
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
index 65d0fef74b25..c7dbca61f90a 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -41,11 +41,7 @@ public class MtpStorage {
mDescription = volume.getDescription(null);
mRemovable = volume.isRemovable();
mMaxFileSize = volume.getMaxFileSize();
- if (volume.isPrimary()) {
- mVolumeName = MediaStore.VOLUME_EXTERNAL_PRIMARY;
- } else {
- mVolumeName = volume.getNormalizedUuid();
- }
+ mVolumeName = volume.getMediaStoreVolumeName();
}
/**
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneOverlayService.java b/packages/SoundPicker/src/com/android/soundpicker/RingtoneOverlayService.java
index 2d37b4c9c342..15fd1f7f4957 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtoneOverlayService.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtoneOverlayService.java
@@ -101,7 +101,7 @@ public class RingtoneOverlayService extends Service {
}
private Uri scanFile(@NonNull final File file) {
- return MediaStore.scanFile(this, file);
+ return MediaStore.scanFile(getContentResolver(), file);
}
private void set(@NonNull final String name, @NonNull final Uri uri) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 0383dee4f9c3..d3ccbeb0afb1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -371,7 +371,8 @@ public class RecordingService extends Service {
Bitmap thumbnailBitmap = null;
try {
ContentResolver resolver = getContentResolver();
- Size size = Point.convert(MediaStore.ThumbnailConstants.MINI_SIZE);
+ DisplayMetrics metrics = getResources().getDisplayMetrics();
+ Size size = new Size(metrics.widthPixels, metrics.heightPixels / 2);
thumbnailBitmap = resolver.loadThumbnail(uri, size, null);
} catch (IOException e) {
Log.e(TAG, "Error creating thumbnail: " + e.getMessage());
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 76925b43cfb8..2f401e5271d8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -23,6 +23,8 @@ import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
@@ -48,7 +50,9 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.MediaStore;
+import android.provider.MediaStore.MediaColumns;
import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -275,6 +279,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
Context context = mParams.context;
+ ContentResolver resolver = context.getContentResolver();
Bitmap image = mParams.image;
Resources r = context.getResources();
@@ -284,23 +289,27 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
mSmartActionsProvider, mSmartActionsEnabled, isManagedProfile(context));
// Save the screenshot to the MediaStore
- final MediaStore.PendingParams params = new MediaStore.PendingParams(
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mImageFileName, "image/png");
- params.setRelativePath(Environment.DIRECTORY_PICTURES + File.separator
- + Environment.DIRECTORY_SCREENSHOTS);
-
- final Uri uri = MediaStore.createPending(context, params);
- final MediaStore.PendingSession session = MediaStore.openPending(context, uri);
+ final ContentValues values = new ContentValues();
+ values.put(MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
+ + File.separator + Environment.DIRECTORY_SCREENSHOTS);
+ values.put(MediaColumns.DISPLAY_NAME, mImageFileName);
+ values.put(MediaColumns.MIME_TYPE, "image/png");
+ values.put(MediaColumns.DATE_ADDED, mImageTime / 1000);
+ values.put(MediaColumns.DATE_MODIFIED, mImageTime / 1000);
+ values.put(MediaColumns.DATE_EXPIRES, (mImageTime + DateUtils.DAY_IN_MILLIS) / 1000);
+ values.put(MediaColumns.IS_PENDING, 1);
+
+ final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
try {
// First, write the actual data for our screenshot
- try (OutputStream out = session.openOutputStream()) {
+ try (OutputStream out = resolver.openOutputStream(uri)) {
if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) {
throw new IOException("Failed to compress");
}
}
// Next, write metadata to help index the screenshot
- try (ParcelFileDescriptor pfd = session.open()) {
+ try (ParcelFileDescriptor pfd = resolver.openFile(uri, "rw", null)) {
final ExifInterface exif = new ExifInterface(pfd.getFileDescriptor());
exif.setAttribute(ExifInterface.TAG_SOFTWARE,
@@ -327,12 +336,15 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
exif.saveAttributes();
}
- session.publish();
+
+ // Everything went well above, publish it!
+ values.clear();
+ values.put(MediaColumns.IS_PENDING, 0);
+ values.putNull(MediaColumns.DATE_EXPIRES);
+ resolver.update(uri, values, null, null);
} catch (Exception e) {
- session.abandon();
+ resolver.delete(uri, null);
throw e;
- } finally {
- IoUtils.closeQuietly(session);
}
populateNotificationActions(context, r, uri, smartActionsFuture, mNotificationBuilder);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 9b1d9e9a246c..5a78036911a8 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -118,6 +118,7 @@ import android.sysprop.VoldProperties;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.DataUnit;
import android.util.FeatureFlagUtils;
@@ -3161,16 +3162,20 @@ class StorageManagerService extends IStorageManager.Stub
final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0;
final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0;
final boolean includeInvisible = (flags & StorageManager.FLAG_INCLUDE_INVISIBLE) != 0;
+ final boolean includeRecent = (flags & StorageManager.FLAG_INCLUDE_RECENT) != 0;
// Report all volumes as unmounted until we've recorded that user 0 has unlocked. There
// are no guarantees that callers will see a consistent view of the volume before that
// point
final boolean systemUserUnlocked = isSystemUnlocked(UserHandle.USER_SYSTEM);
+ final boolean userIsDemo;
final boolean userKeyUnlocked;
final boolean storagePermission;
final long token = Binder.clearCallingIdentity();
try {
+ userIsDemo = LocalServices.getService(UserManagerInternal.class)
+ .getUserInfo(userId).isDemo();
userKeyUnlocked = isUserKeyUnlocked(userId);
storagePermission = mStorageManagerInternal.hasExternalStorage(uid, packageName);
} finally {
@@ -3180,6 +3185,7 @@ class StorageManagerService extends IStorageManager.Stub
boolean foundPrimary = false;
final ArrayList<StorageVolume> res = new ArrayList<>();
+ final ArraySet<String> resUuids = new ArraySet<>();
synchronized (mLock) {
for (int i = 0; i < mVolumes.size(); i++) {
final VolumeInfo vol = mVolumes.valueAt(i);
@@ -3222,7 +3228,43 @@ class StorageManagerService extends IStorageManager.Stub
} else {
res.add(userVol);
}
+ resUuids.add(userVol.getUuid());
}
+
+ if (includeRecent) {
+ final long lastWeek = System.currentTimeMillis() - DateUtils.WEEK_IN_MILLIS;
+ for (int i = 0; i < mRecords.size(); i++) {
+ final VolumeRecord rec = mRecords.valueAt(i);
+
+ // Skip if we've already included it above
+ if (resUuids.contains(rec.fsUuid)) continue;
+
+ // Treat as recent if mounted within the last week
+ if (rec.lastSeenMillis > 0 && rec.lastSeenMillis < lastWeek) {
+ final StorageVolume userVol = rec.buildStorageVolume(mContext);
+ res.add(userVol);
+ resUuids.add(userVol.getUuid());
+ }
+ }
+ }
+ }
+
+ // Synthesize a volume for preloaded media under demo users, so that
+ // it's scanned into MediaStore
+ if (userIsDemo) {
+ final String id = "demo";
+ final File path = Environment.getDataPreloadsMediaDirectory();
+ final boolean primary = false;
+ final boolean removable = false;
+ final boolean emulated = true;
+ final boolean allowMassStorage = false;
+ final long maxFileSize = 0;
+ final UserHandle user = new UserHandle(userId);
+ final String envState = Environment.MEDIA_MOUNTED_READ_ONLY;
+ final String description = mContext.getString(android.R.string.unknownName);
+
+ res.add(new StorageVolume(id, path, path, description, primary, removable,
+ emulated, allowMassStorage, maxFileSize, user, id, envState));
}
if (!foundPrimary) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1153fb521c53..99d5e4a21df4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17962,14 +17962,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
mPermissionManager.resetRuntimePermissions(pkg, nextUserId);
- // Also delete contributed media, when requested
- if ((flags & PackageManager.DELETE_CONTRIBUTED_MEDIA) != 0) {
- try {
- MediaStore.deleteContributedMedia(mContext, ps.name, UserHandle.of(nextUserId));
- } catch (IOException e) {
- Slog.w(TAG, "Failed to delete contributed media for " + ps.name, e);
- }
- }
}
if (outInfo != null) {