Merge "Get ANR delay from the MediaProvider" into sc-dev

This commit is contained in:
Zimuzo Ezeozue
2021-02-15 10:36:32 +00:00
committed by Android (Google) Code Review
6 changed files with 155 additions and 16 deletions

View File

@@ -10386,6 +10386,7 @@ package android.service.storage {
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
method public void onFreeCache(@NonNull java.util.UUID, long) throws java.io.IOException;
method public long onGetAnrDelayMillis(@NonNull String, int);
method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException;
method public abstract void onVolumeStateChanged(@NonNull android.os.storage.StorageVolume) throws java.io.IOException;
field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2

View File

@@ -95,6 +95,21 @@ public abstract class ExternalStorageService extends Service {
public static final String EXTRA_ERROR =
"android.service.storage.extra.error";
/**
* {@link Bundle} key for a package name {@link String} value.
*
* {@hide}
*/
public static final String EXTRA_PACKAGE_NAME = "android.service.storage.extra.package_name";
/**
* {@link Bundle} key for a {@link Long} value.
*
* {@hide}
*/
public static final String EXTRA_ANR_TIMEOUT_MS =
"android.service.storage.extra.anr_timeout_ms";
/** @hide */
@IntDef(flag = true, prefix = {"FLAG_SESSION_"},
value = {FLAG_SESSION_TYPE_FUSE, FLAG_SESSION_ATTRIBUTE_INDEXABLE})
@@ -162,6 +177,15 @@ public abstract class ExternalStorageService extends Service {
throw new UnsupportedOperationException("onFreeCacheRequested not implemented");
}
/**
* Called when {@code packageName} is about to ANR
*
* @return ANR dialog delay in milliseconds
*/
public long onGetAnrDelayMillis(@NonNull String packageName, int uid) {
throw new UnsupportedOperationException("onGetAnrDelayMillis not implemented");
}
@Override
@NonNull
public final IBinder onBind(@NonNull Intent intent) {
@@ -222,6 +246,19 @@ public abstract class ExternalStorageService extends Service {
});
}
@Override
public void getAnrDelayMillis(String packageName, int uid, RemoteCallback callback)
throws RemoteException {
mHandler.post(() -> {
try {
long timeoutMs = onGetAnrDelayMillis(packageName, uid);
sendTimeoutResult(packageName, timeoutMs, null /* throwable */, callback);
} catch (Throwable t) {
sendTimeoutResult(packageName, 0 /* timeoutMs */, t, callback);
}
});
}
private void sendResult(String sessionId, Throwable throwable, RemoteCallback callback) {
Bundle bundle = new Bundle();
bundle.putString(EXTRA_SESSION_ID, sessionId);
@@ -230,5 +267,16 @@ public abstract class ExternalStorageService extends Service {
}
callback.sendResult(bundle);
}
private void sendTimeoutResult(String packageName, long timeoutMs, Throwable throwable,
RemoteCallback callback) {
Bundle bundle = new Bundle();
bundle.putString(EXTRA_PACKAGE_NAME, packageName);
bundle.putLong(EXTRA_ANR_TIMEOUT_MS, timeoutMs);
if (throwable != null) {
bundle.putParcelable(EXTRA_ERROR, new ParcelableException(throwable));
}
callback.sendResult(bundle);
}
}
}

View File

@@ -32,4 +32,5 @@ oneway interface IExternalStorageService
in RemoteCallback callback);
void freeCache(@utf8InCpp String sessionId, in String volumeUuid, long bytes,
in RemoteCallback callback);
void getAnrDelayMillis(String packageName, int uid, in RemoteCallback callback);
}

View File

@@ -939,9 +939,12 @@ class StorageManagerService extends IStorageManager.Stub
if (transcodeEnabled) {
LocalServices.getService(ActivityManagerInternal.class)
.registerAnrController((packageName, uid) -> {
// TODO: Retrieve delay from ExternalStorageService that can check
// transcoding status
return SystemProperties.getInt("sys.fuse.transcode_anr_delay_ms", 0);
try {
return mStorageSessionController.getAnrDelayMillis(packageName, uid);
} catch (ExternalStorageServiceException e) {
Log.e(TAG, "Failed to get ANR delay for " + packageName, e);
return 0;
}
});
}
}

View File

@@ -157,6 +157,29 @@ public final class StorageSessionController {
}
}
/**
* Called when {@code packageName} is about to ANR
*
* @return ANR dialog delay in milliseconds
*/
public long getAnrDelayMillis(String packageName, int uid)
throws ExternalStorageServiceException {
synchronized (mLock) {
int size = mConnections.size();
for (int i = 0; i < size; i++) {
int key = mConnections.keyAt(i);
StorageUserConnection connection = mConnections.get(key);
if (connection != null) {
long delay = connection.getAnrDelayMillis(packageName, uid);
if (delay > 0) {
return delay;
}
}
}
}
return 0;
}
/**
* Removes and returns the {@link StorageUserConnection} for {@code vol}.
*

View File

@@ -16,6 +16,7 @@
package com.android.server.storage;
import static android.service.storage.ExternalStorageService.EXTRA_ANR_TIMEOUT_MS;
import static android.service.storage.ExternalStorageService.EXTRA_ERROR;
import static android.service.storage.ExternalStorageService.FLAG_SESSION_ATTRIBUTE_INDEXABLE;
import static android.service.storage.ExternalStorageService.FLAG_SESSION_TYPE_FUSE;
@@ -142,6 +143,24 @@ public final class StorageUserConnection {
}
}
/**
* Called when {@code packageName} is about to ANR
*
* @return ANR dialog delay in milliseconds
*/
public long getAnrDelayMillis(String packageName, int uid)
throws ExternalStorageServiceException {
synchronized (mSessionsLock) {
for (String sessionId : mSessions.keySet()) {
long delay = mActiveConnection.getAnrDelayMillis(packageName, uid);
if (delay > 0) {
return delay;
}
}
}
return 0;
}
/**
* Removes a session without ending it or waiting for exit.
*
@@ -234,6 +253,9 @@ public final class StorageUserConnection {
@GuardedBy("mLock")
private final ArrayList<CompletableFuture<Void>> mOutstandingOps = new ArrayList<>();
@GuardedBy("mLock")
private final ArrayList<CompletableFuture<Long>> mOutstandingTimeoutOps = new ArrayList<>();
@Override
public void close() {
ServiceConnection oldConnection = null;
@@ -250,6 +272,9 @@ public final class StorageUserConnection {
for (CompletableFuture<Void> op : mOutstandingOps) {
op.cancel(true);
}
for (CompletableFuture<Long> op : mOutstandingTimeoutOps) {
op.cancel(true);
}
mOutstandingOps.clear();
}
@@ -264,27 +289,44 @@ public final class StorageUserConnection {
}
}
private void waitForAsync(AsyncStorageServiceCall asyncCall) throws Exception {
CompletableFuture<IExternalStorageService> serviceFuture = connectIfNeeded();
private void waitForAsyncVoid(AsyncStorageServiceCall asyncCall) throws Exception {
CompletableFuture<Void> opFuture = new CompletableFuture<>();
RemoteCallback callback = new RemoteCallback(result -> setResult(result, opFuture));
waitForAsync(asyncCall, callback, opFuture, mOutstandingOps,
DEFAULT_REMOTE_TIMEOUT_SECONDS);
}
private long waitForAsyncLong(AsyncStorageServiceCall asyncCall) throws Exception {
CompletableFuture<Long> opFuture = new CompletableFuture<>();
RemoteCallback callback =
new RemoteCallback(result -> setTimeoutResult(result, opFuture));
return waitForAsync(asyncCall, callback, opFuture, mOutstandingTimeoutOps,
1 /* timeoutSeconds */);
}
private <T> T waitForAsync(AsyncStorageServiceCall asyncCall, RemoteCallback callback,
CompletableFuture<T> opFuture, ArrayList<CompletableFuture<T>> outstandingOps,
long timeoutSeconds) throws Exception {
CompletableFuture<IExternalStorageService> serviceFuture = connectIfNeeded();
try {
synchronized (mLock) {
mOutstandingOps.add(opFuture);
outstandingOps.add(opFuture);
}
serviceFuture.thenCompose(service -> {
return serviceFuture.thenCompose(service -> {
try {
asyncCall.run(service,
new RemoteCallback(result -> setResult(result, opFuture)));
asyncCall.run(service, callback);
} catch (RemoteException e) {
opFuture.completeExceptionally(e);
}
return opFuture;
}).get(DEFAULT_REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}).get(timeoutSeconds, TimeUnit.SECONDS);
} finally {
synchronized (mLock) {
mOutstandingOps.remove(opFuture);
outstandingOps.remove(opFuture);
}
}
}
@@ -292,9 +334,9 @@ public final class StorageUserConnection {
public void startSession(Session session, ParcelFileDescriptor fd)
throws ExternalStorageServiceException {
try {
waitForAsync((service, callback) -> service.startSession(session.sessionId,
waitForAsyncVoid((service, callback) -> service.startSession(session.sessionId,
FLAG_SESSION_TYPE_FUSE | FLAG_SESSION_ATTRIBUTE_INDEXABLE,
fd, session.upperPath, session.lowerPath, callback));
fd, session.upperPath, session.lowerPath, callback));
} catch (Exception e) {
throw new ExternalStorageServiceException("Failed to start session: " + session, e);
} finally {
@@ -308,7 +350,7 @@ public final class StorageUserConnection {
public void endSession(Session session) throws ExternalStorageServiceException {
try {
waitForAsync((service, callback) ->
waitForAsyncVoid((service, callback) ->
service.endSession(session.sessionId, callback));
} catch (Exception e) {
throw new ExternalStorageServiceException("Failed to end session: " + session, e);
@@ -319,7 +361,7 @@ public final class StorageUserConnection {
public void notifyVolumeStateChanged(String sessionId, StorageVolume vol) throws
ExternalStorageServiceException {
try {
waitForAsync((service, callback) ->
waitForAsyncVoid((service, callback) ->
service.notifyVolumeStateChanged(sessionId, vol, callback));
} catch (Exception e) {
throw new ExternalStorageServiceException("Failed to notify volume state changed "
@@ -330,7 +372,7 @@ public final class StorageUserConnection {
public void freeCache(String sessionId, String volumeUuid, long bytes)
throws ExternalStorageServiceException {
try {
waitForAsync((service, callback) ->
waitForAsyncVoid((service, callback) ->
service.freeCache(sessionId, volumeUuid, bytes, callback));
} catch (Exception e) {
throw new ExternalStorageServiceException("Failed to free " + bytes
@@ -338,6 +380,27 @@ public final class StorageUserConnection {
}
}
public long getAnrDelayMillis(String packgeName, int uid)
throws ExternalStorageServiceException {
try {
return waitForAsyncLong((service, callback) ->
service.getAnrDelayMillis(packgeName, uid, callback));
} catch (Exception e) {
throw new ExternalStorageServiceException("Failed to notify app not responding: "
+ packgeName, e);
}
}
private void setTimeoutResult(Bundle result, CompletableFuture<Long> future) {
ParcelableException ex = result.getParcelable(EXTRA_ERROR);
if (ex != null) {
future.completeExceptionally(ex);
} else {
long timeoutMs = result.getLong(EXTRA_ANR_TIMEOUT_MS);
future.complete(timeoutMs);
}
}
private void setResult(Bundle result, CompletableFuture<Void> future) {
ParcelableException ex = result.getParcelable(EXTRA_ERROR);
if (ex != null) {