1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.permission.flags.Flags.sensitiveNotificationAppProtection;
20 import static android.view.flags.Flags.sensitiveContentAppProtection;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.os.IBinder;
25 import android.util.ArraySet;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 
29 import java.io.PrintWriter;
30 import java.util.Objects;
31 
32 /**
33  * Cache of distinct package/uid pairs that require being blocked from screen capture. This class is
34  * not threadsafe and any call site should hold {@link WindowManagerGlobalLock}
35  */
36 public class SensitiveContentPackages {
37     private final ArraySet<PackageInfo> mProtectedPackages = new ArraySet<>();
38 
39     /**
40      * Returns {@code true} if package/uid/window combination should be blocked
41      * from screen capture.
42      */
shouldBlockScreenCaptureForApp(String pkg, int uid, IBinder windowToken)43     public boolean shouldBlockScreenCaptureForApp(String pkg, int uid, IBinder windowToken) {
44         if (!(sensitiveContentAppProtection() || sensitiveNotificationAppProtection())) {
45             return false;
46         }
47 
48         for (int i = 0; i < mProtectedPackages.size(); i++) {
49             PackageInfo info = mProtectedPackages.valueAt(i);
50             if (info != null && info.mPkg.equals(pkg) && info.mUid == uid) {
51                 // sensitiveContentAppProtection blocks specific window where sensitive content
52                 // is rendered, whereas sensitiveNotificationAppProtection blocks the package
53                 // if the package has a sensitive notification.
54                 if ((sensitiveContentAppProtection() && windowToken == info.getWindowToken())
55                         || (sensitiveNotificationAppProtection() && info.getWindowToken() == null)
56                 ) {
57                     return true;
58                 }
59             }
60         }
61         return false;
62     }
63 
64     /**
65      * Adds the set of package/uid pairs to set that should be blocked from screen capture
66      *
67      * @param packageInfos packages to be blocked
68      * @return {@code true} if packages set is modified, {@code false} otherwise.
69      */
addBlockScreenCaptureForApps(@onNull ArraySet<PackageInfo> packageInfos)70     public boolean addBlockScreenCaptureForApps(@NonNull ArraySet<PackageInfo> packageInfos) {
71         if (mProtectedPackages.equals(packageInfos)) {
72             // new set is equal to current set of packages, no need to update
73             return false;
74         }
75         mProtectedPackages.addAll(packageInfos);
76         return true;
77     }
78 
79     /**
80      * Clears apps added to collection of apps in which screen capture should be disabled.
81      *
82      * @param packageInfos set of {@link PackageInfo} whose windows should be unblocked
83      *                     from capture.
84      * @return {@code true} if packages set is modified, {@code false} otherwise.
85      */
removeBlockScreenCaptureForApps(@onNull ArraySet<PackageInfo> packageInfos)86     public boolean removeBlockScreenCaptureForApps(@NonNull ArraySet<PackageInfo> packageInfos) {
87         return mProtectedPackages.removeAll(packageInfos);
88     }
89 
90     /**
91      * Clears the set of package/uid pairs that should be blocked from screen capture
92      *
93      * @return {@code true} if packages set is modified, {@code false} otherwise.
94      */
clearBlockedApps()95     public boolean clearBlockedApps() {
96         if (mProtectedPackages.isEmpty()) {
97             return false;
98         }
99         mProtectedPackages.clear();
100         return true;
101     }
102 
103     /**
104      * @return the size of protected packages.
105      */
106     @VisibleForTesting
size()107     public int size() {
108         return mProtectedPackages.size();
109     }
110 
dump(PrintWriter pw)111     void dump(PrintWriter pw) {
112         final String innerPrefix = "  ";
113         pw.println("SensitiveContentPackages:");
114         pw.println(innerPrefix + "Packages that should block screen capture ("
115                 + mProtectedPackages.size() + "):");
116         for (PackageInfo info : mProtectedPackages) {
117             pw.println(innerPrefix + "  package=" + info.mPkg + "  uid=" + info.mUid
118                     + " windowToken=" + info.mWindowToken);
119         }
120     }
121 
122     /**
123      * Helper class that represents a package, uid, and window token combination, window token
124      * is set to block screen capture at window level.
125      */
126     public static class PackageInfo {
127         private final String mPkg;
128         private final int mUid;
129 
130         @Nullable
131         private final IBinder mWindowToken;
132 
PackageInfo(String pkg, int uid)133         public PackageInfo(String pkg, int uid) {
134             this(pkg, uid, null);
135         }
136 
PackageInfo(String pkg, int uid, IBinder windowToken)137         public PackageInfo(String pkg, int uid, IBinder windowToken) {
138             this.mPkg = pkg;
139             this.mUid = uid;
140             this.mWindowToken = windowToken;
141         }
142 
143         @Override
equals(Object o)144         public boolean equals(Object o) {
145             if (this == o) return true;
146             if (!(o instanceof PackageInfo)) return false;
147             PackageInfo that = (PackageInfo) o;
148             return mUid == that.mUid && Objects.equals(mPkg, that.mPkg)
149                     && Objects.equals(mWindowToken, that.mWindowToken);
150         }
151 
152         @Override
hashCode()153         public int hashCode() {
154             return Objects.hash(mPkg, mUid, mWindowToken);
155         }
156 
getWindowToken()157         public IBinder getWindowToken() {
158             return mWindowToken;
159         }
160 
getUid()161         public int getUid() {
162             return mUid;
163         }
164 
getPkg()165         public String getPkg() {
166             return mPkg;
167         }
168 
169         @Override
toString()170         public String toString() {
171             return "package=" + mPkg + "  uid=" + mUid + " windowToken=" + mWindowToken;
172         }
173     }
174 }
175