Merge "Add per-media-type logging for deletion" into mainline-prod
diff --git a/apex/framework/java/android/provider/MediaStore.java b/apex/framework/java/android/provider/MediaStore.java
index 91709f8..e49f338 100644
--- a/apex/framework/java/android/provider/MediaStore.java
+++ b/apex/framework/java/android/provider/MediaStore.java
@@ -1699,6 +1699,12 @@
             public static final int MEDIA_TYPE_DOCUMENT = 6;
 
             /**
+             * Constant indicating the count of {@link #MEDIA_TYPE} columns.
+             * @hide
+             */
+            public static final int MEDIA_TYPE_COUNT = 7;
+
+            /**
              * Modifier of the database row
              *
              * Specifies the last modifying operation of the database row. This
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 9e1efa5..f1902dc 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -4706,6 +4706,7 @@
             };
             final boolean isFilesTable = qb.getTables().equals("files");
             final LongSparseArray<String> deletedDownloadIds = new LongSparseArray<>();
+            final int[] countPerMediaType = new int[FileColumns.MEDIA_TYPE_COUNT];
             if (isFilesTable) {
                 String deleteparam = uri.getQueryParameter(MediaStore.PARAM_DELETE_DATA);
                 if (deleteparam == null || ! deleteparam.equals("false")) {
@@ -4723,7 +4724,13 @@
                             mCallingIdentity.get().setOwned(id, false);
 
                             deleteIfAllowed(uri, extras, data);
-                            count += qb.delete(helper, BaseColumns._ID + "=" + id, null);
+                            int res = qb.delete(helper, BaseColumns._ID + "=" + id, null);
+                            count += res;
+                            // Avoid ArrayIndexOutOfBounds if more mediaTypes are added,
+                            // but mediaTypeSize is not updated
+                            if (res > 0 && mediaType < countPerMediaType.length) {
+                                countPerMediaType[mediaType] += res;
+                            }
 
                             // Only need to inform DownloadProvider about the downloads deleted on
                             // external volume.
@@ -4781,7 +4788,7 @@
 
             if (isFilesTable && !isCallingPackageSelf()) {
                 Metrics.logDeletion(volumeName, mCallingIdentity.get().uid,
-                        getCallingPackageOrSelf(), count);
+                        getCallingPackageOrSelf(), count, countPerMediaType);
             }
         }
 
diff --git a/src/com/android/providers/media/scan/ModernMediaScanner.java b/src/com/android/providers/media/scan/ModernMediaScanner.java
index 5dc59d4..27e3711 100644
--- a/src/com/android/providers/media/scan/ModernMediaScanner.java
+++ b/src/com/android/providers/media/scan/ModernMediaScanner.java
@@ -47,6 +47,8 @@
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 
+import static com.android.providers.media.util.Metrics.translateReason;
+
 import android.content.ContentProviderClient;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
@@ -511,12 +513,20 @@
             queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_INCLUDE);
             queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_FAVORITE, MediaStore.MATCH_INCLUDE);
 
-            try (Cursor c = mResolver.query(mFilesUri, new String[] { FileColumns._ID },
+            final int[] countPerMediaType = new int[FileColumns.MEDIA_TYPE_COUNT];
+            try (Cursor c = mResolver.query(mFilesUri,
+                    new String[] { FileColumns._ID, FileColumns.MEDIA_TYPE },
                     queryArgs, mSignal)) {
                 while (c.moveToNext()) {
                     final long id = c.getLong(0);
                     if (Arrays.binarySearch(scannedIds, id) < 0) {
                         mUnknownIds.add(id);
+                        final int mediaType = c.getInt(1);
+                        // Avoid ArrayIndexOutOfBounds if more mediaTypes are added,
+                        // but mediaTypeSize is not updated
+                        if (mediaType < countPerMediaType.length) {
+                            countPerMediaType[mediaType]++;
+                        }
                     }
                 }
             } finally {
@@ -538,6 +548,10 @@
                 }
                 applyPending();
             } finally {
+                if (mUnknownIds.size() > 0) {
+                    String scanReason = "scan triggered by reason: " + translateReason(mReason);
+                    Metrics.logDeletionPersistent(mVolumeName, scanReason, countPerMediaType);
+                }
                 Trace.endSection();
             }
         }
diff --git a/src/com/android/providers/media/util/Metrics.java b/src/com/android/providers/media/util/Metrics.java
index 86a0302..410da3a 100644
--- a/src/com/android/providers/media/util/Metrics.java
+++ b/src/com/android/providers/media/util/Metrics.java
@@ -60,12 +60,30 @@
                 normalizedInsertCount, normalizedUpdateCount, normalizedDeleteCount);
     }
 
-    public static void logDeletion(@NonNull String volumeName, int uid, String packageName,
-            int itemCount) {
-        Logging.logPersistent(String.format(
-                "Deleted %3$d items on %1$s due to %2$s",
-                volumeName, packageName, itemCount));
+    /**
+     * Logs persistent deletion logs on-device.
+     */
+    public static void logDeletionPersistent(@NonNull String volumeName, String reason,
+            int[] countPerMediaType) {
+        final StringBuilder builder = new StringBuilder("Deleted ");
+        for (int count: countPerMediaType) {
+            builder.append(count).append(' ');
+        }
+        builder.append("items on ")
+                .append(volumeName)
+                .append(" due to ")
+                .append(reason);
 
+        Logging.logPersistent(builder.toString());
+    }
+
+    /**
+     * Logs persistent deletion logs on-device and stats metrics. Count of items per-media-type
+     * are not uploaded to MediaProviderStats logs.
+     */
+    public static void logDeletion(@NonNull String volumeName, int uid, String packageName,
+            int itemCount, int[] countPerMediaType) {
+        logDeletionPersistent(volumeName, packageName, countPerMediaType);
         MediaProviderStatsLog.write(MEDIA_CONTENT_DELETED,
                 translateVolumeName(volumeName), uid, itemCount);
     }
diff --git a/tests/src/com/android/providers/media/util/MetricsTest.java b/tests/src/com/android/providers/media/util/MetricsTest.java
index 41e25b5..688e2e9 100644
--- a/tests/src/com/android/providers/media/util/MetricsTest.java
+++ b/tests/src/com/android/providers/media/util/MetricsTest.java
@@ -41,7 +41,8 @@
         final String packageName = "com.example";
 
         Metrics.logScan(volumeName, MediaScanner.REASON_UNKNOWN, 42, 42, 42, 42, 42);
-        Metrics.logDeletion(volumeName, 42, packageName, 42);
+        Metrics.logDeletionPersistent(volumeName, "scanReason", new int[] { 42 });
+        Metrics.logDeletion(volumeName, 42, packageName, 42, new int[] { 42 });
         Metrics.logPermissionGranted(volumeName, 42, packageName, 42);
         Metrics.logPermissionDenied(volumeName, 42, packageName, 42);
         Metrics.logSchemaChange(volumeName, 42, 42, 42, 42);