Read clipboard item mimetype from description

Currently we construct the clipboard model by calling
contentResolver.getType on the main thread, which can cause problems
because that call can hang.
This change switches to reading the mimeType set in the clip description
instead (which should be set correctly per-item).

Bug: 357197236
Flag: com.android.systemui.clipboard_use_description_mimetype
Test: manual with flag on/off, atest ClipboardModelTest
Change-Id: I78550105f9c4f9b3ee1cf69a732b075397cdf4ac
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 02e8cd6..47ddae7 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -582,6 +582,16 @@
 }
 
 flag {
+    name: "clipboard_use_description_mimetype"
+    namespace: "systemui"
+    description: "Read item mimetype from description rather than checking URI"
+    bug: "357197236"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "screenshot_action_dismiss_system_windows"
     namespace: "systemui"
     description: "Dismiss existing system windows when starting action from screenshot UI"
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt
index 12597a7..99c026c 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardModel.kt
@@ -24,6 +24,7 @@
 import android.util.Log
 import android.util.Size
 import android.view.textclassifier.TextLinks
+import com.android.systemui.Flags.clipboardUseDescriptionMimetype
 import com.android.systemui.res.R
 import java.io.IOException
 
@@ -70,11 +71,11 @@
             context: Context,
             utils: ClipboardOverlayUtils,
             clipData: ClipData,
-            source: String
+            source: String,
         ): ClipboardModel {
             val sensitive = clipData.description?.extras?.getBoolean(EXTRA_IS_SENSITIVE) ?: false
             val item = clipData.getItemAt(0)!!
-            val type = getType(context, item)
+            val type = getType(context, item, clipData.description.getMimeType(0))
             val remote = utils.isRemoteCopy(context, clipData, source)
             return ClipboardModel(
                 clipData,
@@ -84,18 +85,26 @@
                 item.textLinks,
                 item.uri,
                 sensitive,
-                remote
+                remote,
             )
         }
 
-        private fun getType(context: Context, item: ClipData.Item): Type {
+        private fun getType(context: Context, item: ClipData.Item, mimeType: String): Type {
             return if (!TextUtils.isEmpty(item.text)) {
                 Type.TEXT
             } else if (item.uri != null) {
-                if (context.contentResolver.getType(item.uri)?.startsWith("image") == true) {
-                    Type.IMAGE
+                if (clipboardUseDescriptionMimetype()) {
+                    if (mimeType.startsWith("image")) {
+                        Type.IMAGE
+                    } else {
+                        Type.URI
+                    }
                 } else {
-                    Type.URI
+                    if (context.contentResolver.getType(item.uri)?.startsWith("image") == true) {
+                        Type.IMAGE
+                    } else {
+                        Type.URI
+                    }
                 }
             } else {
                 Type.OTHER
@@ -107,6 +116,6 @@
         TEXT,
         IMAGE,
         URI,
-        OTHER
+        OTHER,
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
index 5d76e32..85e8ab4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
@@ -22,10 +22,12 @@
 import android.graphics.Bitmap
 import android.net.Uri
 import android.os.PersistableBundle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_CLIPBOARD_USE_DESCRIPTION_MIMETYPE
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.mockito.whenever
 import java.io.IOException
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
@@ -37,6 +39,7 @@
 import org.mockito.ArgumentMatchers.any
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -88,7 +91,8 @@
 
     @Test
     @Throws(IOException::class)
-    fun test_imageClipData() {
+    @DisableFlags(FLAG_CLIPBOARD_USE_DESCRIPTION_MIMETYPE)
+    fun test_imageClipData_legacy() {
         val testBitmap = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888)
         whenever(mMockContext.contentResolver).thenReturn(mMockContentResolver)
         whenever(mMockContext.resources).thenReturn(mContext.resources)
@@ -103,6 +107,21 @@
 
     @Test
     @Throws(IOException::class)
+    @EnableFlags(FLAG_CLIPBOARD_USE_DESCRIPTION_MIMETYPE)
+    fun test_imageClipData() {
+        val testBitmap = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888)
+        whenever(mMockContext.contentResolver).thenReturn(mMockContentResolver)
+        whenever(mMockContext.resources).thenReturn(mContext.resources)
+        whenever(mMockContentResolver.loadThumbnail(any(), any(), any())).thenReturn(testBitmap)
+        whenever(mMockContentResolver.getType(any())).thenReturn("text")
+        val imageClipData = ClipData("Test", arrayOf("image/png"), ClipData.Item(Uri.parse("test")))
+        val model = ClipboardModel.fromClipData(mMockContext, mClipboardUtils, imageClipData, "")
+        assertEquals(ClipboardModel.Type.IMAGE, model.type)
+        assertEquals(testBitmap, model.loadThumbnail(mMockContext))
+    }
+
+    @Test
+    @Throws(IOException::class)
     fun test_imageClipData_loadFailure() {
         whenever(mMockContext.contentResolver).thenReturn(mMockContentResolver)
         whenever(mMockContext.resources).thenReturn(mContext.resources)