Test if allocation supported; @removed clean up.
SM.allocateBytes() doesn't offer a clear way to detect if a failed
request could ever succeed. (For example, we can never work with
pipes, or files on an unsupported storage device.) So give
developers a first-class API to test if allocation is supported.
If the underlying filesystem doesn't support fallocate(), fall back
to ftruncate() instead of failing completely.
Clean up @removed APIs that were refactoring during API 26 review
process.
Remove support for storing downloads on the /cache partition, which
doesn't exist on many devices.
Bug: 63057877
Test: bit DownloadProviderTests:*
Test: bit DocumentsUITests:com.android.documentsui.services.CopyJobTest
Test: bit DocumentsUITests:com.android.documentsui.services.MoveJobTest
Change-Id: I85d42a1a7240034b4f2a6f359011ac182bdce36e
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 5baaeb3..b444f17 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -18,9 +18,9 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.SdkConstant.SdkConstantType;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -36,8 +36,8 @@
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.provider.Downloads;
-import android.provider.Settings;
import android.provider.MediaStore.Images;
+import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.Pair;
@@ -393,7 +393,6 @@
private int mFlags = 0;
private boolean mIsVisibleInDownloadsUi = true;
private boolean mScannable = false;
- private boolean mUseSystemCache = false;
/** if a file is designated as a MediaScanner scannable file, the following value is
* stored in the database column {@link Downloads.Impl#COLUMN_MEDIA_SCANNED}.
*/
@@ -474,24 +473,6 @@
}
/**
- * Set the local destination for the downloaded file to the system cache dir (/cache).
- * This is only available to System apps with the permission
- * {@link android.Manifest.permission#ACCESS_CACHE_FILESYSTEM}.
- * <p>
- * The downloaded file is not scanned by MediaScanner.
- * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}.
- * <p>
- * Files downloaded to /cache may be deleted by the system at any time to reclaim space.
- *
- * @return this object
- * @hide
- */
- public Request setDestinationToSystemCache() {
- mUseSystemCache = true;
- return this;
- }
-
- /**
* Set the local destination for the downloaded file to a path within
* the application's external files directory (as returned by
* {@link Context#getExternalFilesDir(String)}.
@@ -772,13 +753,13 @@
values.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, packageName);
if (mDestinationUri != null) {
- values.put(Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_FILE_URI);
- values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT, mDestinationUri.toString());
+ values.put(Downloads.Impl.COLUMN_DESTINATION,
+ Downloads.Impl.DESTINATION_FILE_URI);
+ values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT,
+ mDestinationUri.toString());
} else {
values.put(Downloads.Impl.COLUMN_DESTINATION,
- (this.mUseSystemCache) ?
- Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION :
- Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE);
+ Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE);
}
// is the file supposed to be media-scannable?
values.put(Downloads.Impl.COLUMN_MEDIA_SCANNED, (mScannable) ? SCANNABLE_VALUE_YES :
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 71f5ff7..6372113 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -739,7 +739,7 @@
* {@link Environment#getDataDirectory()}, the returned value will be
* {@link #UUID_DEFAULT}.
*
- * @throws IOException when the storage device at the given path isn't
+ * @throws IOException when the storage device hosting the given path isn't
* present, or when it doesn't have a valid UUID.
*/
public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException {
@@ -771,6 +771,19 @@
throw new FileNotFoundException("Failed to find a storage device for " + volumeUuid);
}
+ /**
+ * Test if the given file descriptor supports allocation of disk space using
+ * {@link #allocateBytes(FileDescriptor, long)}.
+ */
+ public boolean isAllocationSupported(@NonNull FileDescriptor fd) {
+ try {
+ getUuidForPath(ParcelFileDescriptor.getFile(fd));
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
/** {@hide} */
public @NonNull List<VolumeInfo> getVolumes() {
try {
@@ -1562,12 +1575,6 @@
}
}
- /** @removed */
- @Deprecated
- public long getCacheQuotaBytes(@NonNull File path) throws IOException {
- return getCacheQuotaBytes(getUuidForPath(path));
- }
-
/**
* Return total size in bytes of all cached data belonging to the calling
* app on the given storage volume.
@@ -1603,36 +1610,6 @@
}
}
- /** @removed */
- @Deprecated
- public long getCacheSizeBytes(@NonNull File path) throws IOException {
- return getCacheSizeBytes(getUuidForPath(path));
- }
-
- /** @removed */
- @Deprecated
- public long getCacheQuotaBytes() throws IOException {
- return getCacheQuotaBytes(mContext.getCacheDir());
- }
-
- /** @removed */
- @Deprecated
- public long getCacheSizeBytes() throws IOException {
- return getCacheSizeBytes(mContext.getCacheDir());
- }
-
- /** @removed */
- @Deprecated
- public long getExternalCacheQuotaBytes() throws IOException {
- return getCacheQuotaBytes(mContext.getExternalCacheDir());
- }
-
- /** @removed */
- @Deprecated
- public long getExternalCacheSizeBytes() throws IOException {
- return getCacheSizeBytes(mContext.getExternalCacheDir());
- }
-
/**
* Flag indicating that a disk space allocation request should operate in an
* aggressive mode. This flag should only be rarely used in situations that
@@ -1741,15 +1718,6 @@
}
}
- /** @removed */
- @Deprecated
- @WorkerThread
- @SuppressLint("Doclava125")
- public long getAllocatableBytes(@NonNull File path,
- @RequiresPermission @AllocateFlags int flags) throws IOException {
- return getAllocatableBytes(getUuidForPath(path), flags);
- }
-
/**
* Allocate the requested number of bytes for your application to use on the
* given storage volume. This will cause the system to delete any cached
@@ -1798,15 +1766,6 @@
}
}
- /** @removed */
- @Deprecated
- @WorkerThread
- @SuppressLint("Doclava125")
- public void allocateBytes(@NonNull File path, @BytesLong long bytes,
- @RequiresPermission @AllocateFlags int flags) throws IOException {
- allocateBytes(getUuidForPath(path), bytes, flags);
- }
-
/**
* Allocate the requested number of bytes for your application to use in the
* given open file. This will cause the system to delete any cached files
@@ -1834,6 +1793,7 @@
* doesn't support allocating space, or if the device had
* trouble allocating the requested space.
* @see #getAllocatableBytes(UUID, int)
+ * @see #isAllocationSupported(FileDescriptor)
* @see Environment#isExternalStorageEmulated(File)
*/
@WorkerThread
@@ -1848,17 +1808,28 @@
public void allocateBytes(FileDescriptor fd, @BytesLong long bytes,
@RequiresPermission @AllocateFlags int flags) throws IOException {
final File file = ParcelFileDescriptor.getFile(fd);
+ final UUID uuid = getUuidForPath(file);
for (int i = 0; i < 3; i++) {
try {
final long haveBytes = Os.fstat(fd).st_blocks * 512;
final long needBytes = bytes - haveBytes;
if (needBytes > 0) {
- allocateBytes(file, needBytes, flags);
+ allocateBytes(uuid, needBytes, flags);
}
- Os.posix_fallocate(fd, 0, bytes);
- return;
+ try {
+ Os.posix_fallocate(fd, 0, bytes);
+ return;
+ } catch (ErrnoException e) {
+ if (e.errno == OsConstants.ENOSYS || e.errno == OsConstants.ENOTSUP) {
+ Log.w(TAG, "fallocate() not supported; falling back to ftruncate()");
+ Os.ftruncate(fd, bytes);
+ return;
+ } else {
+ throw e;
+ }
+ }
} catch (ErrnoException e) {
if (e.errno == OsConstants.ENOSPC) {
Log.w(TAG, "Odd, not enough space; let's try again?");
@@ -1941,18 +1912,6 @@
return isCacheBehavior(path, XATTR_CACHE_GROUP);
}
- /** @removed */
- @Deprecated
- public void setCacheBehaviorAtomic(File path, boolean atomic) throws IOException {
- setCacheBehaviorGroup(path, atomic);
- }
-
- /** @removed */
- @Deprecated
- public boolean isCacheBehaviorAtomic(File path) throws IOException {
- return isCacheBehaviorGroup(path);
- }
-
/**
* Enable or disable special cache behavior that leaves deleted cache files
* intact as tombstones.
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 9d83bd7..a2c5a92 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -485,6 +485,7 @@
* partition. This option is only used by system apps and so it requires
* android.permission.ACCESS_CACHE_FILESYSTEM permission.
*/
+ @Deprecated
public static final int DESTINATION_SYSTEMCACHE_PARTITION = 5;
/**
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 9c361b3..e923223 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -16,11 +16,14 @@
package com.android.internal.content;
+import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL;
+
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageParser.PackageLite;
import android.os.Environment;
@@ -39,21 +42,19 @@
import com.android.internal.annotations.VisibleForTesting;
+import libcore.io.IoUtils;
+
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Objects;
+import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
-import libcore.io.IoUtils;
-
-import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL;
-
/**
* Constants used internally between the PackageManager
* and media container service transports.
@@ -358,7 +359,7 @@
public boolean fitsOnInternalStorage(Context context, SessionParams params)
throws IOException {
StorageManager storage = getStorageManager(context);
- File target = getDataDirectory();
+ final UUID target = storage.getUuidForPath(getDataDirectory());
return (params.sizeBytes <= storage.getAllocatableBytes(target,
translateAllocateFlags(params.installFlags)));
}
@@ -466,7 +467,8 @@
boolean isInternalStorage = ID_PRIVATE_INTERNAL.equals(vol.id);
if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()
&& (!isInternalStorage || allow3rdPartyOnInternal)) {
- final long availBytes = storageManager.getAllocatableBytes(new File(vol.path),
+ final UUID target = storageManager.getUuidForPath(new File(vol.path));
+ final long availBytes = storageManager.getAllocatableBytes(target,
translateAllocateFlags(params.installFlags));
if (availBytes >= params.sizeBytes) {
allCandidates.add(vol.fsUuid);
@@ -523,7 +525,7 @@
public static boolean fitsOnInternal(Context context, SessionParams params) throws IOException {
final StorageManager storage = context.getSystemService(StorageManager.class);
- final File target = Environment.getDataDirectory();
+ final UUID target = storage.getUuidForPath(Environment.getDataDirectory());
return (params.sizeBytes <= storage.getAllocatableBytes(target,
translateAllocateFlags(params.installFlags)));
}
diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index 42b06f5..68b9b00 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -29,13 +29,15 @@
import android.net.wifi.WifiManager;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.os.UserHandle;
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.test.InstrumentationTestCase;
import android.util.Log;
+import libcore.io.Streams;
+
import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.MockWebServer;
@@ -54,8 +56,6 @@
import java.util.Set;
import java.util.concurrent.TimeoutException;
-import libcore.io.Streams;
-
/**
* Base class for Instrumented tests for the Download Manager.
*/
@@ -83,9 +83,6 @@
protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000; // 1 second
protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 30 * 1000; // 30 seconds
- protected static final int DOWNLOAD_TO_SYSTEM_CACHE = 1;
- protected static final int DOWNLOAD_TO_DOWNLOAD_CACHE_DIR = 2;
-
// Just a few popular file types used to return from a download
protected enum DownloadFileType {
PLAINTEXT,
@@ -923,13 +920,13 @@
* @param body The body to return in the response from the server
*/
protected long doStandardEnqueue(byte[] body) throws Exception {
- return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+ return enqueueDownloadRequest(body);
}
- protected long enqueueDownloadRequest(byte[] body, int location) throws Exception {
+ protected long enqueueDownloadRequest(byte[] body) throws Exception {
// Prepare the mock server with a standard response
mServer.enqueue(buildResponse(HTTP_OK, body));
- return doEnqueue(location);
+ return doEnqueue();
}
/**
@@ -938,13 +935,13 @@
* @param body The body to return in the response from the server, contained in the file
*/
protected long doStandardEnqueue(File body) throws Exception {
- return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+ return enqueueDownloadRequest(body);
}
- protected long enqueueDownloadRequest(File body, int location) throws Exception {
+ protected long enqueueDownloadRequest(File body) throws Exception {
// Prepare the mock server with a standard response
mServer.enqueue(buildResponse(HTTP_OK, body));
- return doEnqueue(location);
+ return doEnqueue();
}
/**
@@ -952,16 +949,12 @@
* doing a standard enqueue request to the server.
*/
protected long doCommonStandardEnqueue() throws Exception {
- return doEnqueue(DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+ return doEnqueue();
}
- private long doEnqueue(int location) throws Exception {
+ private long doEnqueue() throws Exception {
Uri uri = getServerUri(DEFAULT_FILENAME);
Request request = new Request(uri).setTitle(DEFAULT_FILENAME);
- if (location == DOWNLOAD_TO_SYSTEM_CACHE) {
- request.setDestinationToSystemCache();
- }
-
return mDownloadManager.enqueue(request);
}
@@ -1026,8 +1019,8 @@
/**
* Helper that does the actual basic download verification.
*/
- protected long doBasicDownload(byte[] blobData, int location) throws Exception {
- long dlRequest = enqueueDownloadRequest(blobData, location);
+ protected long doBasicDownload(byte[] blobData) throws Exception {
+ long dlRequest = enqueueDownloadRequest(blobData);
// wait for the download to complete
waitForDownloadOrTimeout(dlRequest);
diff --git a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
index d1a5d28..c1d4be0 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
@@ -23,12 +23,13 @@
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.test.suitebuilder.annotation.LargeTest;
+
import com.google.mockwebserver.MockResponse;
import java.io.File;
-import java.util.concurrent.TimeoutException;
import java.util.Iterator;
import java.util.Set;
+import java.util.concurrent.TimeoutException;
/**
* Integration tests of the DownloadManager API.
@@ -95,11 +96,11 @@
* Test a basic download of a binary file 500k in size.
*/
@LargeTest
- public void testBinaryDownloadToSystemCache() throws Exception {
+ public void testBinaryDownload() throws Exception {
int fileSize = 1024;
byte[] blobData = generateData(fileSize, DataType.BINARY);
- long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+ long dlRequest = doBasicDownload(blobData);
verifyDownload(dlRequest, blobData);
mDownloadManager.remove(dlRequest);
}
@@ -108,11 +109,11 @@
* Tests the basic downloading of a text file 300000 bytes in size.
*/
@LargeTest
- public void testTextDownloadToSystemCache() throws Exception {
+ public void testTextDownload() throws Exception {
int fileSize = 1024;
byte[] blobData = generateData(fileSize, DataType.TEXT);
- long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+ long dlRequest = doBasicDownload(blobData);
verifyDownload(dlRequest, blobData);
mDownloadManager.remove(dlRequest);
}
@@ -318,7 +319,7 @@
int fileSize = 1024;
byte[] blobData = generateData(fileSize, DataType.BINARY);
- long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+ long dlRequest = doBasicDownload(blobData);
Cursor cursor = mDownloadManager.query(new Query().setFilterById(dlRequest));
try {
assertEquals("The count of downloads with this ID is not 1!", 1, cursor.getCount());
diff --git a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
index 9fa9131..39d9a8e 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
@@ -195,7 +195,7 @@
// try to download 1MB file into /cache - and it should succeed
byte[] blobData = generateData(DOWNLOAD_FILE_SIZE, DataType.TEXT);
- long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+ long dlRequest = doBasicDownload(blobData);
verifyAndCleanupSingleFileDownload(dlRequest, blobData);
} finally {
if (outFile != null) {
diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
index 5c497b4..55092fa 100644
--- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
@@ -34,6 +34,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.UUID;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.os.storage.VolumeInfo.STATE_MOUNTED;
@@ -90,14 +91,20 @@
File internalFile = new File(sInternalVolPath);
File adoptedFile = new File(sAdoptedVolPath);
File publicFile = new File(sPublicVolPath);
+ UUID internalUuid = UUID.randomUUID();
+ UUID adoptedUuid = UUID.randomUUID();
+ UUID publicUuid = UUID.randomUUID();
Mockito.when(storageManager.getStorageBytesUntilLow(internalFile)).thenReturn(sInternalSize);
Mockito.when(storageManager.getStorageBytesUntilLow(adoptedFile)).thenReturn(sAdoptedSize);
Mockito.when(storageManager.getStorageBytesUntilLow(publicFile)).thenReturn(sPublicSize);
- Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(internalFile), Mockito.anyInt()))
+ Mockito.when(storageManager.getUuidForPath(Mockito.eq(internalFile))).thenReturn(internalUuid);
+ Mockito.when(storageManager.getUuidForPath(Mockito.eq(adoptedFile))).thenReturn(adoptedUuid);
+ Mockito.when(storageManager.getUuidForPath(Mockito.eq(publicFile))).thenReturn(publicUuid);
+ Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(internalUuid), Mockito.anyInt()))
.thenReturn(sInternalSize);
- Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(adoptedFile), Mockito.anyInt()))
+ Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(adoptedUuid), Mockito.anyInt()))
.thenReturn(sAdoptedSize);
- Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(publicFile), Mockito.anyInt()))
+ Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(publicUuid), Mockito.anyInt()))
.thenReturn(sPublicSize);
return storageManager;
}