Make setRecentsScreenshotEnabled public

Enable setRecentsScreenshotEnabled as a public API to allow apps to
disable the screenshot that is shown in recents.

There's no need for a getter method because it's only set by the app so
it will not be changed by system

Test: SnapshotTaskTests
Fixes: 166729178
Change-Id: I06f1e4fa62ad50c050b6259e04a4af27cd25be67
This commit is contained in:
chaviw 2022-01-25 18:05:16 -06:00
parent 083297ec6b
commit 62df8ad093
14 changed files with 124 additions and 37 deletions

View File

@ -4221,6 +4221,7 @@ package android.app {
method @Deprecated public final void setProgressBarIndeterminate(boolean);
method @Deprecated public final void setProgressBarIndeterminateVisibility(boolean);
method @Deprecated public final void setProgressBarVisibility(boolean);
method public void setRecentsScreenshotEnabled(boolean);
method public void setRequestedOrientation(int);
method public final void setResult(int);
method public final void setResult(int, android.content.Intent);

View File

@ -2850,6 +2850,7 @@ package android.view {
method public default void setShouldShowSystemDecors(int, boolean);
method public default void setShouldShowWithInsecureKeyguard(int, boolean);
method public default boolean shouldShowSystemDecors(int);
method @Nullable public default android.graphics.Bitmap snapshotTaskForRecents(@IntRange(from=0) int);
field public static final int DISPLAY_IME_POLICY_FALLBACK_DISPLAY = 1; // 0x1
field public static final int DISPLAY_IME_POLICY_HIDE = 2; // 0x2
field public static final int DISPLAY_IME_POLICY_LOCAL = 0; // 0x0

View File

@ -8517,8 +8517,18 @@ public class Activity extends ContextThemeWrapper
}
/**
* If set to true, this indicates to the system that it should never take a
* screenshot of the activity to be used as a representation while it is not in a started state.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.S,
publicAlternatives = "Use {@link #setRecentsScreenshotEnabled(boolean)} instead.")
public void setDisablePreviewScreenshots(boolean disable) {
setRecentsScreenshotEnabled(!disable);
}
/**
* If set to false, this indicates to the system that it should never take a
* screenshot of the activity to be used as a representation in recents screen. By default, this
* value is {@code true}.
* <p>
* Note that the system may use the window background of the theme instead to represent
* the window when it is not running.
@ -8531,12 +8541,10 @@ public class Activity extends ContextThemeWrapper
* {@link android.service.voice.VoiceInteractionService} requests a screenshot via
* {@link android.service.voice.VoiceInteractionSession#SHOW_WITH_SCREENSHOT}.
*
* @param disable {@code true} to disable preview screenshots; {@code false} otherwise.
* @hide
* @param enabled {@code true} to enable recents screenshots; {@code false} otherwise.
*/
@UnsupportedAppUsage
public void setDisablePreviewScreenshots(boolean disable) {
ActivityClient.getInstance().setDisablePreviewScreenshots(mToken, disable);
public void setRecentsScreenshotEnabled(boolean enabled) {
ActivityClient.getInstance().setRecentsScreenshotEnabled(mToken, enabled);
}
/**

View File

@ -438,9 +438,9 @@ public class ActivityClient {
}
}
void setDisablePreviewScreenshots(IBinder token, boolean disable) {
void setRecentsScreenshotEnabled(IBinder token, boolean enabled) {
try {
getActivityClientController().setDisablePreviewScreenshots(token, disable);
getActivityClientController().setRecentsScreenshotEnabled(token, enabled);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}

View File

@ -115,8 +115,8 @@ interface IActivityClientController {
int enterAnim, int exitAnim, int backgroundColor);
int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
/** See {@link android.app.Activity#setDisablePreviewScreenshots}. */
oneway void setDisablePreviewScreenshots(in IBinder token, boolean disable);
/** See {@link android.app.Activity#setRecentsScreenshotEnabled}. */
oneway void setRecentsScreenshotEnabled(in IBinder token, boolean enabled);
/**
* It should only be called from home activity to remove its outdated snapshot. The home

View File

@ -941,4 +941,14 @@ interface IWindowManager
* @hide
*/
void unregisterTaskFpsCallback(in IOnFpsCallbackListener listener);
/**
* Take a snapshot using the same path that's used for Recents. This is used for Testing only.
*
* @param taskId to take the snapshot of
*
* Returns a bitmap of the screenshot or {@code null} if it was unable to screenshot.
* @hide
*/
Bitmap snapshotTaskForRecents(int taskId);
}

View File

@ -97,6 +97,7 @@ import android.content.ClipData;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Point;
@ -4886,4 +4887,21 @@ public interface WindowManager extends ViewManager {
*/
@SystemApi
default void unregisterTaskFpsCallback(@NonNull TaskFpsCallback callback) {}
/**
* Take a snapshot using the same path that's used for Recents. This is used for Testing only.
*
* @param taskId to take the snapshot of
*
* @return a bitmap of the screenshot or {@code null} if it was unable to screenshot. The
* screenshot can fail if the taskId is invalid or if there's no SurfaceControl associated with
* that task.
*
* @hide
*/
@TestApi
@Nullable
default Bitmap snapshotTaskForRecents(@IntRange(from = 0) int taskId) {
return null;
}
}

View File

@ -32,6 +32,7 @@ import android.app.ResourcesManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
@ -439,4 +440,14 @@ public final class WindowManagerImpl implements WindowManager {
} catch (RemoteException e) {
}
}
@Override
public Bitmap snapshotTaskForRecents(int taskId) {
try {
return WindowManagerGlobal.getWindowManagerService().snapshotTaskForRecents(taskId);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
return null;
}
}

View File

@ -1135,13 +1135,13 @@ class ActivityClientController extends IActivityClientController.Stub {
}
@Override
public void setDisablePreviewScreenshots(IBinder token, boolean disable) {
public void setRecentsScreenshotEnabled(IBinder token, boolean enabled) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
if (r != null) {
r.setDisablePreviewScreenshots(disable);
r.setRecentsScreenshotEnabled(enabled);
}
}
} finally {

View File

@ -752,7 +752,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Last visibility state we reported to the app token.
boolean reportedVisible;
boolean mDisablePreviewScreenshots;
boolean mEnablePreviewScreenshots = true;
// Information about an application starting window if displayed.
// Note: these are de-referenced before the starting window animates away.
@ -5100,22 +5100,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
* See {@link Activity#setDisablePreviewScreenshots}.
* See {@link Activity#setRecentsScreenshotEnabled}.
*/
void setDisablePreviewScreenshots(boolean disable) {
mDisablePreviewScreenshots = disable;
void setRecentsScreenshotEnabled(boolean enabled) {
mEnablePreviewScreenshots = enabled;
}
/**
* Retrieves whether we'd like to generate a snapshot that's based solely on the theme. This is
* the case when preview screenshots are disabled {@link #setDisablePreviewScreenshots} or when
* the case when preview screenshots are disabled {@link #setRecentsScreenshotEnabled} or when
* we can't take a snapshot for other reasons, for example, if we have a secure window.
*
* @return True if we need to generate an app theme snapshot, false if we'd like to take a real
* screenshot.
*/
boolean shouldUseAppThemeSnapshot() {
return mDisablePreviewScreenshots || forAllWindows(WindowState::isSecureLocked,
return !mEnablePreviewScreenshots || forAllWindows(WindowState::isSecureLocked,
true /* topToBottom */);
}

View File

@ -196,15 +196,21 @@ class TaskSnapshotController {
snapshotTasks(tasks, false /* allowSnapshotHome */);
}
void recordTaskSnapshot(Task task, boolean allowSnapshotHome) {
/**
* This is different than {@link #recordTaskSnapshot(Task, boolean)} because it doesn't store
* the snapshot to the cache and returns the TaskSnapshot immediately.
*
* This is only used for testing so the snapshot content can be verified.
*/
@VisibleForTesting
TaskSnapshot captureTaskSnapshot(Task task, boolean snapshotHome) {
final TaskSnapshot snapshot;
final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome();
if (snapshotHome) {
snapshot = snapshotTask(task);
} else {
switch (getSnapshotMode(task)) {
case SNAPSHOT_MODE_NONE:
return;
return null;
case SNAPSHOT_MODE_APP_THEME:
snapshot = drawAppThemeSnapshot(task);
break;
@ -216,19 +222,27 @@ class TaskSnapshotController {
break;
}
}
if (snapshot != null) {
final HardwareBuffer buffer = snapshot.getHardwareBuffer();
if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
buffer.close();
Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
+ buffer.getHeight());
} else {
mCache.putSnapshot(task, snapshot);
// Don't persist or notify the change for the temporal snapshot.
if (!snapshotHome) {
mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
task.onSnapshotChanged(snapshot);
}
return snapshot;
}
void recordTaskSnapshot(Task task, boolean allowSnapshotHome) {
final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome();
final TaskSnapshot snapshot = captureTaskSnapshot(task, snapshotHome);
if (snapshot == null) {
return;
}
final HardwareBuffer buffer = snapshot.getHardwareBuffer();
if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
buffer.close();
Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
+ buffer.getHeight());
} else {
mCache.putSnapshot(task, snapshot);
// Don't persist or notify the change for the temporal snapshot.
if (!snapshotHome) {
mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
task.onSnapshotChanged(snapshot);
}
}
}

View File

@ -114,6 +114,7 @@ import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON
import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
@ -8861,4 +8862,27 @@ public class WindowManagerService extends IWindowManager.Stub
mTaskFpsCallbackController.unregisterCallback(listener);
}
@Override
public Bitmap snapshotTaskForRecents(int taskId) {
if (!checkCallingPermission(READ_FRAME_BUFFER, "snapshotTaskForRecents()")) {
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
}
TaskSnapshot taskSnapshot;
synchronized (mGlobalLock) {
Task task = mRoot.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (task == null) {
throw new IllegalArgumentException(
"Failed to find matching task for taskId=" + taskId);
}
taskSnapshot = mTaskSnapshotController.captureTaskSnapshot(task, false);
}
if (taskSnapshot == null || taskSnapshot.getHardwareBuffer() == null) {
return null;
}
return Bitmap.wrapHardwareBuffer(taskSnapshot.getHardwareBuffer(),
taskSnapshot.getColorSpace());
}
}

View File

@ -116,7 +116,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
public void testGetSnapshotMode() {
final WindowState disabledWindow = createWindow(null,
FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow");
disabledWindow.mActivityRecord.setDisablePreviewScreenshots(true);
disabledWindow.mActivityRecord.setRecentsScreenshotEnabled(false);
assertEquals(SNAPSHOT_MODE_APP_THEME,
mWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask()));

View File

@ -30,7 +30,7 @@ public class DisableScreenshotsActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setDisablePreviewScreenshots(true);
setRecentsScreenshotEnabled(false);
getWindow().getDecorView().setBackgroundColor(Color.RED);
}