Implement ANR delay with app IO blocked reason

The ExternalStorageService now notifies the system_server of app IO
blocked reasons, specifically transcoding. If an ANR occurs, we query
these reasons for the uid to modify the ANR dialog behavior

Bug: 170486601
Test: Manual
Change-Id: I86f800c4a6c565a7bced0f2a5c5da7ba6c048168
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index b12bb2e..396ba2d 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.os.IVold;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -112,4 +113,10 @@
      * @param bytes number of bytes which need to be freed
      */
     public abstract void freeCache(@Nullable String volumeUuid, long bytes);
+
+    /**
+     * Returns the {@link VolumeInfo#getId()} values for the volumes matching
+     * {@link VolumeInfo#isPrimary()}
+     */
+    public abstract List<String> getPrimaryVolumeIds();
 }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 233a50d..5486ca6 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -573,6 +573,12 @@
      */
     private static final int PBKDF2_HASH_ROUNDS = 1024;
 
+    private static final String ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY =
+            "anr_delay_millis";
+
+    private static final String ANR_DELAY_NOTIFY_EXTERNAL_STORAGE_SERVICE_DEVICE_CONFIG_KEY =
+            "anr_delay_notify_external_storage_service";
+
     /**
      * Mounted OBB tracking information. Used to track the current state of all
      * OBBs.
@@ -943,25 +949,51 @@
         }
     }
 
-    // TODO(b/170486601): Check transcoding status based on events pushed from the MediaProvider
     private class ExternalStorageServiceAnrController implements AnrController {
         @Override
         public long getAnrDelayMillis(String packageName, int uid) {
-            int delay = SystemProperties.getInt("sys.fuse.transcode_anr_delay", 0);
-            Log.d(TAG, "getAnrDelayMillis: " + packageName + ". Delaying for " + delay + "ms");
+            if (!isAppIoBlocked(uid)) {
+                return 0;
+            }
+
+            int delay = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                    ANR_DELAY_MILLIS_DEVICE_CONFIG_KEY, 0);
+            Slog.v(TAG, "getAnrDelayMillis for " + packageName + ". " + delay + "ms");
             return delay;
         }
 
         @Override
         public void onAnrDelayStarted(String packageName, int uid) {
-            Log.d(TAG, "onAnrDelayStarted: " + packageName);
+            if (!isAppIoBlocked(uid)) {
+                return;
+            }
+
+            boolean notifyExternalStorageService = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                    ANR_DELAY_NOTIFY_EXTERNAL_STORAGE_SERVICE_DEVICE_CONFIG_KEY, true);
+            if (notifyExternalStorageService) {
+                Slog.d(TAG, "onAnrDelayStarted for " + packageName
+                        + ". Notifying external storage service");
+                try {
+                    mStorageSessionController.notifyAnrDelayStarted(packageName, uid, 0 /* tid */,
+                            StorageManager.APP_IO_BLOCKED_REASON_TRANSCODING);
+                } catch (ExternalStorageServiceException e) {
+                    Slog.e(TAG, "Failed to notify ANR delay started for " + packageName, e);
+                }
+            } else {
+                // TODO(b/170973510): Implement framework spinning dialog for ANR delay
+            }
         }
 
         @Override
         public boolean onAnrDelayCompleted(String packageName, int uid) {
-            boolean show = SystemProperties.getBoolean("sys.fuse.transcode_anr_dialog_show", true);
-            Log.d(TAG, "onAnrDelayCompleted: " + packageName + ". Show: " + show);
-            return show;
+            if (isAppIoBlocked(uid)) {
+                Slog.d(TAG, "onAnrDelayCompleted for " + packageName + ". Showing ANR dialog...");
+                return true;
+            } else {
+                Slog.d(TAG, "onAnrDelayCompleted for " + packageName + ". Skipping ANR dialog...");
+                return false;
+            }
         }
     }
 
@@ -4637,5 +4669,19 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        @Override
+        public List<String> getPrimaryVolumeIds() {
+            final List<String> primaryVolumeIds = new ArrayList<>();
+            synchronized (mLock) {
+                for (int i = 0; i < mVolumes.size(); i++) {
+                    final VolumeInfo vol = mVolumes.valueAt(i);
+                    if (vol.isPrimary()) {
+                        primaryVolumeIds.add(vol.getId());
+                    }
+                }
+            }
+            return primaryVolumeIds;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index d2b05c0..797f0f2 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -53,6 +53,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -72,6 +73,7 @@
     private final Context mContext;
     private final int mUserId;
     private final StorageSessionController mSessionController;
+    private final StorageManagerInternal mSmInternal;
     private final ActiveConnection mActiveConnection = new ActiveConnection();
     @GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>();
     @GuardedBy("mLock") private final Set<Integer> mUidsBlockedOnIo = new ArraySet<>();
@@ -81,6 +83,7 @@
         mContext = Objects.requireNonNull(context);
         mUserId = Preconditions.checkArgumentNonnegative(userId);
         mSessionController = controller;
+        mSmInternal = LocalServices.getService(StorageManagerInternal.class);
         mHandlerThread = new HandlerThread("StorageUserConnectionThread-" + mUserId);
         mHandlerThread.start();
     }
@@ -152,9 +155,13 @@
      */
     public void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason)
             throws ExternalStorageServiceException {
+        List<String> primarySessionIds = mSmInternal.getPrimaryVolumeIds();
         synchronized (mSessionsLock) {
             for (String sessionId : mSessions.keySet()) {
-                mActiveConnection.notifyAnrDelayStarted(packageName, uid, tid, reason);
+                if (primarySessionIds.contains(sessionId)) {
+                    mActiveConnection.notifyAnrDelayStarted(packageName, uid, tid, reason);
+                    return;
+                }
             }
         }
     }
@@ -201,8 +208,7 @@
                 return;
             }
         }
-        StorageManagerInternal sm = LocalServices.getService(StorageManagerInternal.class);
-        sm.resetUser(mUserId);
+        mSmInternal.resetUser(mUserId);
     }
 
     /**