Update DrmOutputStream to use raw FileDescriptor.

This allows DownloadManager to use FDs, paving the way for downloading
directly to content:// Uris.

Also return flag indicating if deleteOlderFiles() actually deleted
anything.  Update tests to verify.

Bug: 5287571
Change-Id: I2579e5e2113f31b2860d7b021bd61c91b6310963
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 15a154a..dc18dee 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -326,14 +326,15 @@
      *
      * @param minCount Always keep at least this many files.
      * @param minAge Always keep files younger than this age.
+     * @return if any files were deleted.
      */
-    public static void deleteOlderFiles(File dir, int minCount, long minAge) {
+    public static boolean deleteOlderFiles(File dir, int minCount, long minAge) {
         if (minCount < 0 || minAge < 0) {
             throw new IllegalArgumentException("Constraints must be positive or 0");
         }
 
         final File[] files = dir.listFiles();
-        if (files == null) return;
+        if (files == null) return false;
 
         // Sort with newest files first
         Arrays.sort(files, new Comparator<File>() {
@@ -344,16 +345,20 @@
         });
 
         // Keep at least minCount files
+        boolean deleted = false;
         for (int i = minCount; i < files.length; i++) {
             final File file = files[i];
 
             // Keep files newer than minAge
             final long age = System.currentTimeMillis() - file.lastModified();
             if (age > minAge) {
-                Log.d(TAG, "Deleting old file " + file);
-                file.delete();
+                if (file.delete()) {
+                    Log.d(TAG, "Deleted old file " + file);
+                    deleted = true;
+                }
             }
         }
+        return deleted;
     }
 
     /**
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 616f5ab..93e68eb 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -140,7 +140,7 @@
         touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
         touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
         touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
-        FileUtils.deleteOlderFiles(mDir, 3, DAY_IN_MILLIS);
+        assertTrue(FileUtils.deleteOlderFiles(mDir, 3, DAY_IN_MILLIS));
         assertDirContents("file1", "file2", "file3");
     }
 
@@ -148,13 +148,13 @@
         touch("file1", -HOUR_IN_MILLIS);
         touch("file2", HOUR_IN_MILLIS);
         touch("file3", WEEK_IN_MILLIS);
-        FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS);
+        assertTrue(FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS));
         assertDirContents("file1", "file2");
 
         touch("file1", -HOUR_IN_MILLIS);
         touch("file2", HOUR_IN_MILLIS);
         touch("file3", WEEK_IN_MILLIS);
-        FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS);
+        assertTrue(FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS));
         assertDirContents("file1", "file2");
     }
 
@@ -164,7 +164,8 @@
         touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
         touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
         touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
-        FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS);
+        assertTrue(FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS));
+        assertFalse(FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS));
         assertDirContents("file1");
     }
 
@@ -174,7 +175,8 @@
         touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
         touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
         touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
-        FileUtils.deleteOlderFiles(mDir, 2, 0);
+        assertTrue(FileUtils.deleteOlderFiles(mDir, 2, 0));
+        assertFalse(FileUtils.deleteOlderFiles(mDir, 2, 0));
         assertDirContents("file1", "file2");
     }
 
diff --git a/drm/java/android/drm/DrmOutputStream.java b/drm/java/android/drm/DrmOutputStream.java
index 87677b8..22e7ac2 100644
--- a/drm/java/android/drm/DrmOutputStream.java
+++ b/drm/java/android/drm/DrmOutputStream.java
@@ -18,18 +18,23 @@
 
 import static android.drm.DrmConvertedStatus.STATUS_OK;
 import static android.drm.DrmManagerClient.INVALID_SESSION;
+import static libcore.io.OsConstants.SEEK_SET;
 
+import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
+import libcore.io.ErrnoException;
+import libcore.io.IoBridge;
+import libcore.io.Libcore;
+import libcore.io.Streams;
+
+import java.io.FileDescriptor;
 import java.io.FilterOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.RandomAccessFile;
 import java.net.UnknownServiceException;
 import java.util.Arrays;
 
-import libcore.io.Streams;
-
 /**
  * Stream that applies a {@link DrmManagerClient} transformation to data before
  * writing to disk, similar to a {@link FilterOutputStream}.
@@ -40,17 +45,19 @@
     private static final String TAG = "DrmOutputStream";
 
     private final DrmManagerClient mClient;
-    private final RandomAccessFile mFile;
+    private final ParcelFileDescriptor mPfd;
+    private final FileDescriptor mFd;
 
     private int mSessionId = INVALID_SESSION;
 
     /**
-     * @param file Opened with "rw" mode.
+     * @param pfd Opened with "rw" mode.
      */
-    public DrmOutputStream(DrmManagerClient client, RandomAccessFile file, String mimeType)
+    public DrmOutputStream(DrmManagerClient client, ParcelFileDescriptor pfd, String mimeType)
             throws IOException {
         mClient = client;
-        mFile = file;
+        mPfd = pfd;
+        mFd = pfd.getFileDescriptor();
 
         mSessionId = mClient.openConvertSession(mimeType);
         if (mSessionId == INVALID_SESSION) {
@@ -61,8 +68,12 @@
     public void finish() throws IOException {
         final DrmConvertedStatus status = mClient.closeConvertSession(mSessionId);
         if (status.statusCode == STATUS_OK) {
-            mFile.seek(status.offset);
-            mFile.write(status.convertedData);
+            try {
+                Libcore.os.lseek(mFd, status.offset, SEEK_SET);
+            } catch (ErrnoException e) {
+                e.rethrowAsIOException();
+            }
+            IoBridge.write(mFd, status.convertedData, 0, status.convertedData.length);
             mSessionId = INVALID_SESSION;
         } else {
             throw new IOException("Unexpected DRM status: " + status.statusCode);
@@ -75,7 +86,7 @@
             Log.w(TAG, "Closing stream without finishing");
         }
 
-        mFile.close();
+        mPfd.close();
     }
 
     @Override
@@ -92,7 +103,7 @@
 
         final DrmConvertedStatus status = mClient.convertData(mSessionId, exactBuffer);
         if (status.statusCode == STATUS_OK) {
-            mFile.write(status.convertedData);
+            IoBridge.write(mFd, status.convertedData, 0, status.convertedData.length);
         } else {
             throw new IOException("Unexpected DRM status: " + status.statusCode);
         }