Avoid wm<->am deadlock when checking uri permission
Bug: 115619667
Test: N/A
Change-Id: I43a868b261eb1546464c401b6ec1417870fa6f9c
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2b86d7f..dc6714b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -337,6 +337,7 @@
import com.android.internal.util.function.HexFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.TriFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AlarmManagerInternal;
import com.android.server.AttributeCache;
import com.android.server.DeviceIdleInternal;
@@ -18763,28 +18764,39 @@
@Override
public int checkContentProviderUriPermission(Uri uri, int userId,
int callingUid, int modeFlags) {
- // We can find ourselves needing to check Uri permissions while
- // already holding the WM lock, which means reaching back here for
- // the AM lock would cause an inversion. The WM team has requested
- // that we use the strategy below instead of shifting where Uri
- // grants are calculated.
+ final Object wmLock = mActivityTaskManager.getGlobalLock();
+ if (Thread.currentThread().holdsLock(wmLock)
+ && !Thread.currentThread().holdsLock(ActivityManagerService.this)) {
+ // We can find ourselves needing to check Uri permissions while already holding the
+ // WM lock, which means reaching back here for the AM lock would cause an inversion.
+ // The WM team has requested that we use the strategy below instead of shifting
+ // where Uri grants are calculated.
+ synchronized (wmLock) {
+ final int[] result = new int[1];
+ final Message msg = PooledLambda.obtainMessage(
+ LocalService::checkContentProviderUriPermission,
+ this, uri, userId, callingUid, modeFlags, wmLock, result);
+ mHandler.sendMessage(msg);
+ try {
+ wmLock.wait();
+ } catch (InterruptedException ignore) {
- // Since we could also arrive here while holding the AM lock, we
- // can't always delegate the call through the handler, and we need
- // to delicately dance between the deadlocks.
- if (Thread.currentThread().holdsLock(ActivityManagerService.this)) {
+ }
+ return result[0];
+ }
+ } else {
return ActivityManagerService.this.checkContentProviderUriPermission(uri,
userId, callingUid, modeFlags);
- } else {
- final CompletableFuture<Integer> res = new CompletableFuture<>();
- mHandler.post(() -> {
- res.complete(ActivityManagerService.this.checkContentProviderUriPermission(uri,
- userId, callingUid, modeFlags));
- });
- try {
- return res.get();
- } catch (InterruptedException | ExecutionException e) {
- throw new RuntimeException(e);
+ }
+ }
+
+ void checkContentProviderUriPermission(
+ Uri uri, int userId, int callingUid, int modeFlags, Object wmLock, int[] result) {
+ synchronized (ActivityManagerService.this) {
+ synchronized (wmLock) {
+ result[0] = ActivityManagerService.this.checkContentProviderUriPermission(
+ uri, userId, callingUid, modeFlags);
+ wmLock.notify();
}
}
}