diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl index b136d5bc4db3f..2bdd5c8bc977d 100644 --- a/media/java/android/media/projection/IMediaProjection.aidl +++ b/media/java/android/media/projection/IMediaProjection.aidl @@ -17,7 +17,7 @@ package android.media.projection; import android.media.projection.IMediaProjectionCallback; -import android.window.WindowContainerToken; +import android.os.IBinder; /** {@hide} */ interface IMediaProjection { @@ -31,14 +31,14 @@ interface IMediaProjection { void unregisterCallback(IMediaProjectionCallback callback); /** - * Returns the {@link android.window.WindowContainerToken} identifying the task to record, or - * {@code null} if there is none. + * Returns the {@link android.os.IBinder} identifying the task to record, or {@code null} if + * there is none. */ - WindowContainerToken getTaskRecordingWindowContainerToken(); + IBinder getLaunchCookie(); /** - * Updates the {@link android.window.WindowContainerToken} identifying the task to record, or - * {@code null} if there is none. + * Updates the {@link android.os.IBinder} identifying the task to record, or {@code null} if + * there is none. */ - void setTaskRecordingWindowContainerToken(in WindowContainerToken token); + void setLaunchCookie(in IBinder launchCookie); } diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java index ba7bf3f5f5d32..ae44fc575f7ce 100644 --- a/media/java/android/media/projection/MediaProjection.java +++ b/media/java/android/media/projection/MediaProjection.java @@ -25,13 +25,13 @@ import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.hardware.display.VirtualDisplayConfig; import android.os.Handler; +import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.util.ArrayMap; import android.util.Log; import android.view.ContentRecordingSession; import android.view.Surface; -import android.window.WindowContainerToken; import java.util.Map; @@ -172,18 +172,16 @@ public final class MediaProjection { @NonNull VirtualDisplayConfig.Builder virtualDisplayConfig, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { try { - final WindowContainerToken taskWindowContainerToken = - mImpl.getTaskRecordingWindowContainerToken(); + final IBinder launchCookie = mImpl.getLaunchCookie(); Context windowContext = null; ContentRecordingSession session; - if (taskWindowContainerToken == null) { + if (launchCookie == null) { windowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(), TYPE_APPLICATION, null /* options */); session = ContentRecordingSession.createDisplaySession( windowContext.getWindowContextToken()); } else { - session = ContentRecordingSession.createTaskSession( - taskWindowContainerToken.asBinder()); + session = ContentRecordingSession.createTaskSession(launchCookie); } virtualDisplayConfig.setWindowManagerMirroring(true); final DisplayManager dm = mContext.getSystemService(DisplayManager.class); diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index 098e8f74749ca..7d12ede754ef2 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -46,7 +46,6 @@ import android.os.UserHandle; import android.util.ArrayMap; import android.util.Slog; import android.view.ContentRecordingSession; -import android.window.WindowContainerToken; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; @@ -433,7 +432,7 @@ public final class MediaProjectionManagerService extends SystemService private IBinder mToken; private IBinder.DeathRecipient mDeathEater; private boolean mRestoreSystemAlertWindow; - private WindowContainerToken mTaskRecordingWindowContainerToken = null; + private IBinder mLaunchCookie = null; MediaProjection(int type, int uid, String packageName, int targetSdkVersion, boolean isPrivileged) { @@ -609,14 +608,13 @@ public final class MediaProjectionManagerService extends SystemService } @Override // Binder call - public void setTaskRecordingWindowContainerToken(WindowContainerToken token) { - // TODO(b/221417940) set the task id to record from sysui, for the package chosen. - mTaskRecordingWindowContainerToken = token; + public void setLaunchCookie(IBinder launchCookie) { + mLaunchCookie = launchCookie; } @Override // Binder call - public WindowContainerToken getTaskRecordingWindowContainerToken() { - return mTaskRecordingWindowContainerToken; + public IBinder getLaunchCookie() { + return mLaunchCookie; } public MediaProjectionInfo getProjectionInfo() { diff --git a/services/core/java/com/android/server/wm/ContentRecordingController.java b/services/core/java/com/android/server/wm/ContentRecordingController.java index fca4942d4b798..fff7637acc7ea 100644 --- a/services/core/java/com/android/server/wm/ContentRecordingController.java +++ b/services/core/java/com/android/server/wm/ContentRecordingController.java @@ -63,6 +63,7 @@ final class ContentRecordingController { */ void setContentRecordingSessionLocked(@Nullable ContentRecordingSession incomingSession, @NonNull WindowManagerService wmService) { + // TODO(b/219761722) handle a null session arriving due to task setup failing if (incomingSession != null && (!ContentRecordingSession.isValid(incomingSession) || ContentRecordingSession.isSameDisplay(mSession, incomingSession))) { // Ignore an invalid session, or a session for the same display as currently recording. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 92091bc747662..b5abd3258ad8a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -48,6 +48,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR; import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH; +import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; @@ -288,6 +289,7 @@ import android.view.displayhash.VerifiedDisplayHash; import android.window.ClientWindowFrames; import android.window.ITaskFpsCallback; import android.window.TaskSnapshot; +import android.window.WindowContainerToken; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -8281,6 +8283,26 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setContentRecordingSession(@Nullable ContentRecordingSession incomingSession) { synchronized (mGlobalLock) { + // Allow the controller to handle teardown or a non-task session. + if (incomingSession == null + || incomingSession.getContentToRecord() != RECORD_CONTENT_TASK) { + mContentRecordingController.setContentRecordingSessionLocked(incomingSession, + WindowManagerService.this); + return; + } + // For a task session, find the activity identified by the launch cookie. + final WindowContainerToken wct = getTaskWindowContainerTokenForLaunchCookie( + incomingSession.getTokenToRecord()); + if (wct == null) { + Slog.w(TAG, "Handling a new recording session; unable to find the " + + "WindowContainerToken"); + mContentRecordingController.setContentRecordingSessionLocked(null, + WindowManagerService.this); + return; + } + // Replace the launch cookie in the session details with the task's + // WindowContainerToken. + incomingSession.setTokenToRecord(wct.asBinder()); mContentRecordingController.setContentRecordingSessionLocked(incomingSession, WindowManagerService.this); } @@ -8538,6 +8560,38 @@ public class WindowManagerService extends IWindowManager.Stub mAtmService.setFocusedTask(task.mTaskId, touchedActivity); } + /** + * Retrieve the {@link WindowContainerToken} of the task that contains the activity started + * with the given launch cookie. + * + * @param launchCookie the launch cookie set on the {@link ActivityOptions} when starting an + * activity + * @return a token representing the task containing the activity started with the given launch + * cookie, or {@code null} if the token couldn't be found. + */ + @VisibleForTesting + @Nullable + WindowContainerToken getTaskWindowContainerTokenForLaunchCookie(@NonNull IBinder launchCookie) { + // Find the activity identified by the launch cookie. + final ActivityRecord targetActivity = mRoot.getActivity( + activity -> activity.mLaunchCookie == launchCookie); + if (targetActivity == null) { + Slog.w(TAG, "Unable to find the activity for this launch cookie"); + return null; + } + if (targetActivity.getTask() == null) { + Slog.w(TAG, "Unable to find the task for this launch cookie"); + return null; + } + WindowContainerToken taskWindowContainerToken = + targetActivity.getTask().mRemoteToken.toWindowContainerToken(); + if (taskWindowContainerToken == null) { + Slog.w(TAG, "Unable to find the WindowContainerToken for " + targetActivity.getName()); + return null; + } + return taskWindowContainerToken; + } + /** * You need ALLOW_SLIPPERY_TOUCHES permission to be able to set FLAG_SLIPPERY. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index e09a94f3bcf9e..1a64f5e3a356b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -61,6 +61,7 @@ import android.view.InsetsState; import android.view.InsetsVisibilities; import android.view.View; import android.view.WindowManager; +import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; @@ -317,4 +318,76 @@ public class WindowManagerServiceTests extends WindowTestsBase { verify(mWm.mInputManager).setInTouchMode( !currentTouchMode, callingPid, callingUid, /* hasPermission= */ false); } + + @Test + public void testGetTaskWindowContainerTokenForLaunchCookie_nullCookie() { + WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(null); + assertThat(wct).isNull(); + } + + @Test + public void testGetTaskWindowContainerTokenForLaunchCookie_invalidCookie() { + Binder cookie = new Binder("test cookie"); + WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie); + assertThat(wct).isNull(); + + final ActivityRecord testActivity = new ActivityBuilder(mAtm) + .setCreateTask(true) + .build(); + + wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie); + assertThat(wct).isNull(); + } + + @Test + public void testGetTaskWindowContainerTokenForLaunchCookie_validCookie() { + final Binder cookie = new Binder("ginger cookie"); + final WindowContainerToken launchRootTask = mock(WindowContainerToken.class); + setupActivityWithLaunchCookie(cookie, launchRootTask); + + WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie); + assertThat(wct).isEqualTo(launchRootTask); + } + + @Test + public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies() { + final Binder cookie1 = new Binder("ginger cookie"); + final WindowContainerToken launchRootTask1 = mock(WindowContainerToken.class); + setupActivityWithLaunchCookie(cookie1, launchRootTask1); + + setupActivityWithLaunchCookie(new Binder("choc chip cookie"), + mock(WindowContainerToken.class)); + + setupActivityWithLaunchCookie(new Binder("peanut butter cookie"), + mock(WindowContainerToken.class)); + + WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie1); + assertThat(wct).isEqualTo(launchRootTask1); + } + + @Test + public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies_noneValid() { + setupActivityWithLaunchCookie(new Binder("ginger cookie"), + mock(WindowContainerToken.class)); + + setupActivityWithLaunchCookie(new Binder("choc chip cookie"), + mock(WindowContainerToken.class)); + + setupActivityWithLaunchCookie(new Binder("peanut butter cookie"), + mock(WindowContainerToken.class)); + + WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie( + new Binder("some other cookie")); + assertThat(wct).isNull(); + } + + private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) { + final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class); + when(remoteToken.toWindowContainerToken()).thenReturn(wct); + final ActivityRecord testActivity = new ActivityBuilder(mAtm) + .setCreateTask(true) + .build(); + testActivity.mLaunchCookie = launchCookie; + testActivity.getTask().mRemoteToken = remoteToken; + } }