Merge "Add TestAPI to replace content on a display." into main

This commit is contained in:
Chavi Weingarten
2023-07-13 18:06:04 +00:00
committed by Android (Google) Code Review
11 changed files with 212 additions and 11 deletions

View File

@@ -3622,6 +3622,8 @@ package android.view {
method public default void holdLock(android.os.IBinder, int);
method public default boolean isGlobalKey(int);
method public default boolean isTaskSnapshotSupported();
method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithMirror(int, @NonNull android.view.Window);
method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithSc(int, @NonNull android.view.SurfaceControl);
method public default void setDisplayImePolicy(int, int);
method public default void setShouldShowSystemDecors(int, boolean);
method public default void setShouldShowWithInsecureKeyguard(int, boolean);

View File

@@ -1044,4 +1044,13 @@ interface IWindowManager
* @return List of ComponentNames corresponding to the activities that were notified.
*/
List<ComponentName> notifyScreenshotListeners(int displayId);
/**
* Replace the content of the displayId with the SurfaceControl passed in. This can be used for
* tests when creating a VirtualDisplay, but only want to capture specific content and not
* mirror the entire display.
*/
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.ACCESS_SURFACE_FLINGER)")
boolean replaceContentOnDisplay(int displayId, in SurfaceControl sc);
}

View File

@@ -5701,4 +5701,35 @@ public interface WindowManager extends ViewManager {
default @NonNull List<ComponentName> notifyScreenshotListeners(int displayId) {
throw new UnsupportedOperationException();
}
/**
* @param displayId The displayId to that should have its content replaced.
* @param window The window that should get mirrored and the mirrored content rendered on
* displayId passed in.
*
* @return Whether it successfully created a mirror SurfaceControl and replaced the display
* content with the mirror of the Window.
*
* @hide
*/
@TestApi
@RequiresPermission(permission.ACCESS_SURFACE_FLINGER)
default boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) {
throw new UnsupportedOperationException();
}
/**
* @param displayId The displayId to that should have its content replaced.
* @param sc The SurfaceControl that should get rendered onto the displayId passed in.
*
* @return Whether it successfully created a mirror SurfaceControl and replaced the display
* content with the mirror of the Window.
*
* @hide
*/
@TestApi
@RequiresPermission(permission.ACCESS_SURFACE_FLINGER)
default boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) {
throw new UnsupportedOperationException();
}
}

View File

@@ -34,6 +34,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.StrictMode;
import android.util.Log;
import android.window.ITaskFpsCallback;
import android.window.TaskFpsCallback;
import android.window.WindowContext;
@@ -80,6 +81,8 @@ import java.util.function.IntConsumer;
* @hide
*/
public final class WindowManagerImpl implements WindowManager {
private static final String TAG = "WindowManager";
@UnsupportedAppUsage
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@UiContext
@@ -467,4 +470,42 @@ public final class WindowManagerImpl implements WindowManager {
throw e.rethrowFromSystemServer();
}
}
@Override
public boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) {
View decorView = window.peekDecorView();
if (decorView == null) {
Log.e(TAG, "replaceContentOnDisplayWithMirror: Window's decorView was null.");
return false;
}
ViewRootImpl viewRoot = decorView.getViewRootImpl();
if (viewRoot == null) {
Log.e(TAG, "replaceContentOnDisplayWithMirror: Window's viewRootImpl was null.");
return false;
}
SurfaceControl sc = viewRoot.getSurfaceControl();
if (!sc.isValid()) {
Log.e(TAG, "replaceContentOnDisplayWithMirror: Window's SC is invalid.");
return false;
}
return replaceContentOnDisplayWithSc(displayId, SurfaceControl.mirrorSurface(sc));
}
@Override
public boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) {
if (!sc.isValid()) {
Log.e(TAG, "replaceContentOnDisplayWithSc: Invalid SC.");
return false;
}
try {
return WindowManagerGlobal.getWindowManagerService()
.replaceContentOnDisplay(displayId, sc);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
return false;
}
}

View File

@@ -7032,4 +7032,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Display is the root, so it's not rotated relative to anything.
return Surface.ROTATION_0;
}
public void replaceContent(SurfaceControl sc) {
new Transaction().reparent(sc, getSurfaceControl())
.reparent(mWindowingLayer, null)
.reparent(mOverlayLayer, null)
.reparent(mA11yOverlayLayer, null)
.apply();
}
}

View File

@@ -16,6 +16,7 @@
package com.android.server.wm;
import static android.Manifest.permission.ACCESS_SURFACE_FLINGER;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.INPUT_CONSUMER;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
@@ -24,7 +25,6 @@ import static android.Manifest.permission.MANAGE_APP_TOKENS;
import static android.Manifest.permission.MODIFY_TOUCH_MODE_STATE;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.STATUS_BAR_SERVICE;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
@@ -9552,4 +9552,23 @@ public class WindowManagerService extends IWindowManager.Stub
return List.copyOf(notifiedApps);
}
}
@RequiresPermission(ACCESS_SURFACE_FLINGER)
@Override
public boolean replaceContentOnDisplay(int displayId, SurfaceControl sc) {
if (!checkCallingPermission(ACCESS_SURFACE_FLINGER,
"replaceDisplayContent()")) {
throw new SecurityException("Requires ACCESS_SURFACE_FLINGER permission");
}
DisplayContent dc;
synchronized (mGlobalLock) {
dc = mRoot.getDisplayContentOrCreate(displayId);
if (dc == null) {
return false;
}
dc.replaceContent(sc);
return true;
}
}
}

View File

@@ -109,10 +109,6 @@
</intent-filter>
</activity>
<service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
android:foregroundServiceType="mediaProjection"
android:enabled="true">
</service>
</application>
<instrumentation

View File

@@ -18,7 +18,6 @@ package com.android.server.wm;
import static android.server.wm.UiDeviceUtils.pressUnlockButton;
import static android.server.wm.UiDeviceUtils.pressWakeupButton;
import static android.server.wm.WindowManagerState.getLogicalDisplaySize;
import android.app.KeyguardManager;
import android.os.PowerManager;
@@ -46,7 +45,6 @@ public class SurfaceViewSyncContinuousTest {
@Before
public void setup() {
mCapturedActivity = mActivityRule.getActivity();
mCapturedActivity.setLogicalDisplaySize(getLogicalDisplaySize());
final KeyguardManager km = mCapturedActivity.getSystemService(KeyguardManager.class);
if (km != null && km.isKeyguardLocked() || !Objects.requireNonNull(

View File

@@ -43,7 +43,7 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
<service android:name="com.android.test.LocalMediaProjectionService"
android:foregroundServiceType="mediaProjection"
android:enabled="true">
</service>

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
public class LocalMediaProjectionService extends Service {
private Bitmap mTestBitmap;
private static final String NOTIFICATION_CHANNEL_ID = "Surfacevalidator";
private static final String CHANNEL_NAME = "ProjectionService";
static final int MSG_START_FOREGROUND_DONE = 1;
static final String EXTRA_MESSENGER = "messenger";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(intent);
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
if (mTestBitmap != null) {
mTestBitmap.recycle();
mTestBitmap = null;
}
super.onDestroy();
}
private Icon createNotificationIcon() {
mTestBitmap = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(mTestBitmap);
canvas.drawColor(Color.BLUE);
return Icon.createWithBitmap(mTestBitmap);
}
private void startForeground(Intent intent) {
final NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
final NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
final Notification.Builder notificationBuilder =
new Notification.Builder(this, NOTIFICATION_CHANNEL_ID);
final Notification notification = notificationBuilder.setOngoing(true)
.setContentTitle("App is running")
.setSmallIcon(createNotificationIcon())
.setCategory(Notification.CATEGORY_SERVICE)
.setContentText("Context")
.build();
startForeground(2, notification);
final Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
final Message msg = Message.obtain();
msg.what = MSG_START_FOREGROUND_DONE;
try {
messenger.send(msg);
} catch (RemoteException e) {
}
}
}

View File

@@ -19,7 +19,6 @@ import android.annotation.ColorInt
import android.content.Context
import android.content.Intent
import android.graphics.Rect
import android.server.wm.WindowManagerState.getLogicalDisplaySize
import android.view.cts.surfacevalidator.CapturedActivity
import android.view.cts.surfacevalidator.ISurfaceValidatorTestCase
import android.view.cts.surfacevalidator.PixelChecker
@@ -44,8 +43,6 @@ open class ScreenRecordTestBase(useBlastAdapter: Boolean) :
mActivity = mActivityRule.launchActivity(Intent())
lateinit var surfaceReadyLatch: CountDownLatch
runOnUiThread {
it.dismissPermissionDialog()
it.setLogicalDisplaySize(getLogicalDisplaySize())
surfaceReadyLatch = it.addSurfaceView(defaultBufferSize)
}
surfaceReadyLatch.await()