ImageExporter: Accept and store UUID as ImageUniqueId in output

Write a provided UUID as the ImageUniqueId tag in the Exif metadata.

Bug: 177996487
Test: atest ImageExporterTest
Change-Id: I150af1bf80e6ead7e78d7d961168a64112994ede
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
index 87e236ce..8fc2830 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -47,6 +47,7 @@
 import java.time.Instant;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
+import java.util.UUID;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -74,7 +75,7 @@
     private static final String EXIF_WRITE_EXCEPTION =
             "ExifInterface threw an exception writing to the file descriptor.";
     private static final String RESOLVER_UPDATE_ZERO_ROWS =
-            "Failed to publishEntry. ContentResolver#update reported no rows updated.";
+            "Failed to publish entry. ContentResolver#update reported no rows updated.";
     private static final String IMAGE_COMPRESS_RETURNED_FALSE =
             "Bitmap.compress returned false. (Failure unknown)";
 
@@ -116,7 +117,7 @@
      *
      * @return a listenable future result
      */
-    ListenableFuture<Result> export(Executor executor, String requestId, Bitmap bitmap) {
+    ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap) {
         return export(executor, requestId, bitmap, ZonedDateTime.now());
     }
 
@@ -128,7 +129,7 @@
      *
      * @return a listenable future result
      */
-    ListenableFuture<Result> export(Executor executor, String requestId, Bitmap bitmap,
+    ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
             ZonedDateTime captureTime) {
         final Task task =
                 new Task(mResolver, requestId, bitmap, captureTime, mCompressFormat, mQuality);
@@ -147,7 +148,7 @@
     }
 
     static class Result {
-        String requestId;
+        UUID requestId;
         String fileName;
         long timestamp;
         Uri uri;
@@ -156,14 +157,14 @@
 
     private static class Task {
         private final ContentResolver mResolver;
-        private final String mRequestId;
+        private final UUID mRequestId;
         private final Bitmap mBitmap;
         private final ZonedDateTime mCaptureTime;
         private final CompressFormat mFormat;
         private final int mQuality;
         private final String mFileName;
 
-        Task(ContentResolver resolver, String requestId, Bitmap bitmap, ZonedDateTime captureTime,
+        Task(ContentResolver resolver, UUID requestId, Bitmap bitmap, ZonedDateTime captureTime,
                 CompressFormat format, int quality) {
             mResolver = resolver;
             mRequestId = requestId;
@@ -191,7 +192,7 @@
                 writeImage(mBitmap, mFormat, mQuality, uri);
                 throwIfInterrupted();
 
-                writeExif(uri, mBitmap.getWidth(), mBitmap.getHeight(), mCaptureTime);
+                writeExif(uri, mRequestId, mBitmap.getWidth(), mBitmap.getHeight(), mCaptureTime);
                 throwIfInterrupted();
 
                 publishEntry(uri);
@@ -251,7 +252,7 @@
             }
         }
 
-        void writeExif(Uri uri, int width, int height, ZonedDateTime captureTime)
+        void writeExif(Uri uri, UUID requestId, int width, int height, ZonedDateTime captureTime)
                 throws ImageExportException {
             Trace.beginSection("ImageExporter_writeExif");
             ParcelFileDescriptor pfd = null;
@@ -267,7 +268,7 @@
                     throw new ImageExportException(EXIF_READ_EXCEPTION, e);
                 }
 
-                updateExifAttributes(exif, width, height, captureTime);
+                updateExifAttributes(exif, requestId, width, height, captureTime);
                 try {
                     exif.saveAttributes();
                 } catch (IOException e) {
@@ -321,8 +322,10 @@
         return values;
     }
 
-    static void updateExifAttributes(ExifInterface exif, int width, int height,
+    static void updateExifAttributes(ExifInterface exif, UUID uniqueId, int width, int height,
             ZonedDateTime captureTime) {
+        exif.setAttribute(ExifInterface.TAG_IMAGE_UNIQUE_ID, uniqueId.toString());
+
         exif.setAttribute(ExifInterface.TAG_SOFTWARE, "Android " + Build.DISPLAY);
         exif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH, Integer.toString(width));
         exif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH, Integer.toString(height));
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index d42847d..7f13397 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -119,14 +119,13 @@
             return null;
         }
         // TODO: move to constructor / from ScreenshotRequest
-        final UUID uuid = UUID.randomUUID();
+        final UUID requestId = UUID.randomUUID();
         final UserHandle user = getUserHandleOfForegroundApplication(mContext);
 
         Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
 
         Bitmap image = mParams.image;
-        String requestId = uuid.toString();
-        mScreenshotId = String.format(SCREENSHOT_ID_TEMPLATE, uuid);
+        mScreenshotId = String.format(SCREENSHOT_ID_TEMPLATE, requestId);
         try {
             // Call synchronously here since already on a background thread.
             ListenableFuture<ImageExporter.Result> future =
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 7d57799..f59c8f1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -55,6 +55,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 2ccb2ce..54b1b2c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -55,7 +55,7 @@
     private final ImageTileSet mImageTileSet;
 
     private ZonedDateTime mCaptureTime;
-    private String mRequestId;
+    private UUID mRequestId;
 
     public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
             Executor bgExecutor, ImageExporter exporter) {
@@ -74,7 +74,7 @@
      */
     public void run(final Runnable after) {
         mCaptureTime = ZonedDateTime.now();
-        mRequestId = UUID.randomUUID().toString();
+        mRequestId = UUID.randomUUID();
         mConnection.start((session) -> startCapture(session, after));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java
index c2a08ba..b0f78ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java
@@ -54,6 +54,7 @@
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
+import java.util.UUID;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 
@@ -78,10 +79,13 @@
     public void testUpdateExifAttributes_timeZoneUTC() throws IOException {
         ExifInterface exifInterface = new ExifInterface(new ByteArrayInputStream(EXIF_FILE_TAG),
                 ExifInterface.STREAM_TYPE_EXIF_DATA_ONLY);
-
-        ImageExporter.updateExifAttributes(exifInterface, 100, 100,
+        ImageExporter.updateExifAttributes(exifInterface,
+                UUID.fromString("3c11da99-9284-4863-b1d5-6f3684976814"), 100, 100,
                 ZonedDateTime.of(LocalDateTime.of(2020, 12, 15, 18, 15), ZoneId.of("UTC")));
 
+        assertEquals("Exif " + ExifInterface.TAG_IMAGE_UNIQUE_ID,
+                "3c11da99-9284-4863-b1d5-6f3684976814",
+                exifInterface.getAttribute(ExifInterface.TAG_IMAGE_UNIQUE_ID));
         assertEquals("Exif " + ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "+00:00",
                 exifInterface.getAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL));
     }
@@ -92,7 +96,7 @@
         ContentResolver contentResolver = context.getContentResolver();
         ImageExporter exporter = new ImageExporter(contentResolver);
 
-        String requestId = "some_random_unique_id";
+        UUID requestId = UUID.fromString("3c11da99-9284-4863-b1d5-6f3684976814");
         Bitmap original = createCheckerBitmap(10, 10, 10);
 
         ListenableFuture<ImageExporter.Result> direct =
@@ -120,6 +124,10 @@
                 assertNotNull(pfd);
                 ExifInterface exifInterface = new ExifInterface(pfd.getFileDescriptor());
 
+                assertEquals("Exif " + ExifInterface.TAG_IMAGE_UNIQUE_ID,
+                        "3c11da99-9284-4863-b1d5-6f3684976814",
+                        exifInterface.getAttribute(ExifInterface.TAG_IMAGE_UNIQUE_ID));
+
                 assertEquals("Exif " + ExifInterface.TAG_SOFTWARE, "Android " + Build.DISPLAY,
                         exifInterface.getAttribute(ExifInterface.TAG_SOFTWARE));