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;
18 
19 import static android.permission.flags.Flags.sensitiveContentImprovements;
20 import static android.permission.flags.Flags.sensitiveContentMetricsBugfix;
21 import static android.permission.flags.Flags.sensitiveNotificationAppProtection;
22 import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS;
23 import static android.view.flags.Flags.sensitiveContentAppProtection;
24 
25 import static com.android.internal.util.FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION;
26 import static com.android.internal.util.FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__FRAMEWORKS;
27 import static com.android.internal.util.FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START;
28 import static com.android.internal.util.FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP;
29 import static com.android.internal.util.FrameworkStatsLog.SENSITIVE_NOTIFICATION_APP_PROTECTION_SESSION;
30 import static com.android.server.wm.WindowManagerInternal.OnWindowRemovedListener;
31 
32 import android.annotation.NonNull;
33 import android.annotation.Nullable;
34 import android.content.ComponentName;
35 import android.content.Context;
36 import android.content.pm.PackageManager;
37 import android.content.pm.PackageManagerInternal;
38 import android.media.projection.MediaProjectionInfo;
39 import android.media.projection.MediaProjectionManager;
40 import android.os.Binder;
41 import android.os.IBinder;
42 import android.os.RemoteException;
43 import android.os.Trace;
44 import android.os.UserHandle;
45 import android.provider.Settings;
46 import android.service.notification.NotificationListenerService;
47 import android.service.notification.NotificationListenerService.RankingMap;
48 import android.service.notification.StatusBarNotification;
49 import android.util.ArraySet;
50 import android.util.Log;
51 import android.view.ISensitiveContentProtectionManager;
52 
53 import com.android.internal.annotations.GuardedBy;
54 import com.android.internal.annotations.VisibleForTesting;
55 import com.android.internal.util.FrameworkStatsLog;
56 import com.android.server.wm.SensitiveContentPackages.PackageInfo;
57 import com.android.server.wm.WindowManagerInternal;
58 
59 import java.util.Objects;
60 import java.util.Random;
61 import java.util.Set;
62 
63 /**
64  * This service protects sensitive content from screen sharing. The service monitors notifications
65  * for sensitive content and protects from screen share. The service also protects sensitive
66  * content rendered on screen during screen share.
67  */
68 public final class SensitiveContentProtectionManagerService extends SystemService {
69     private static final String TAG = "SensitiveContentProtect";
70     private static final boolean DEBUG = false;
71 
72     @VisibleForTesting
73     @Nullable
74     NotificationListener mNotificationListener;
75     @Nullable
76     private MediaProjectionManager mProjectionManager;
77 
78     @GuardedBy("mSensitiveContentProtectionLock")
79     @Nullable
80     private MediaProjectionSession mMediaProjectionSession;
81 
82     private PackageManagerInternal mPackageManagerInternal;
83 
84     @Nullable
85     private WindowManagerInternal mWindowManager;
86 
87     // screen recorder packages exempted from screen share protection.
88     private ArraySet<String> mExemptedPackages = null;
89 
90     final Object mSensitiveContentProtectionLock = new Object();
91 
92     private final ArraySet<PackageInfo> mPackagesShowingSensitiveContent = new ArraySet<>();
93 
94     @GuardedBy("mSensitiveContentProtectionLock")
95     private boolean mProjectionActive = false;
96 
97     private static class MediaProjectionSession {
98         private final int mUid; // UID of app that started projection session
99         private final long mSessionId;
100         private final boolean mIsExempted;
101         private final ArraySet<String> mAllSeenNotificationKeys = new ArraySet<>();
102         private final ArraySet<String> mSeenOtpNotificationKeys = new ArraySet<>();
103 
MediaProjectionSession(int uid, boolean isExempted, long sessionId)104         MediaProjectionSession(int uid, boolean isExempted, long sessionId) {
105             mUid = uid;
106             mIsExempted = isExempted;
107             mSessionId = sessionId;
108         }
109 
logProjectionSessionStart()110         public void logProjectionSessionStart() {
111             FrameworkStatsLog.write(
112                     SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION,
113                     mSessionId,
114                     mUid,
115                     mIsExempted,
116                     SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START,
117                     SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__FRAMEWORKS
118             );
119         }
120 
logProjectionSessionStop()121         public void logProjectionSessionStop() {
122             FrameworkStatsLog.write(
123                     SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION,
124                     mSessionId,
125                     mUid,
126                     mIsExempted,
127                     SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP,
128                     SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__FRAMEWORKS
129             );
130         }
131 
logAppNotificationsProtected()132         public void logAppNotificationsProtected() {
133             FrameworkStatsLog.write(
134                     SENSITIVE_NOTIFICATION_APP_PROTECTION_SESSION,
135                     mSessionId,
136                     mAllSeenNotificationKeys.size(),
137                     mSeenOtpNotificationKeys.size());
138         }
139 
logAppBlocked(int uid)140         public void logAppBlocked(int uid) {
141             FrameworkStatsLog.write(
142                     FrameworkStatsLog.SENSITIVE_CONTENT_APP_PROTECTION,
143                     mSessionId,
144                     uid,
145                     mUid,
146                     FrameworkStatsLog.SENSITIVE_CONTENT_APP_PROTECTION__STATE__BLOCKED
147             );
148         }
149 
logAppUnblocked(int uid)150         public void logAppUnblocked(int uid) {
151             FrameworkStatsLog.write(
152                     FrameworkStatsLog.SENSITIVE_CONTENT_APP_PROTECTION,
153                     mSessionId,
154                     uid,
155                     mUid,
156                     FrameworkStatsLog.SENSITIVE_CONTENT_APP_PROTECTION__STATE__UNBLOCKED
157             );
158         }
159 
addSeenNotificationKey(String key)160         private void addSeenNotificationKey(String key) {
161             mAllSeenNotificationKeys.add(key);
162         }
163 
addSeenOtpNotificationKey(String key)164         private void addSeenOtpNotificationKey(String key) {
165             mAllSeenNotificationKeys.add(key);
166             mSeenOtpNotificationKeys.add(key);
167         }
168 
addSeenNotifications( @onNull StatusBarNotification[] notifications, @NonNull RankingMap rankingMap)169         public void addSeenNotifications(
170                 @NonNull StatusBarNotification[] notifications,
171                 @NonNull RankingMap rankingMap) {
172             for (StatusBarNotification sbn : notifications) {
173                 if (sbn == null) {
174                     Log.w(TAG, "Unable to parse null notification");
175                     continue;
176                 }
177 
178                 if (notificationHasSensitiveContent(sbn, rankingMap)) {
179                     addSeenOtpNotificationKey(sbn.getKey());
180                 } else {
181                     addSeenNotificationKey(sbn.getKey());
182                 }
183             }
184         }
185     }
186 
187     private final MediaProjectionManager.Callback mProjectionCallback =
188             new MediaProjectionManager.Callback() {
189                 @Override
190                 public void onStart(MediaProjectionInfo info) {
191                     if (DEBUG) Log.d(TAG, "onStart projection: " + info);
192                     Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
193                             "SensitiveContentProtectionManagerService.onProjectionStart");
194                     try {
195                         onProjectionStart(info);
196                     } finally {
197                         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
198                     }
199                 }
200 
201                 @Override
202                 public void onStop(MediaProjectionInfo info) {
203                     if (DEBUG) Log.d(TAG, "onStop projection: " + info);
204                     Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
205                             "SensitiveContentProtectionManagerService.onProjectionStop");
206                     try {
207                         onProjectionEnd();
208                     } finally {
209                         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
210                     }
211                 }
212             };
213 
214     private final OnWindowRemovedListener mOnWindowRemovedListener = token -> {
215         synchronized (mSensitiveContentProtectionLock) {
216             mPackagesShowingSensitiveContent.removeIf(pkgInfo -> pkgInfo.getWindowToken() == token);
217         }
218     };
219 
SensitiveContentProtectionManagerService(@onNull Context context)220     public SensitiveContentProtectionManagerService(@NonNull Context context) {
221         super(context);
222         if (sensitiveNotificationAppProtection()) {
223             mNotificationListener = new NotificationListener();
224         }
225     }
226 
227     @Override
onStart()228     public void onStart() {}
229 
230     @Override
onBootPhase(int phase)231     public void onBootPhase(int phase) {
232         if (phase != SystemService.PHASE_BOOT_COMPLETED) {
233             return;
234         }
235 
236         if (DEBUG) Log.d(TAG, "onBootPhase - PHASE_BOOT_COMPLETED");
237         init(getContext().getSystemService(MediaProjectionManager.class),
238                 LocalServices.getService(WindowManagerInternal.class),
239                 LocalServices.getService(PackageManagerInternal.class),
240                 getExemptedPackages()
241         );
242         if (sensitiveContentAppProtection()) {
243             publishBinderService(Context.SENSITIVE_CONTENT_PROTECTION_SERVICE,
244                     new SensitiveContentProtectionManagerServiceBinder());
245         }
246     }
247 
248     @VisibleForTesting
init(MediaProjectionManager projectionManager, WindowManagerInternal windowManager, PackageManagerInternal packageManagerInternal, ArraySet<String> exemptedPackages)249     void init(MediaProjectionManager projectionManager, WindowManagerInternal windowManager,
250             PackageManagerInternal packageManagerInternal, ArraySet<String> exemptedPackages) {
251         if (DEBUG) Log.d(TAG, "init");
252 
253         Objects.requireNonNull(projectionManager);
254         Objects.requireNonNull(windowManager);
255 
256         mProjectionManager = projectionManager;
257         mWindowManager = windowManager;
258         mPackageManagerInternal = packageManagerInternal;
259         mExemptedPackages = exemptedPackages;
260 
261         // TODO(b/317250444): use MediaProjectionManagerService directly, reduces unnecessary
262         //  handler, delegate, and binder death recipient
263         mProjectionManager.addCallback(mProjectionCallback, getContext().getMainThreadHandler());
264 
265         if (sensitiveNotificationAppProtection()) {
266             try {
267                 mNotificationListener.registerAsSystemService(
268                         getContext(),
269                         new ComponentName(getContext(), NotificationListener.class),
270                         UserHandle.USER_ALL);
271             } catch (RemoteException e) {
272                 // Intra-process call, should never happen.
273             }
274         }
275 
276         if (sensitiveContentAppProtection()) {
277             mWindowManager.registerOnWindowRemovedListener(mOnWindowRemovedListener);
278         }
279     }
280 
281     /** Cleanup any callbacks and listeners */
282     @VisibleForTesting
onDestroy()283     void onDestroy() {
284         if (mProjectionManager != null) {
285             mProjectionManager.removeCallback(mProjectionCallback);
286         }
287         if (sensitiveNotificationAppProtection()) {
288             try {
289                 mNotificationListener.unregisterAsSystemService();
290             } catch (RemoteException e) {
291                 // Intra-process call, should never happen.
292             }
293         }
294 
295         if (mWindowManager != null) {
296             onProjectionEnd();
297         }
298     }
299 
canRecordSensitiveContent(@onNull String packageName)300     private boolean canRecordSensitiveContent(@NonNull String packageName) {
301         return getContext().getPackageManager()
302                 .checkPermission(android.Manifest.permission.RECORD_SENSITIVE_CONTENT,
303                         packageName) == PackageManager.PERMISSION_GRANTED;
304     }
305 
306     // These packages are exempted from screen share protection.
getExemptedPackages()307     private ArraySet<String> getExemptedPackages() {
308         return SystemConfig.getInstance().getBugreportWhitelistedPackages();
309     }
310 
onProjectionStart(MediaProjectionInfo projectionInfo)311     private void onProjectionStart(MediaProjectionInfo projectionInfo) {
312         boolean isPackageExempted = (mExemptedPackages != null && mExemptedPackages.contains(
313                 projectionInfo.getPackageName()))
314                 || canRecordSensitiveContent(projectionInfo.getPackageName())
315                 || isAutofillServiceRecorderPackage(projectionInfo.getUserHandle().getIdentifier(),
316                         projectionInfo.getPackageName());
317         // TODO(b/324447419): move GlobalSettings lookup to background thread
318         boolean isFeatureDisabled = Settings.Global.getInt(getContext().getContentResolver(),
319                 DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 0) != 0;
320         int uid = mPackageManagerInternal.getPackageUid(projectionInfo.getPackageName(), 0,
321                 projectionInfo.getUserHandle().getIdentifier());
322         synchronized (mSensitiveContentProtectionLock) {
323             mMediaProjectionSession = new MediaProjectionSession(
324                     uid, isPackageExempted || isFeatureDisabled, new Random().nextLong());
325             mMediaProjectionSession.logProjectionSessionStart();
326 
327             if (isPackageExempted || isFeatureDisabled) {
328                 Log.w(TAG, "projection session is exempted, package ="
329                         + projectionInfo.getPackageName() + ", isFeatureDisabled="
330                         + isFeatureDisabled);
331                 return;
332             }
333 
334             mProjectionActive = true;
335 
336             if (sensitiveContentMetricsBugfix()) {
337                 mWindowManager.setBlockScreenCaptureForAppsSessionId(
338                         mMediaProjectionSession.mSessionId);
339             }
340 
341             if (sensitiveNotificationAppProtection()) {
342                 updateAppsThatShouldBlockScreenCapture();
343             }
344 
345             if (sensitiveContentAppProtection() && mPackagesShowingSensitiveContent.size() > 0) {
346                 mWindowManager.addBlockScreenCaptureForApps(mPackagesShowingSensitiveContent);
347             }
348         }
349     }
350 
onProjectionEnd()351     private void onProjectionEnd() {
352         synchronized (mSensitiveContentProtectionLock) {
353             mProjectionActive = false;
354             if (mMediaProjectionSession != null) {
355                 mMediaProjectionSession.logProjectionSessionStop();
356                 if (sensitiveContentImprovements()) {
357                     mMediaProjectionSession.logAppNotificationsProtected();
358                 }
359                 mMediaProjectionSession = null;
360             }
361 
362             // notify windowmanager to clear any sensitive notifications observed during projection
363             // session
364             mWindowManager.clearBlockedApps();
365         }
366     }
367 
368     @GuardedBy("mSensitiveContentProtectionLock")
updateAppsThatShouldBlockScreenCapture()369     private void updateAppsThatShouldBlockScreenCapture() {
370         RankingMap rankingMap;
371         try {
372             rankingMap = mNotificationListener.getCurrentRanking();
373         } catch (SecurityException e) {
374             Log.e(TAG, "SensitiveContentProtectionManagerService doesn't have access.", e);
375             rankingMap = null;
376         }
377 
378         if (rankingMap == null) {
379             Log.w(TAG, "Ranking map not initialized.");
380             return;
381         }
382 
383         updateAppsThatShouldBlockScreenCapture(rankingMap);
384     }
385 
386     @GuardedBy("mSensitiveContentProtectionLock")
updateAppsThatShouldBlockScreenCapture(@onNull RankingMap rankingMap)387     private void updateAppsThatShouldBlockScreenCapture(@NonNull RankingMap rankingMap) {
388         StatusBarNotification[] notifications;
389         try {
390             notifications = mNotificationListener.getActiveNotifications();
391         } catch (SecurityException e) {
392             Log.e(TAG, "SensitiveContentProtectionManagerService doesn't have access.", e);
393             notifications = new StatusBarNotification[0];
394         }
395 
396         if (sensitiveContentImprovements() && mMediaProjectionSession != null) {
397             mMediaProjectionSession.addSeenNotifications(notifications, rankingMap);
398         }
399 
400         // notify windowmanager of any currently posted sensitive content notifications
401         ArraySet<PackageInfo> packageInfos =
402                 getSensitivePackagesFromNotifications(notifications, rankingMap);
403 
404         if (packageInfos.size() > 0) {
405             mWindowManager.addBlockScreenCaptureForApps(packageInfos);
406         }
407     }
408 
getSensitivePackagesFromNotifications( @onNull StatusBarNotification[] notifications, @NonNull RankingMap rankingMap)409     private static @NonNull ArraySet<PackageInfo> getSensitivePackagesFromNotifications(
410             @NonNull StatusBarNotification[] notifications, @NonNull RankingMap rankingMap) {
411         ArraySet<PackageInfo> sensitivePackages = new ArraySet<>();
412         for (StatusBarNotification sbn : notifications) {
413             if (sbn == null) {
414                 Log.w(TAG, "Unable to parse null notification");
415                 continue;
416             }
417 
418             PackageInfo info = getSensitivePackageFromNotification(sbn, rankingMap);
419             if (info != null) {
420                 sensitivePackages.add(info);
421             }
422         }
423         return sensitivePackages;
424     }
425 
getSensitivePackageFromNotification( @onNull StatusBarNotification sbn, @NonNull RankingMap rankingMap)426     private static @Nullable PackageInfo getSensitivePackageFromNotification(
427             @NonNull StatusBarNotification sbn, @NonNull RankingMap rankingMap) {
428         if (notificationHasSensitiveContent(sbn, rankingMap)) {
429             return new PackageInfo(sbn.getPackageName(), sbn.getUid());
430         }
431         return null;
432     }
433 
notificationHasSensitiveContent( @onNull StatusBarNotification sbn, @NonNull RankingMap rankingMap)434     private static boolean notificationHasSensitiveContent(
435             @NonNull StatusBarNotification sbn, @NonNull RankingMap rankingMap) {
436         NotificationListenerService.Ranking ranking = rankingMap.getRawRankingObject(sbn.getKey());
437         return ranking != null && ranking.hasSensitiveContent();
438     }
439 
440     @VisibleForTesting
441     class NotificationListener extends NotificationListenerService {
442         @Override
onListenerConnected()443         public void onListenerConnected() {
444             super.onListenerConnected();
445             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
446                     "SensitiveContentProtectionManagerService.onListenerConnected");
447             try {
448                 // Projection started before notification listener was connected
449                 synchronized (mSensitiveContentProtectionLock) {
450                     if (mProjectionActive) {
451                         updateAppsThatShouldBlockScreenCapture();
452                     }
453                 }
454             } finally {
455                 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
456             }
457         }
458 
459         @Override
onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap)460         public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
461             super.onNotificationPosted(sbn, rankingMap);
462             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
463                     "SensitiveContentProtectionManagerService.onNotificationPosted");
464             try {
465                 if (sbn == null) {
466                     Log.w(TAG, "Unable to parse null notification");
467                     return;
468                 }
469 
470                 if (rankingMap == null) {
471                     Log.w(TAG, "Ranking map not initialized.");
472                     return;
473                 }
474 
475                 synchronized (mSensitiveContentProtectionLock) {
476                     if (!mProjectionActive) {
477                         return;
478                     }
479 
480                     // notify windowmanager of any currently posted sensitive content notifications
481                     PackageInfo packageInfo = getSensitivePackageFromNotification(sbn, rankingMap);
482 
483                     if (packageInfo != null) {
484                         mWindowManager.addBlockScreenCaptureForApps(
485                                 new ArraySet(Set.of(packageInfo)));
486                     }
487 
488                     if (sensitiveContentImprovements() && mMediaProjectionSession != null) {
489                         if (packageInfo != null) {
490                             mMediaProjectionSession.addSeenOtpNotificationKey(sbn.getKey());
491                         } else {
492                             mMediaProjectionSession.addSeenNotificationKey(sbn.getKey());
493                         }
494                     }
495                 }
496             } finally {
497                 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
498             }
499         }
500 
501         @Override
onNotificationRankingUpdate(RankingMap rankingMap)502         public void onNotificationRankingUpdate(RankingMap rankingMap) {
503             super.onNotificationRankingUpdate(rankingMap);
504             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
505                     "SensitiveContentProtectionManagerService.onNotificationRankingUpdate");
506             try {
507                 if (rankingMap == null) {
508                     Log.w(TAG, "Ranking map not initialized.");
509                     return;
510                 }
511 
512                 synchronized (mSensitiveContentProtectionLock) {
513                     if (mProjectionActive) {
514                         updateAppsThatShouldBlockScreenCapture(rankingMap);
515                     }
516                 }
517             } finally {
518                 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
519             }
520         }
521     }
522 
523     /**
524      * Block projection for a package window when the window is showing sensitive content on
525      * the screen, the projection is unblocked when window no more shows sensitive content.
526      *
527      * @param windowToken               window where the content is shown.
528      * @param packageName               package name.
529      * @param uid                       uid of the package.
530      * @param isShowingSensitiveContent whether the window is showing sensitive content.
531      */
532     @VisibleForTesting
setSensitiveContentProtection(IBinder windowToken, String packageName, int uid, boolean isShowingSensitiveContent)533     void setSensitiveContentProtection(IBinder windowToken, String packageName, int uid,
534             boolean isShowingSensitiveContent) {
535         synchronized (mSensitiveContentProtectionLock) {
536             // The window token distinguish this package from packages added for notifications.
537             PackageInfo packageInfo = new PackageInfo(packageName, uid, windowToken);
538             // track these packages to protect when screen share starts.
539             if (isShowingSensitiveContent) {
540                 mPackagesShowingSensitiveContent.add(packageInfo);
541                 if (mPackagesShowingSensitiveContent.size() > 100) {
542                     Log.w(TAG, "Unexpectedly large number of sensitive windows, count: "
543                             + mPackagesShowingSensitiveContent.size());
544                 }
545             } else {
546                 mPackagesShowingSensitiveContent.remove(packageInfo);
547             }
548             if (!mProjectionActive) {
549                 return;
550             }
551 
552             if (DEBUG) {
553                 Log.d(TAG, "setSensitiveContentProtection - current package=" + packageInfo
554                         + ", isShowingSensitiveContent=" + isShowingSensitiveContent
555                         + ", sensitive packages=" + mPackagesShowingSensitiveContent);
556             }
557 
558             ArraySet<PackageInfo> packageInfos = new ArraySet<>();
559             packageInfos.add(packageInfo);
560             if (isShowingSensitiveContent) {
561                 mWindowManager.addBlockScreenCaptureForApps(packageInfos);
562                 if (mMediaProjectionSession != null) {
563                     mMediaProjectionSession.logAppBlocked(uid);
564                 }
565             } else {
566                 mWindowManager.removeBlockScreenCaptureForApps(packageInfos);
567                 if (mMediaProjectionSession != null) {
568                     mMediaProjectionSession.logAppUnblocked(uid);
569                 }
570             }
571         }
572     }
573 
574     // TODO: b/328251279 - Autofill service exemption is temporary and will be removed in future.
isAutofillServiceRecorderPackage(int userId, String projectionPackage)575     private boolean isAutofillServiceRecorderPackage(int userId, String projectionPackage) {
576         String autofillServiceName = Settings.Secure.getStringForUser(
577                 getContext().getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, userId);
578         if (DEBUG) {
579             Log.d(TAG, "autofill service for user " + userId + " is " + autofillServiceName);
580         }
581 
582         if (autofillServiceName == null) {
583             return false;
584         }
585         ComponentName serviceComponent = ComponentName.unflattenFromString(autofillServiceName);
586         if (serviceComponent == null) {
587             return false;
588         }
589         String autofillServicePackage = serviceComponent.getPackageName();
590 
591         return autofillServicePackage != null
592                 && autofillServicePackage.equals(projectionPackage);
593     }
594 
595     private final class SensitiveContentProtectionManagerServiceBinder
596             extends ISensitiveContentProtectionManager.Stub {
setSensitiveContentProtection(IBinder windowToken, String packageName, boolean isShowingSensitiveContent)597         public void setSensitiveContentProtection(IBinder windowToken, String packageName,
598                 boolean isShowingSensitiveContent) {
599             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
600                     "SensitiveContentProtectionManagerService.setSensitiveContentProtection");
601             try {
602                 int callingUid = Binder.getCallingUid();
603                 verifyCallingPackage(callingUid, packageName);
604                 final long identity = Binder.clearCallingIdentity();
605                 try {
606                     if (isShowingSensitiveContent
607                             && mWindowManager.getWindowName(windowToken) == null) {
608                         Log.e(TAG, "window token is not know to WMS, can't apply protection,"
609                                 + " token: " + windowToken + ", package: " + packageName);
610                         return;
611                     }
612                     SensitiveContentProtectionManagerService.this.setSensitiveContentProtection(
613                             windowToken, packageName, callingUid, isShowingSensitiveContent);
614                 } finally {
615                     Binder.restoreCallingIdentity(identity);
616                 }
617             } finally {
618                 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
619             }
620         }
621 
verifyCallingPackage(int callingUid, String callingPackage)622         private void verifyCallingPackage(int callingUid, String callingPackage) {
623             if (mPackageManagerInternal.getPackageUid(
624                     callingPackage, 0, UserHandle.getUserId(callingUid)) != callingUid) {
625                 throw new SecurityException("Specified calling package [" + callingPackage
626                         + "] does not match the calling uid " + callingUid);
627             }
628         }
629     }
630 }
631