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
This commit is contained in:
Zim
2021-03-02 09:30:41 +00:00
parent 055878b433
commit 1985e314e2
3 changed files with 69 additions and 10 deletions

View File

@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IVold;
import java.util.List;
import java.util.Set;
/**
@@ -112,4 +113,10 @@ public abstract class StorageManagerInternal {
* @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();
}

View File

@@ -573,6 +573,12 @@ class StorageManagerService extends IStorageManager.Stub
*/
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 @@ class StorageManagerService extends IStorageManager.Stub
}
}
// 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 @@ class StorageManagerService extends IStorageManager.Stub
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;
}
}
}

View File

@@ -53,6 +53,7 @@ import java.io.IOException;
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 @@ public final class StorageUserConnection {
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 @@ public final class StorageUserConnection {
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 final class StorageUserConnection {
*/
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 @@ public final class StorageUserConnection {
return;
}
}
StorageManagerInternal sm = LocalServices.getService(StorageManagerInternal.class);
sm.resetUser(mUserId);
mSmInternal.resetUser(mUserId);
}
/**