Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 25f1c6eb authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Update behavior of some DownloadManager APIs.

-- Update behavior of DownloadManager.setDestinationUri(),
   DownloadManager.setDestinationInExternalPublicDir() and
   DownloadManager.addCompletedDownload() based on the latest
   storage re-design. Essentially, going forward these APIs
   will only allow downloading files into package owned dirs
   or the top-level Download dir.
-- Allow some system components to specify
   MediaColumns.OWNER_PACKAGE_NAME when inserting items into
   MediaProvider.
-- Don't copy DownloadManager.COLUMN_TITLE to MediaProvider.
   DownloadProvider and MediaProvider have different constraints
   around "title" and there isn't really a need to keep these
   in sync.
-- Sanity check file download paths hinted by apps.
-- Remove sandbox related logic in DownloadProvider.

Bug: 120879208
Bug: 128630262
Bug: 130797842

Test: manual
Test: atest DownloadProviderTests
Test: atest cts/tests/app/src/android/app/cts/DownloadManagerTest.java
Test: atest cts/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java
Test: atest cts/tests/app/DownloadManagerApi28Test/src/android/app/cts/DownloadManagerApi28Test.java
Test: atest cts/tests/tests/provider/src/android/provider/cts/MediaStore*

Change-Id: If48bc9ecf9ed94412c6c62ce4e5e6a55fff9b789
parent 47159d96
Loading
Loading
Loading
Loading
+84 −9
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -33,9 +34,11 @@ import android.net.ConnectivityManager;
import android.net.NetworkPolicyManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.provider.Downloads;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
@@ -471,6 +474,15 @@ public class DownloadManager {
         * By default, downloads are saved to a generated filename in the shared download cache and
         * may be deleted by the system at any time to reclaim space.
         *
         * <p> For applications targeting {@link android.os.Build.VERSION_CODES#Q} or above,
         * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE WRITE EXTERNAL_STORAGE}
         * permission is not needed and the {@code uri} must refer to a path within the
         * directories owned by the application (e.g. {@link Context#getExternalFilesDir(String)})
         * or a path within the top-level Downloads directory (as returned by
         * {@link Environment#getExternalStoragePublicDirectory(String)} with
         * {@link Environment#DIRECTORY_DOWNLOADS}).
         *
         * @param uri a file {@link Uri} indicating the destination for the downloaded file.
         * @return this object
         */
        public Request setDestinationUri(Uri uri) {
@@ -524,6 +536,11 @@ public class DownloadManager {
         * The downloaded file is not scanned by MediaScanner. But it can be
         * made scannable by calling {@link #allowScanningByMediaScanner()}.
         *
         * <p> For applications targeting {@link android.os.Build.VERSION_CODES#Q} or above,
         * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE}
         * permission is not needed and the {@code dirType} must
         * be {@link Environment#DIRECTORY_DOWNLOADS}.
         *
         * @param dirType the directory type to pass to {@link Environment#getExternalStoragePublicDirectory(String)}
         * @param subPath the path within the external directory, including the
         *            destination filename
@@ -535,15 +552,29 @@ public class DownloadManager {
            File file = Environment.getExternalStoragePublicDirectory(dirType);
            if (file == null) {
                throw new IllegalStateException("Failed to get external storage public directory");
            } else if (file.exists()) {
                if (!file.isDirectory()) {
                    throw new IllegalStateException(file.getAbsolutePath() +
                            " already exists and is not a directory");
            }

            final Context context = AppGlobals.getInitialApplication();
            if (context.getApplicationInfo().targetSdkVersion
                    >= Build.VERSION_CODES.Q || Environment.isExternalStorageSandboxed()) {
                try (ContentProviderClient client = context.getContentResolver()
                        .acquireContentProviderClient(Downloads.Impl.AUTHORITY)) {
                    final Bundle extras = new Bundle();
                    extras.putString(Downloads.DIR_TYPE, dirType);
                    client.call(Downloads.CALL_CREATE_EXTERNAL_PUBLIC_DIR, null, extras);
                } catch (RemoteException e) {
                    throw new IllegalStateException("Unable to create directory: "
                            + file.getAbsolutePath());
                }
            } else {
                if (!file.mkdirs()) {
                    throw new IllegalStateException("Unable to create directory: "+
                            file.getAbsolutePath());
                if (file.exists()) {
                    if (!file.isDirectory()) {
                        throw new IllegalStateException(file.getAbsolutePath()
                                + " already exists and is not a directory");
                    }
                } else if (!file.mkdirs()) {
                    throw new IllegalStateException("Unable to create directory: "
                            + file.getAbsolutePath());
                }
            }
            setDestinationFromBase(file, subPath);
@@ -1316,6 +1347,16 @@ public class DownloadManager {
     * isMediaScannerScannable to true. It makes the file visible in media managing
     * applications such as Gallery App, which could be a useful purpose of using this API.
     *
     * <p> For applications targeting {@link android.os.Build.VERSION_CODES#Q} or above,
     * {@code path} must be within directories owned by the application
     * {e.g. {@link Context#getExternalFilesDir(String)}} or if the application is running under
     * the legacy storage model (see
     * {@link android.R.styleable#AndroidManifestApplication_requestLegacyExternalStorage
     * android:requestLegacyExternalStorage}), {@code path} can also be within the top-level
     * Downloads directory (as returned by
     * {@link Environment#getExternalStoragePublicDirectory(String)} with
     * {@link Environment#DIRECTORY_DOWNLOADS}).
     *
     * @param title the title that would appear for this file in Downloads App.
     * @param description the description that would appear for this file in Downloads App.
     * @param isMediaScannerScannable true if the file is to be scanned by MediaScanner. Files
@@ -1345,6 +1386,16 @@ public class DownloadManager {
     * isMediaScannerScannable to true. It makes the file visible in media managing
     * applications such as Gallery App, which could be a useful purpose of using this API.
     *
     * <p> For applications targeting {@link android.os.Build.VERSION_CODES#Q} or above,
     * {@code path} must be within directories owned by the application
     * {e.g. {@link Context#getExternalFilesDir(String)}} or if the application is running under
     * the legacy storage model (see
     * {@link android.R.styleable#AndroidManifestApplication_requestLegacyExternalStorage
     * android:requestLegacyExternalStorage}), {@code path} can also be within the top-level
     * Downloads directory (as returned by
     * {@link Environment#getExternalStoragePublicDirectory(String)} with
     * {@link Environment#DIRECTORY_DOWNLOADS}).
     *
     * @param title the title that would appear for this file in Downloads App.
     * @param description the description that would appear for this file in Downloads App.
     * @param isMediaScannerScannable true if the file is to be scanned by MediaScanner. Files
@@ -1368,7 +1419,19 @@ public class DownloadManager {
                length, showNotification, false, uri, referer);
    }

    /** {@hide} */
    /**
     * <p> For applications targeting {@link android.os.Build.VERSION_CODES#Q} or above,
     * {@code path} must be within directories owned by the application
     * {e.g. {@link Context#getExternalFilesDir(String)}} or if the application is running under
     * the legacy storage model (see
     * {@link android.R.styleable#AndroidManifestApplication_requestLegacyExternalStorage
     * android:requestLegacyExternalStorage}), {@code path} can also be within the top-level
     * Downloads directory (as returned by
     * {@link Environment#getExternalStoragePublicDirectory(String)} with
     * {@link Environment#DIRECTORY_DOWNLOADS}).
     *
     * {@hide}
     */
    public long addCompletedDownload(String title, String description,
            boolean isMediaScannerScannable, String mimeType, String path, long length,
            boolean showNotification, boolean allowWrite) {
@@ -1376,7 +1439,19 @@ public class DownloadManager {
                length, showNotification, allowWrite, null, null);
    }

    /** {@hide} */
    /**
     * <p> For applications targeting {@link android.os.Build.VERSION_CODES#Q} or above,
     * {@code path} must be within directories owned by the application
     * {e.g. {@link Context#getExternalFilesDir(String)}} or if the application is running under
     * the legacy storage model (see
     * {@link android.R.styleable#AndroidManifestApplication_requestLegacyExternalStorage
     * android:requestLegacyExternalStorage}), {@code path} can also be within the top-level
     * Downloads directory (as returned by
     * {@link Environment#getExternalStoragePublicDirectory(String)} with
     * {@link Environment#DIRECTORY_DOWNLOADS}).
     *
     * {@hide}
     */
    public long addCompletedDownload(String title, String description,
            boolean isMediaScannerScannable, String mimeType, String path, long length,
            boolean showNotification, boolean allowWrite, Uri uri, Uri referer) {
+6 −14
Original line number Diff line number Diff line
@@ -54,7 +54,6 @@ public class Environment {

    /** {@hide} */
    public static final String DIR_ANDROID = "Android";
    private static final String DIR_SANDBOX = "sandbox";
    private static final String DIR_DATA = "data";
    private static final String DIR_MEDIA = "media";
    private static final String DIR_OBB = "obb";
@@ -128,10 +127,6 @@ public class Environment {
            return buildPaths(getExternalDirs(), type);
        }

        public File[] buildExternalStorageAndroidSandboxDirs() {
            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_SANDBOX);
        }

        public File[] buildExternalStorageAndroidDataDirs() {
            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA);
        }
@@ -838,15 +833,6 @@ public class Environment {
        return sCurrentUser.buildExternalStoragePublicDirs(type)[0];
    }

    /**
     * Returns the path for android-specific data on the SD card.
     * @hide
     */
    public static File[] buildExternalStorageAndroidSandboxDirs() {
        throwIfUserRequired();
        return sCurrentUser.buildExternalStorageAndroidSandboxDirs();
    }

    /**
     * Returns the path for android-specific data on the SD card.
     * @hide
@@ -907,6 +893,12 @@ public class Environment {
        return sCurrentUser.buildExternalStorageAppCacheDirs(packageName);
    }

    /** @hide */
    public static File[] buildExternalStoragePublicDirs(@NonNull String dirType) {
        throwIfUserRequired();
        return sCurrentUser.buildExternalStoragePublicDirs(dirType);
    }

    /**
     * Return the download/cache content directory.
     */
+5 −1
Original line number Diff line number Diff line
@@ -846,12 +846,16 @@ public final class Downloads {
    }

    /** @hide */
    public static final String MEDIASTORE_DOWNLOADS_DELETED_CALL = "mediastore_downloads_deleted";
    public static final String CALL_MEDIASTORE_DOWNLOADS_DELETED = "mediastore_downloads_deleted";
    /** @hide */
    public static final String CALL_CREATE_EXTERNAL_PUBLIC_DIR = "create_external_public_dir";

    /** @hide */
    public static final String EXTRA_IDS = "ids";
    /** @hide */
    public static final String EXTRA_MIME_TYPES = "mime_types";
    /** @hide */
    public static final String DIR_TYPE = "dir_type";

    /**
     * Query where clause for general querying.