Merge "Add API to disable snapshotting of activities"
diff --git a/api/system-current.txt b/api/system-current.txt
index 875b8c7..eef7839 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3861,6 +3861,7 @@
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 07540f3..e149868 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7257,6 +7257,33 @@
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 d940857..b08ca4f 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -612,6 +612,11 @@
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 05a59d3..2be5e77 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.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 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;
@@ -10851,6 +10848,25 @@
}
}
+ @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 b9bb106..6cea483 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 @@
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 bd38be4..ef3d87c 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.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 @@
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 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 a474316..32cd7d8 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -167,6 +167,8 @@
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,
@@ -1428,6 +1430,14 @@
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 469a8a7..4ae6dbe 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 @@
}
}
- 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 58d277b..45a7999 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 @@
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 @@
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 ce632ae..6f78245 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.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 7b9c9f1..4dd51dc 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 0000000..de24ca2
--- /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);
+ }
+}