Merge "[MediaProjection] Address deadlock between multiple services" into udc-qpr-dev

This commit is contained in:
Naomi Musgrave
2023-07-25 09:27:59 +00:00
committed by Android (Google) Code Review
2 changed files with 29 additions and 13 deletions

View File

@@ -117,6 +117,10 @@ public final class MediaProjectionManagerService extends SystemService
// WindowManagerService -> MediaProjectionManagerService -> DisplayManagerService // WindowManagerService -> MediaProjectionManagerService -> DisplayManagerService
// See mediaprojection.md // See mediaprojection.md
private final Object mLock = new Object(); private final Object mLock = new Object();
// A handler for posting tasks that must interact with a service holding another lock,
// especially for services that will eventually acquire the WindowManager lock.
@NonNull private final Handler mHandler;
private final Map<IBinder, IBinder.DeathRecipient> mDeathEaters; private final Map<IBinder, IBinder.DeathRecipient> mDeathEaters;
private final CallbackDelegate mCallbackDelegate; private final CallbackDelegate mCallbackDelegate;
@@ -145,6 +149,8 @@ public final class MediaProjectionManagerService extends SystemService
super(context); super(context);
mContext = context; mContext = context;
mInjector = injector; mInjector = injector;
// Post messages on the main thread; no need for a separate thread.
mHandler = new Handler(Looper.getMainLooper());
mClock = injector.createClock(); mClock = injector.createClock();
mDeathEaters = new ArrayMap<IBinder, IBinder.DeathRecipient>(); mDeathEaters = new ArrayMap<IBinder, IBinder.DeathRecipient>();
mCallbackDelegate = new CallbackDelegate(injector.createCallbackLooper()); mCallbackDelegate = new CallbackDelegate(injector.createCallbackLooper());
@@ -243,7 +249,9 @@ public final class MediaProjectionManagerService extends SystemService
if (!mProjectionGrant.requiresForegroundService()) { if (!mProjectionGrant.requiresForegroundService()) {
return; return;
} }
}
// Run outside the lock when calling into ActivityManagerService.
if (mActivityManagerInternal.hasRunningForegroundService( if (mActivityManagerInternal.hasRunningForegroundService(
uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)) { uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)) {
// If there is any process within this UID running a FGS // If there is any process within this UID running a FGS
@@ -251,6 +259,7 @@ public final class MediaProjectionManagerService extends SystemService
return; return;
} }
synchronized (mLock) {
mProjectionGrant.stop(); mProjectionGrant.stop();
} }
} }
@@ -867,7 +876,6 @@ public final class MediaProjectionManagerService extends SystemService
mTargetSdkVersion = targetSdkVersion; mTargetSdkVersion = targetSdkVersion;
mIsPrivileged = isPrivileged; mIsPrivileged = isPrivileged;
mCreateTimeMs = mClock.uptimeMillis(); mCreateTimeMs = mClock.uptimeMillis();
// TODO(b/267740338): Add unit test.
mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(), mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
MEDIA_PROJECTION_TOKEN_EVENT_CREATED); MEDIA_PROJECTION_TOKEN_EVENT_CREATED);
} }
@@ -924,6 +932,10 @@ public final class MediaProjectionManagerService extends SystemService
if (callback == null) { if (callback == null) {
throw new IllegalArgumentException("callback must not be null"); throw new IllegalArgumentException("callback must not be null");
} }
// Cache result of calling into ActivityManagerService outside of the lock, to prevent
// deadlock with WindowManagerService.
final boolean hasFGS = mActivityManagerInternal.hasRunningForegroundService(
uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
synchronized (mLock) { synchronized (mLock) {
if (isCurrentProjection(asBinder())) { if (isCurrentProjection(asBinder())) {
Slog.w(TAG, "UID " + Binder.getCallingUid() Slog.w(TAG, "UID " + Binder.getCallingUid()
@@ -935,9 +947,7 @@ public final class MediaProjectionManagerService extends SystemService
} }
if (REQUIRE_FG_SERVICE_FOR_PROJECTION if (REQUIRE_FG_SERVICE_FOR_PROJECTION
&& requiresForegroundService() && requiresForegroundService() && !hasFGS) {
&& !mActivityManagerInternal.hasRunningForegroundService(
uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)) {
throw new SecurityException("Media projections require a foreground service" throw new SecurityException("Media projections require a foreground service"
+ " of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION"); + " of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION");
} }
@@ -1026,10 +1036,11 @@ public final class MediaProjectionManagerService extends SystemService
mToken = null; mToken = null;
unregisterCallback(mCallback); unregisterCallback(mCallback);
mCallback = null; mCallback = null;
// TODO(b/267740338): Add unit test.
mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED);
} }
// Run on a separate thread, to ensure no lock is held when calling into
// ActivityManagerService.
mHandler.post(() -> mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED));
} }
@Override // Binder call @Override // Binder call

View File

@@ -11,6 +11,11 @@ Calls must follow the below invocation order while holding locks:
`WindowManagerService -> MediaProjectionManagerService -> DisplayManagerService` `WindowManagerService -> MediaProjectionManagerService -> DisplayManagerService`
`MediaProjectionManagerService` should never lock when calling into a service that may acquire
the `WindowManagerService` global lock (for example,
`MediaProjectionManagerService -> ActivityManagerService` may result in deadlock, since
`ActivityManagerService -> WindowManagerService`).
### Justification ### Justification
`MediaProjectionManagerService` calls into `WindowManagerService` in the below cases. While handling `MediaProjectionManagerService` calls into `WindowManagerService` in the below cases. While handling