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