diff options
12 files changed, 141 insertions, 7 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 173aaf3db262..445ae14e3f9a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3859,6 +3859,7 @@ package android.app { method public void setContentView(android.view.View); method public void setContentView(android.view.View, android.view.ViewGroup.LayoutParams); method public final void setDefaultKeyMode(int); + method public void setDisablePreviewScreenshots(boolean); method public void setEnterSharedElementCallback(android.app.SharedElementCallback); method public void setExitSharedElementCallback(android.app.SharedElementCallback); method public final void setFeatureDrawable(int, android.graphics.drawable.Drawable); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 07540f3bfcea..e1498683c968 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -7257,6 +7257,33 @@ public class Activity extends ContextThemeWrapper mAutoFillResetNeeded = true; } + /** + * 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. + * <p> + * Note that the system may use the window background of the theme instead to represent + * the window when it is not running. + * <p> + * Also note that in comparison to {@link android.view.WindowManager.LayoutParams#FLAG_SECURE}, + * this only affects the behavior when the activity's screenshot would be used as a + * representation when the activity is not in a started state, i.e. in Overview. The system may + * still take screenshots of the activity in other contexts; for example, when the user takes a + * screenshot of the entire screen, or when the active + * {@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 + */ + @SystemApi + public void setDisablePreviewScreenshots(boolean disable) { + try { + ActivityManager.getService().setDisablePreviewScreenshots(mToken, disable); + } catch (RemoteException e) { + Log.e(TAG, "Failed to call setDisablePreviewScreenshots", e); + } + } + class HostCallbacks extends FragmentHostCallback<Activity> { public HostCallbacks() { super(Activity.this /*activity*/); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index d9408574b2a5..b08ca4fadf0d 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -612,6 +612,11 @@ interface IActivityManager { void waitForNetworkStateUpdate(long procStateSeq); + /** + * See {@link android.app.Activity#setDisablePreviewScreenshots} + */ + void setDisablePreviewScreenshots(IBinder token, boolean disable); + // WARNING: when these transactions are updated, check if they are any callers on the native // side. If so, make sure they are using the correct transaction ids and arguments. // If a transaction which will also be used on the native side is being inserted, add it diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 18befef4772a..28214465a745 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -128,7 +128,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.FORCE_NEW_TASK_FLAGS; import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED; import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME; -import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS; import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY; import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; import static com.android.server.am.ActivityStackSupervisor.ON_TOP; @@ -149,8 +148,6 @@ import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; -import static java.lang.Integer.MAX_VALUE; - import android.Manifest; import android.Manifest.permission; import android.annotation.NonNull; @@ -10826,6 +10823,25 @@ public class ActivityManagerService extends IActivityManager.Stub } } + @Override + public void setDisablePreviewScreenshots(IBinder token, boolean disable) + throws RemoteException { + synchronized (this) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + Slog.w(TAG, "setDisablePreviewScreenshots: Unable to find activity for token=" + + token); + return; + } + final long origId = Binder.clearCallingIdentity(); + try { + r.setDisablePreviewScreenshots(disable); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + // ========================================================= // CONTENT PROVIDERS // ========================================================= diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 7868fdfd864a..ecaa7517a5db 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -1973,6 +1973,10 @@ final class ActivityRecord implements AppWindowContainerListener { task.taskId, requestedOrientation); } + void setDisablePreviewScreenshots(boolean disable) { + mWindowContainerController.setDisablePreviewScreenshots(disable); + } + /** * Set the last reported global configuration to the client. Should be called whenever a new * global configuration is sent to the client for this activity. diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index bd38be4c090c..ef3d87c9ae97 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -35,7 +35,6 @@ import android.graphics.Bitmap; import android.os.Debug; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.Trace; import android.util.Slog; import android.view.IApplicationToken; @@ -228,7 +227,7 @@ public class AppWindowContainerController boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, int configChanges, boolean launchTaskBehind, boolean alwaysFocusable, AppWindowContainerController controller) { - return new AppWindowToken(service, token, voiceInteraction, dc, + return new AppWindowToken(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, controller); @@ -298,6 +297,17 @@ public class AppWindowContainerController } } + public void setDisablePreviewScreenshots(boolean disable) { + synchronized (mWindowMap) { + if (mContainer == null) { + Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app" + + " token: " + mToken); + return; + } + mContainer.setDisablePreviewSnapshots(disable); + } + } + public void setVisibility(boolean visible) { synchronized(mWindowMap) { if (mContainer == null) { diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index c20ee973bac5..27e9deaa45ce 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -166,6 +166,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>(); ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>(); + private boolean mDisbalePreviewScreenshots; + AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, @@ -1433,6 +1435,14 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return candidate; } + void setDisablePreviewSnapshots(boolean disable) { + mDisbalePreviewScreenshots = disable; + } + + boolean shouldDisablePreviewScreenshots() { + return mDisbalePreviewScreenshots; + } + @Override int getAnimLayerAdjustment() { return mAppAnimator.animLayerAdjustment; diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 469a8a710f07..4ae6dbe21721 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -152,8 +152,13 @@ class TaskSnapshotController { } } - private boolean canSnapshotTask(Task task) { - return !StackId.isHomeOrRecentsStack(task.mStack.mStackId); + @VisibleForTesting + boolean canSnapshotTask(Task task) { + // TODO: Figure out what happens when snapshots are disabled. Can we draw a splash screen + // instead? + final AppWindowToken topChild = task.getTopChild(); + return !StackId.isHomeOrRecentsStack(task.mStack.mStackId) + && topChild != null && !topChild.shouldDisablePreviewScreenshots(); } /** diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java index 58d277b274e8..45a799948801 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -19,6 +19,8 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static com.android.server.wm.AppTransition.TRANSIT_UNSET; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; @@ -68,4 +70,15 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks); assertEquals(0, closingTasks.size()); } + + @Test + public void testSnapshotsDisabled() throws Exception { + final WindowState disabledWindow = createWindow(null, + FIRST_APPLICATION_WINDOW, sDisplayContent, "disabledWindow"); + disabledWindow.mAppToken.setDisablePreviewSnapshots(true); + assertFalse(sWm.mTaskSnapshotController.canSnapshotTask(disabledWindow.getTask())); + final WindowState normalWindow = createWindow(null, + FIRST_APPLICATION_WINDOW, sDisplayContent, "normalWindow"); + assertTrue(sWm.mTaskSnapshotController.canSnapshotTask(normalWindow.getTask())); + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index 65efd9cd23ae..96481dd83b55 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -42,6 +42,7 @@ import static android.app.AppOpsManager.OP_NONE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.EMPTY; +import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml index 7b9c9f1325fd..4dd51dcc7d09 100644 --- a/tests/ActivityTests/AndroidManifest.xml +++ b/tests/ActivityTests/AndroidManifest.xml @@ -79,5 +79,12 @@ android:singleUser="true" android:exported="true" /> <receiver android:name="TrackTimeReceiver" /> <receiver android:name="AlarmSpamReceiver" /> + <activity android:name="DisableScreenshotsActivity" + android:label="DisableScreenshots"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> </application> </manifest> diff --git a/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java b/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java new file mode 100644 index 000000000000..de24ca2ac9e8 --- /dev/null +++ b/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 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.google.android.test.activity; + +import android.annotation.Nullable; +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; + +/** + * Activity for which screenshotting is disabled. + */ +public class DisableScreenshotsActivity extends Activity { + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setDisablePreviewScreenshots(true); + getWindow().getDecorView().setBackgroundColor(Color.RED); + } +} |