Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 090f31e3 authored by Manjeet Rulhania's avatar Manjeet Rulhania Committed by Android (Google) Code Review
Browse files

Merge "Expose binder for sensitive content protection" into main

parents 543c7873 cf105e4e
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -6558,6 +6558,13 @@ public abstract class Context {
    @SystemApi
    public static final String ECM_ENHANCED_CONFIRMATION_SERVICE = "ecm_enhanced_confirmation";

    /**
     * Service to protect sensitive content during screen share.
     * @hide
     */
    public static final String SENSITIVE_CONTENT_PROTECTION_SERVICE =
            "sensitive_content_protection_service";

    /**
     * Use with {@link #getSystemService(String)} to retrieve a
     * {@link android.provider.ContactKeysManager} to managing contact keys.
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 android.view;

import android.os.IBinder;

/**
 * @hide
 */
oneway interface ISensitiveContentProtectionManager {
    /**
     * Block projection for a package's window when the window is showing sensitive content on
     * the screen, the projection is unblocked when the window no more shows sensitive content.
     *
     * @param windowToken window where the content is shown.
     * @param packageName package name.
     * @param isShowingSensitiveContent whether the window is showing sensitive content.
     */
    void setSensitiveContentProtection(in IBinder windowToken, in String packageName,
            in boolean isShowingSensitiveContent);
}
+91 −13
Original line number Diff line number Diff line
@@ -18,14 +18,17 @@ package com.android.server;

import static android.permission.flags.Flags.sensitiveNotificationAppProtection;
import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS;
import static com.android.internal.util.Preconditions.checkNotNull;
import static android.view.flags.Flags.sensitiveContentAppProtection;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
@@ -35,29 +38,30 @@ import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
import android.view.ISensitiveContentProtectionManager;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.SensitiveContentPackages.PackageInfo;
import com.android.server.wm.WindowManagerInternal;

import java.util.Objects;
import java.util.Set;

/**
 * Service that monitors for notifications with sensitive content and protects content from screen
 * sharing
 * This service protects sensitive content from screen sharing. The service monitors notifications
 * for sensitive content and protects from screen share. The service also protects sensitive
 * content rendered on screen during screen share.
 */
public final class SensitiveContentProtectionManagerService extends SystemService {
    private static final String TAG = "SensitiveContentProtect";
    private static final boolean DEBUG = false;
    private static final boolean sNotificationProtectionEnabled =
            sensitiveNotificationAppProtection();

    @VisibleForTesting
    @Nullable
    NotificationListener mNotificationListener;
    private @Nullable MediaProjectionManager mProjectionManager;
    private @Nullable WindowManagerInternal mWindowManager;
    @Nullable private MediaProjectionManager mProjectionManager;
    @Nullable private WindowManagerInternal mWindowManager;

    final Object mSensitiveContentProtectionLock = new Object();

@@ -92,7 +96,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic

    public SensitiveContentProtectionManagerService(@NonNull Context context) {
        super(context);
        if (sNotificationProtectionEnabled) {
        if (sensitiveNotificationAppProtection()) {
            mNotificationListener = new NotificationListener();
        }
    }
@@ -110,14 +114,18 @@ public final class SensitiveContentProtectionManagerService extends SystemServic

        init(getContext().getSystemService(MediaProjectionManager.class),
                LocalServices.getService(WindowManagerInternal.class));
        if (sensitiveContentAppProtection()) {
            publishBinderService(Context.SENSITIVE_CONTENT_PROTECTION_SERVICE,
                    new SensitiveContentProtectionManagerServiceBinder());
        }
    }

    @VisibleForTesting
    void init(MediaProjectionManager projectionManager, WindowManagerInternal windowManager) {
        if (DEBUG) Log.d(TAG, "init");

        checkNotNull(projectionManager, "Failed to get valid MediaProjectionManager");
        checkNotNull(windowManager, "Failed to get valid WindowManagerInternal");
        Objects.requireNonNull(projectionManager);
        Objects.requireNonNull(windowManager);

        mProjectionManager = projectionManager;
        mWindowManager = windowManager;
@@ -126,7 +134,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
        //  handler, delegate, and binder death recipient
        mProjectionManager.addCallback(mProjectionCallback, getContext().getMainThreadHandler());

        if (sNotificationProtectionEnabled) {
        if (sensitiveNotificationAppProtection()) {
            try {
                mNotificationListener.registerAsSystemService(
                        getContext(),
@@ -144,7 +152,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
        if (mProjectionManager != null) {
            mProjectionManager.removeCallback(mProjectionCallback);
        }
        if (sNotificationProtectionEnabled) {
        if (sensitiveNotificationAppProtection()) {
            try {
                mNotificationListener.unregisterAsSystemService();
            } catch (RemoteException e) {
@@ -169,7 +177,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic

        synchronized (mSensitiveContentProtectionLock) {
            mProjectionActive = true;
            if (sNotificationProtectionEnabled) {
            if (sensitiveNotificationAppProtection()) {
                updateAppsThatShouldBlockScreenCapture();
            }
        }
@@ -305,4 +313,74 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
            }
        }
    }

    /**
     * Block projection for a package window when the window is showing sensitive content on
     * the screen, the projection is unblocked when window no more shows sensitive content.
     *
     * @param windowToken window where the content is shown.
     * @param packageName package name.
     * @param uid uid of the package.
     * @param isShowingSensitiveContent whether the window is showing sensitive content.
     */
    @VisibleForTesting
    void setSensitiveContentProtection(IBinder windowToken, String packageName, int uid,
            boolean isShowingSensitiveContent) {
        synchronized (mSensitiveContentProtectionLock) {
            if (!mProjectionActive) {
                return;
            }
            if (DEBUG) {
                Log.d(TAG, "setSensitiveContentProtection - windowToken=" + windowToken
                        + ", package=" + packageName + ", uid=" + uid
                        + ", isShowingSensitiveContent=" + isShowingSensitiveContent);
            }

            // The window token distinguish this package from packages added for notifications.
            PackageInfo packageInfo = new PackageInfo(packageName, uid, windowToken);
            ArraySet<PackageInfo> packageInfos = new ArraySet<>();
            packageInfos.add(packageInfo);
            if (isShowingSensitiveContent) {
                mWindowManager.addBlockScreenCaptureForApps(packageInfos);
            } else {
                mWindowManager.removeBlockScreenCaptureForApps(packageInfos);
            }
        }
    }

    private final class SensitiveContentProtectionManagerServiceBinder
            extends ISensitiveContentProtectionManager.Stub {
        private final PackageManagerInternal mPackageManagerInternal;

        SensitiveContentProtectionManagerServiceBinder() {
            mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
        }

        public void setSensitiveContentProtection(IBinder windowToken, String packageName,
                boolean isShowingSensitiveContent) {
            Trace.beginSection(
                    "SensitiveContentProtectionManagerService.setSensitiveContentProtection");
            try {
                int callingUid = Binder.getCallingUid();
                verifyCallingPackage(callingUid, packageName);
                final long identity = Binder.clearCallingIdentity();
                try {
                    SensitiveContentProtectionManagerService.this.setSensitiveContentProtection(
                            windowToken, packageName, callingUid, isShowingSensitiveContent);
                } finally {
                    Binder.restoreCallingIdentity(identity);
                }
            } finally {
                Trace.endSection();
            }
        }

        private void verifyCallingPackage(int callingUid, String callingPackage) {
            if (mPackageManagerInternal.getPackageUid(
                    callingPackage, 0, UserHandle.getUserId(callingUid)) != callingUid) {
                throw new SecurityException("Specified calling package [" + callingPackage
                        + "] does not match the calling uid " + callingUid);
            }
        }
    }
}
+68 −10
Original line number Diff line number Diff line
@@ -16,9 +16,16 @@

package com.android.server.wm;

import static android.permission.flags.Flags.sensitiveNotificationAppProtection;
import static android.view.flags.Flags.sensitiveContentAppProtection;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
import android.util.ArraySet;

import com.android.internal.annotations.VisibleForTesting;

import java.io.PrintWriter;
import java.util.Objects;

@@ -29,14 +36,28 @@ import java.util.Objects;
public class SensitiveContentPackages {
    private final ArraySet<PackageInfo> mProtectedPackages = new ArraySet<>();

    /** Returns {@code true} if package/uid pair should be blocked from screen capture */
    public boolean shouldBlockScreenCaptureForApp(String pkg, int uid) {
    /**
     * Returns {@code true} if package/uid/window combination should be blocked
     * from screen capture.
     */
    public boolean shouldBlockScreenCaptureForApp(String pkg, int uid, IBinder windowToken) {
        if (!(sensitiveContentAppProtection() || sensitiveNotificationAppProtection())) {
            return false;
        }

        for (int i = 0; i < mProtectedPackages.size(); i++) {
            PackageInfo info = mProtectedPackages.valueAt(i);
            if (info != null && info.mPkg.equals(pkg) && info.mUid == uid) {
                // sensitiveContentAppProtection blocks specific window where sensitive content
                // is rendered, whereas sensitiveNotificationAppProtection blocks the package
                // if the package has a sensitive notification.
                if ((sensitiveContentAppProtection() && windowToken == info.getWindowToken())
                        || (sensitiveNotificationAppProtection() && info.getWindowToken() == null)
                ) {
                    return true;
                }
            }
        }
        return false;
    }

@@ -73,31 +94,50 @@ public class SensitiveContentPackages {
     */
    public boolean clearBlockedApps() {
        if (mProtectedPackages.isEmpty()) {
            // set was already empty
            return false;
        }
        mProtectedPackages.clear();
        return true;
    }

    /**
     * @return the size of protected packages.
     */
    @VisibleForTesting
    public int size() {
        return mProtectedPackages.size();
    }

    void dump(PrintWriter pw) {
        final String innerPrefix = "  ";
        pw.println("SensitiveContentPackages:");
        pw.println(innerPrefix + "Packages that should block screen capture ("
                + mProtectedPackages.size() + "):");
        for (PackageInfo info : mProtectedPackages) {
            pw.println(innerPrefix + "  package=" + info.mPkg + "  uid=" + info.mUid);
            pw.println(innerPrefix + "  package=" + info.mPkg + "  uid=" + info.mUid
                    + " windowToken=" + info.mWindowToken);
        }
    }

    /** Helper class that represents a package/uid pair */
    /**
     * Helper class that represents a package, uid, and window token combination, window token
     * is set to block screen capture at window level.
     */
    public static class PackageInfo {
        private String mPkg;
        private int mUid;
        private final String mPkg;
        private final int mUid;

        @Nullable
        private final IBinder mWindowToken;

        public PackageInfo(String pkg, int uid) {
            this(pkg, uid, null);
        }

        public PackageInfo(String pkg, int uid, IBinder windowToken) {
            this.mPkg = pkg;
            this.mUid = uid;
            this.mWindowToken = windowToken;
        }

        @Override
@@ -105,12 +145,30 @@ public class SensitiveContentPackages {
            if (this == o) return true;
            if (!(o instanceof PackageInfo)) return false;
            PackageInfo that = (PackageInfo) o;
            return mUid == that.mUid && Objects.equals(mPkg, that.mPkg);
            return mUid == that.mUid && Objects.equals(mPkg, that.mPkg)
                    && Objects.equals(mWindowToken, that.mWindowToken);
        }

        @Override
        public int hashCode() {
            return Objects.hash(mPkg, mUid);
            return Objects.hash(mPkg, mUid, mWindowToken);
        }

        public IBinder getWindowToken() {
            return mWindowToken;
        }

        public int getUid() {
            return mUid;
        }

        public String getPkg() {
            return mPkg;
        }

        @Override
        public String toString() {
            return "package=" + mPkg + "  uid=" + mUid + " windowToken=" + mWindowToken;
        }
    }
}
+4 −5
Original line number Diff line number Diff line
@@ -1897,12 +1897,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
            return true;
        }

        if (android.permission.flags.Flags.sensitiveNotificationAppProtection()) {
            if (mWmService.mSensitiveContentPackages
                    .shouldBlockScreenCaptureForApp(getOwningPackage(), getOwningUid())) {
        // block screen capture to protect sensitive notifications or content on the screen.
        if (mWmService.mSensitiveContentPackages.shouldBlockScreenCaptureForApp(
                getOwningPackage(), getOwningUid(), getWindowToken())) {
            return true;
        }
        }

        return !DevicePolicyCache.getInstance().isScreenCaptureAllowed(mShowUserId);
    }
Loading