Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions core/java/android/content/pm/GosPackageStateFlag.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ public interface GosPackageStateFlag {
/** @hide */ int ALLOW_CLIPBOARD_READ_NON_DEFAULT = 31;
/** @hide */ int ALLOW_CLIPBOARD_READ = 32;
/** @hide */ int SUPPRESS_CLIPBOARD_ACCESS_DENIAL_NOTIF = 33;
/** @hide */ int BLOCK_SCREENSHOT_DETECTION = 34;
/** @hide */ int BLOCK_SCREEN_RECORDING_DETECTION = 35;
/** @hide */ int ALLOW_SCREENSHOTS_NON_DEFAULT = 36;
/** @hide */ int ALLOW_SCREENSHOTS = 37;
/** @hide */ int ALLOW_SCREEN_RECORDING_NON_DEFAULT = 38;
/** @hide */ int ALLOW_SCREEN_RECORDING = 39;

/** @hide */
@IntDef(value = {
Expand Down Expand Up @@ -74,6 +80,12 @@ public interface GosPackageStateFlag {
ALLOW_CLIPBOARD_READ_NON_DEFAULT,
ALLOW_CLIPBOARD_READ,
SUPPRESS_CLIPBOARD_ACCESS_DENIAL_NOTIF,
BLOCK_SCREENSHOT_DETECTION,
BLOCK_SCREEN_RECORDING_DETECTION,
ALLOW_SCREENSHOTS_NON_DEFAULT,
ALLOW_SCREENSHOTS,
ALLOW_SCREEN_RECORDING_NON_DEFAULT,
ALLOW_SCREEN_RECORDING,
})
@Retention(RetentionPolicy.SOURCE)
@interface Enum {}
Expand Down
5 changes: 5 additions & 0 deletions core/java/android/ext/settings/app/AppSwitch.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ protected final boolean getNonDefaultValue(GosPackageState ps) {
return gosPsFlagInverted ? !value : value;
}

// returns null if default value is used, for switches whose default isn't a fixed value
public final Boolean getNonDefaultValueOrNull(GosPackageState ps) {
return isUsingDefaultValue(ps) ? null : getNonDefaultValue(ps);
}

public final void setUseDefaultValue(GosPackageState.Editor ed) {
ed.clearFlag(gosPsFlagNonDefault);
ed.clearFlag(gosPsFlag);
Expand Down
35 changes: 35 additions & 0 deletions core/java/android/ext/settings/app/AswAllowScreenRecording.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package android.ext.settings.app;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.GosPackageState;
import android.content.pm.GosPackageStateFlag;

/**
* Per-app override of whether screen recording and screen sharing (MediaProjection) may capture
* the app's windows.
* <p>
* Default: the app decides, i.e. windows with FLAG_SECURE are redacted.<br>
* On: windows with FLAG_SECURE are captured while a MediaProjection session is active. Recents
* thumbnails and long screenshots stay blocked.<br>
* Off: the app's windows are redacted while a MediaProjection session is active.
* <p>
* Use {@link #getNonDefaultValueOrNull(GosPackageState)} to check whether there's an override.
*
* @hide
*/
public class AswAllowScreenRecording extends AppSwitch {
public static final AswAllowScreenRecording I = new AswAllowScreenRecording();

private AswAllowScreenRecording() {
gosPsFlagNonDefault = GosPackageStateFlag.ALLOW_SCREEN_RECORDING_NON_DEFAULT;
gosPsFlag = GosPackageStateFlag.ALLOW_SCREEN_RECORDING;
}

@Override
protected boolean getDefaultValueInner(Context ctx, int userId, ApplicationInfo appInfo,
GosPackageState ps, StateInfo si) {
// screen recording is allowed unless the app opts out via FLAG_SECURE
return true;
}
}
34 changes: 34 additions & 0 deletions core/java/android/ext/settings/app/AswAllowScreenshots.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package android.ext.settings.app;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.GosPackageState;
import android.content.pm.GosPackageStateFlag;

/**
* Per-app override of whether user-initiated screenshots may capture the app's windows.
* <p>
* Default: the app decides, i.e. windows with FLAG_SECURE are redacted.<br>
* On: windows with FLAG_SECURE are captured. Other FLAG_SECURE properties (hidden recents
* thumbnail, no mirroring to non-secure displays, screen recording) are unaffected.<br>
* Off: the app's windows are excluded from screenshots and its recents thumbnail is hidden.
* <p>
* Use {@link #getNonDefaultValueOrNull(GosPackageState)} to check whether there's an override.
*
* @hide
*/
public class AswAllowScreenshots extends AppSwitch {
public static final AswAllowScreenshots I = new AswAllowScreenshots();

private AswAllowScreenshots() {
gosPsFlagNonDefault = GosPackageStateFlag.ALLOW_SCREENSHOTS_NON_DEFAULT;
gosPsFlag = GosPackageStateFlag.ALLOW_SCREENSHOTS;
}

@Override
protected boolean getDefaultValueInner(Context ctx, int userId, ApplicationInfo appInfo,
GosPackageState ps, StateInfo si) {
// screenshots are allowed unless the app opts out via FLAG_SECURE
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package android.ext.settings.app;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.GosPackageState;
import android.content.pm.GosPackageStateFlag;

/**
* When on, callbacks registered via WindowManager#addScreenRecordingCallback always report that
* the app isn't being recorded.
*
* @hide
*/
public class AswBlockScreenRecordingDetection extends AppSwitch {
public static final AswBlockScreenRecordingDetection I = new AswBlockScreenRecordingDetection();

private AswBlockScreenRecordingDetection() {
gosPsFlag = GosPackageStateFlag.BLOCK_SCREEN_RECORDING_DETECTION;
}

@Override
protected boolean getDefaultValueInner(Context ctx, int userId, ApplicationInfo appInfo,
GosPackageState ps, StateInfo si) {
return false;
}

public boolean get(GosPackageState ps) {
return getNonDefaultValue(ps);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package android.ext.settings.app;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.GosPackageState;
import android.content.pm.GosPackageStateFlag;

/**
* When on, the app isn't notified about screenshots via Activity.ScreenCaptureCallback and isn't
* listed in the "app detected this screenshot" notice. The callback API keeps working, it just
* never fires.
*
* @hide
*/
public class AswBlockScreenshotDetection extends AppSwitch {
public static final AswBlockScreenshotDetection I = new AswBlockScreenshotDetection();

private AswBlockScreenshotDetection() {
gosPsFlag = GosPackageStateFlag.BLOCK_SCREENSHOT_DETECTION;
}

@Override
protected boolean getDefaultValueInner(Context ctx, int userId, ApplicationInfo appInfo,
GosPackageState ps, StateInfo si) {
return false;
}

public boolean get(GosPackageState ps) {
return getNonDefaultValue(ps);
}
}
6 changes: 6 additions & 0 deletions core/java/android/window/ScreenCaptureInternal.java
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,12 @@ private CaptureArgs(Parcel in) {
mExclusionMask = in.readInt();
}

/** Layers set using {@link Builder#setExcludeLayers(SurfaceControl[])}, if any. */
@Nullable
public SurfaceControl[] getExcludeLayers() {
return mExcludeLayers;
}

/** Release any layers if set using {@link Builder#setExcludeLayers(SurfaceControl[])}. */
public void release() {
if (mExcludeLayers == null || mExcludeLayers.length == 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.ext.settings.app.AswAllowScreenRecording;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.os.Binder;
Expand All @@ -55,6 +56,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.wm.SensitiveContentPackages.PackageInfo;
import com.android.server.wm.WindowManagerInternal;

Expand Down Expand Up @@ -317,6 +319,9 @@ private ArraySet<String> getExemptedPackages() {
}

private void onProjectionStart(MediaProjectionInfo projectionInfo) {
// the user's per-app choices apply regardless of the exemptions below
updateScreenRecordingOverrides();

boolean isPackageExempted = (mExemptedPackages != null && mExemptedPackages.contains(
projectionInfo.getPackageName()))
|| canRecordSensitiveContent(projectionInfo.getPackageName())
Expand Down Expand Up @@ -377,9 +382,35 @@ private void onProjectionEnd() {
// notify windowmanager to clear any sensitive notifications observed during projection
// session
mWindowManager.clearBlockedApps();
mWindowManager.setScreenRecordingOverrides(new ArraySet<>(), new ArraySet<>());
}
}

// shortcut: overrides are computed once per projection session, changes made during the
// session apply to the next one
private void updateScreenRecordingOverrides() {
final ArraySet<PackageInfo> blocked = new ArraySet<>();
final ArraySet<PackageInfo> allowed = new ArraySet<>();
mPackageManagerInternal.forEachPackageState(ps -> {
final var userStates = ps.getUserStates();
for (int i = 0, n = userStates.size(); i < n; ++i) {
final PackageUserStateInternal userState = userStates.valueAt(i);
if (!userState.isInstalled()) {
continue;
}
final Boolean allow = AswAllowScreenRecording.I.getNonDefaultValueOrNull(
userState.getGosPackageState());
if (allow == null) {
continue;
}
final var info = new PackageInfo(ps.getPackageName(),
UserHandle.getUid(userStates.keyAt(i), ps.getAppId()));
(allow ? allowed : blocked).add(info);
}
});
mWindowManager.setScreenRecordingOverrides(blocked, allowed);
}

@GuardedBy("mSensitiveContentProtectionLock")
private void updateAppsThatShouldBlockScreenCapture() {
RankingMap rankingMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,16 @@
import static android.content.pm.GosPackageStateFlag.ALLOW_ACCESS_TO_OBB_DIRECTORY;
import static android.content.pm.GosPackageStateFlag.ALLOW_CLIPBOARD_READ;
import static android.content.pm.GosPackageStateFlag.ALLOW_CLIPBOARD_READ_NON_DEFAULT;
import static android.content.pm.GosPackageStateFlag.ALLOW_SCREENSHOTS;
import static android.content.pm.GosPackageStateFlag.ALLOW_SCREENSHOTS_NON_DEFAULT;
import static android.content.pm.GosPackageStateFlag.ALLOW_SCREEN_RECORDING;
import static android.content.pm.GosPackageStateFlag.ALLOW_SCREEN_RECORDING_NON_DEFAULT;
import static android.content.pm.GosPackageStateFlag.BLOCK_NATIVE_DEBUGGING;
import static android.content.pm.GosPackageStateFlag.BLOCK_NATIVE_DEBUGGING_NON_DEFAULT;
import static android.content.pm.GosPackageStateFlag.BLOCK_NATIVE_DEBUGGING_SUPPRESS_NOTIF;
import static android.content.pm.GosPackageStateFlag.BLOCK_PLAY_INTEGRITY_API;
import static android.content.pm.GosPackageStateFlag.BLOCK_SCREENSHOT_DETECTION;
import static android.content.pm.GosPackageStateFlag.BLOCK_SCREEN_RECORDING_DETECTION;
import static android.content.pm.GosPackageStateFlag.CONTACT_SCOPES_ENABLED;
import static android.content.pm.GosPackageStateFlag.ENABLE_EXPLOIT_PROTECTION_COMPAT_MODE;
import static android.content.pm.GosPackageStateFlag.FORCE_MEMTAG;
Expand Down Expand Up @@ -151,6 +157,12 @@ static void init(PackageManagerService pm) {
ALLOW_CLIPBOARD_READ_NON_DEFAULT,
ALLOW_CLIPBOARD_READ,
SUPPRESS_CLIPBOARD_ACCESS_DENIAL_NOTIF,
BLOCK_SCREENSHOT_DETECTION,
BLOCK_SCREEN_RECORDING_DETECTION,
ALLOW_SCREENSHOTS_NON_DEFAULT,
ALLOW_SCREENSHOTS,
ALLOW_SCREEN_RECORDING_NON_DEFAULT,
ALLOW_SCREEN_RECORDING,
};
builder()
.readWriteFlags(settingsReadWriteFlags)
Expand Down
4 changes: 3 additions & 1 deletion services/core/java/com/android/server/wm/ActivityRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -5723,7 +5723,9 @@ void setRecentsScreenshotEnabled(boolean enabled) {
*/
boolean shouldUseAppThemeSnapshot() {
return !mEnableRecentsScreenshot || forAllWindows(WindowState::isSecureLocked,
true /* topToBottom */);
true /* topToBottom */)
// also covers screenshots that are taken from task snapshots
|| ScreenCaptureAppPolicy.areScreenshotsBlocked(this);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion services/core/java/com/android/server/wm/DisplayContent.java
Original file line number Diff line number Diff line change
Expand Up @@ -7266,7 +7266,8 @@ public boolean test(WindowState nextWindow) {
return false; /* continue */
}
}
if (nextWindow.isSecureLocked()) {
if (nextWindow.isSecureLocked()
|| ScreenCaptureAppPolicy.areScreenshotsBlocked(nextWindow)) {
return false; /* continue */
}
return true; /* stop, match found */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ void dispatchConfigurationToChild(DisplayContent child, Configuration config) {

void refreshSecureSurfaceState() {
forAllWindows(w -> {
w.setSecureLocked(w.isSecureLocked());
w.setSecureLocked(w.isSurfaceSecureLocked());
}, true /* traverseTopToBottom */);
forAllTasks(t -> {
t.setSecure(t.isSecure());
Expand Down
Loading