• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.car.pm;
18 
19 import static android.Manifest.permission.QUERY_ALL_PACKAGES;
20 import static android.car.CarOccupantZoneManager.DISPLAY_TYPE_MAIN;
21 import static android.car.content.pm.CarPackageManager.BLOCKING_INTENT_EXTRA_BLOCKED_ACTIVITY_NAME;
22 import static android.car.content.pm.CarPackageManager.BLOCKING_INTENT_EXTRA_BLOCKED_TASK_ID;
23 import static android.car.content.pm.CarPackageManager.BLOCKING_INTENT_EXTRA_DISPLAY_ID;
24 import static android.car.content.pm.CarPackageManager.BLOCKING_INTENT_EXTRA_IS_ROOT_ACTIVITY_DO;
25 import static android.car.content.pm.CarPackageManager.BLOCKING_INTENT_EXTRA_ROOT_ACTIVITY_NAME;
26 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING;
27 
28 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
29 import static com.android.car.util.Utils.isEventOfType;
30 
31 import android.annotation.NonNull;
32 import android.annotation.Nullable;
33 import android.annotation.UserIdInt;
34 import android.app.ActivityManager;
35 import android.app.PendingIntent;
36 import android.app.TaskInfo;
37 import android.car.Car;
38 import android.car.CarVersion;
39 import android.car.builtin.app.ActivityManagerHelper;
40 import android.car.builtin.app.TaskInfoHelper;
41 import android.car.builtin.content.pm.PackageManagerHelper;
42 import android.car.builtin.os.BuildHelper;
43 import android.car.builtin.os.ServiceManagerHelper;
44 import android.car.builtin.util.Slogf;
45 import android.car.content.pm.AppBlockingPackageInfo;
46 import android.car.content.pm.CarAppBlockingPolicy;
47 import android.car.content.pm.CarAppBlockingPolicyService;
48 import android.car.content.pm.CarPackageManager;
49 import android.car.content.pm.ICarPackageManager;
50 import android.car.drivingstate.CarUxRestrictions;
51 import android.car.drivingstate.ICarUxRestrictionsChangeListener;
52 import android.car.hardware.power.CarPowerPolicy;
53 import android.car.hardware.power.CarPowerPolicyFilter;
54 import android.car.hardware.power.ICarPowerPolicyListener;
55 import android.car.hardware.power.PowerComponent;
56 import android.car.user.CarUserManager.UserLifecycleListener;
57 import android.car.user.UserLifecycleEventFilter;
58 import android.content.BroadcastReceiver;
59 import android.content.ComponentName;
60 import android.content.Context;
61 import android.content.Intent;
62 import android.content.IntentFilter;
63 import android.content.pm.ActivityInfo;
64 import android.content.pm.ApplicationInfo;
65 import android.content.pm.PackageInfo;
66 import android.content.pm.PackageManager;
67 import android.content.pm.PackageManager.NameNotFoundException;
68 import android.content.pm.ResolveInfo;
69 import android.content.pm.ServiceInfo;
70 import android.content.pm.Signature;
71 import android.content.res.Resources;
72 import android.hardware.display.DisplayManager;
73 import android.os.Binder;
74 import android.os.Handler;
75 import android.os.HandlerThread;
76 import android.os.IBinder;
77 import android.os.Looper;
78 import android.os.Message;
79 import android.os.ParcelFileDescriptor;
80 import android.os.Process;
81 import android.os.RemoteException;
82 import android.os.ServiceSpecificException;
83 import android.os.SystemClock;
84 import android.os.SystemProperties;
85 import android.os.UserHandle;
86 import android.text.TextUtils;
87 import android.util.ArraySet;
88 import android.util.Log;
89 import android.util.Pair;
90 import android.util.SparseArray;
91 import android.util.SparseBooleanArray;
92 import android.util.SparseLongArray;
93 import android.view.Display;
94 import android.view.accessibility.AccessibilityEvent;
95 
96 import com.android.car.CarLocalServices;
97 import com.android.car.CarLog;
98 import com.android.car.CarOccupantZoneService;
99 import com.android.car.CarServiceBase;
100 import com.android.car.CarServiceUtils;
101 import com.android.car.CarUxRestrictionsManagerService;
102 import com.android.car.R;
103 import com.android.car.am.CarActivityService;
104 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
105 import com.android.car.internal.util.IndentingPrintWriter;
106 import com.android.car.internal.util.IntArray;
107 import com.android.car.internal.util.LocalLog;
108 import com.android.car.internal.util.Sets;
109 import com.android.car.power.CarPowerManagementService;
110 import com.android.car.user.CarUserService;
111 import com.android.car.util.Utils;
112 import com.android.internal.annotations.GuardedBy;
113 import com.android.internal.annotations.VisibleForTesting;
114 
115 import java.io.BufferedReader;
116 import java.io.FileReader;
117 import java.io.IOException;
118 import java.lang.ref.WeakReference;
119 import java.util.ArrayList;
120 import java.util.Arrays;
121 import java.util.HashMap;
122 import java.util.HashSet;
123 import java.util.LinkedList;
124 import java.util.List;
125 import java.util.Map;
126 import java.util.Map.Entry;
127 import java.util.Objects;
128 import java.util.Set;
129 
130 /**
131  * Package manager service for cars.
132  */
133 public final class CarPackageManagerService extends ICarPackageManager.Stub
134         implements CarServiceBase {
135 
136     @VisibleForTesting
137     static final String TAG = CarLog.tagFor(CarPackageManagerService.class);
138 
139     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
140 
141     // Delimiters to parse packages and activities in the configuration XML resource.
142     private static final String PACKAGE_DELIMITER = ",";
143     private static final String PACKAGE_ACTIVITY_DELIMITER = "/";
144     private static final int LOG_SIZE = 20;
145     private static final String[] WINDOW_DUMP_ARGUMENTS = new String[]{"windows"};
146 
147     private static final String PROPERTY_RO_DRIVING_SAFETY_REGION =
148             "ro.android.car.drivingsafetyregion";
149     private static final int ABA_LAUNCH_TIMEOUT_MS = 1_000;
150 
151     private final Context mContext;
152     private final CarActivityService mActivityService;
153     private final PackageManager mPackageManager;
154     private final ActivityManager mActivityManager;
155     private final DisplayManager mDisplayManager;
156     private final IBinder mWindowManagerBinder;
157 
158     private final HandlerThread mHandlerThread = CarServiceUtils.getHandlerThread(
159             getClass().getSimpleName());
160     private final PackageHandler mHandler  = new PackageHandler(mHandlerThread.getLooper(), this);
161     private final Object mLock = new Object();
162 
163     // For dumpsys logging.
164     private final LocalLog mBlockedActivityLogs = new LocalLog(LOG_SIZE);
165 
166     // Store the allowlist and blocklist strings from the resource file.
167     private String mConfiguredAllowlist;
168     private String mConfiguredSystemAllowlist;
169     private String mConfiguredBlocklist;
170     @GuardedBy("mLock")
171     private Map<String, Set<String>> mConfiguredAllowlistMap;
172     @GuardedBy("mLock")
173     private Map<String, Set<String>> mConfiguredBlocklistMap;
174 
175     private final List<String> mAllowedAppInstallSources;
176 
177     // A SparseBooleanArray to handle the already blocked display IDs when iterating on the visible
178     // tasks. This is defined as an instance variable to avoid frequent creations.
179     // This Array is cleared everytime before its use.
180     private final SparseBooleanArray mBlockedDisplayIds = new SparseBooleanArray();
181 
182     @GuardedBy("mLock")
183     private final SparseArray<ComponentName> mTopActivityWithDialogPerDisplay = new SparseArray<>();
184 
185     /**
186      * Hold policy set from policy service or client.
187      * Key: packageName of policy service
188      */
189     @GuardedBy("mLock")
190     private final HashMap<String, ClientPolicy> mClientPolicies = new HashMap<>();
191     @GuardedBy("mLock")
192     private HashMap<String, AppBlockingPackageInfoWrapper> mActivityAllowlistMap = new HashMap<>();
193     @GuardedBy("mLock")
194     private  HashSet<String> mActivityDenylistPackages = new HashSet<String>();
195 
196     @GuardedBy("mLock")
197     private LinkedList<AppBlockingPolicyProxy> mProxies;
198 
199     @GuardedBy("mLock")
200     private final LinkedList<CarAppBlockingPolicy> mWaitingPolicies = new LinkedList<>();
201 
202     @GuardedBy("mLock")
203     private String mCurrentDrivingSafetyRegion = CarPackageManager.DRIVING_SAFETY_REGION_ALL;
204     // Package name + '/' + className format
205     @GuardedBy("mLock")
206     private final ArraySet<String> mTempAllowedActivities = new ArraySet<>();
207 
208     private final CarUxRestrictionsManagerService mCarUxRestrictionsService;
209     private final CarOccupantZoneService mCarOccupantZoneService;
210     private final boolean mEnableActivityBlocking;
211 
212     private final ComponentName mActivityBlockingActivity;
213     // Memorize the target of ABA to defend bypassing it with launching two Activities continuously.
214     private final SparseArray<ComponentName> mBlockingActivityTargets = new SparseArray<>();
215     private final SparseLongArray mBlockingActivityLaunchTimes = new SparseLongArray();
216     private final boolean mPreventTemplatedAppsFromShowingDialog;
217     private final String mTemplateActivityClassName;
218 
219     private final ActivityLaunchListener mActivityLaunchListener = new ActivityLaunchListener();
220 
221     // K: (logical) display id of a physical display, V: UXR change listener of this display.
222     // For multi-display, monitor UXR change on each display.
223     private final SparseArray<UxRestrictionsListener> mUxRestrictionsListeners =
224             new SparseArray<>();
225     private final VendorServiceController mVendorServiceController;
226 
227     // Information related to when the installed packages should be parsed for building a allow and
228     // block list
229     private final Set<String> mPackageManagerActions = Sets.newArraySet(
230             Intent.ACTION_PACKAGE_ADDED,
231             Intent.ACTION_PACKAGE_CHANGED,
232             Intent.ACTION_PACKAGE_REMOVED,
233             Intent.ACTION_PACKAGE_REPLACED);
234 
235     private final PackageParsingEventReceiver mPackageParsingEventReceiver =
236             new PackageParsingEventReceiver();
237 
238     private final UserLifecycleListener mUserLifecycleListener = event -> {
239         if (!isEventOfType(TAG, event, USER_LIFECYCLE_EVENT_TYPE_SWITCHING)) {
240             return;
241         }
242 
243         synchronized (mLock) {
244             resetTempAllowedActivitiesLocked();
245         }
246     };
247 
248     private final ICarPowerPolicyListener mDisplayPowerPolicyListener =
249             new ICarPowerPolicyListener.Stub() {
250                 @Override
251                 public void onPolicyChanged(CarPowerPolicy policy,
252                         CarPowerPolicy accumulatedPolicy) {
253                     if (!policy.isComponentEnabled(PowerComponent.DISPLAY)) {
254                         synchronized (mLock) {
255                             resetTempAllowedActivitiesLocked();
256                         }
257                     }
258                 }
259             };
260 
CarPackageManagerService(Context context, CarUxRestrictionsManagerService uxRestrictionsService, CarActivityService activityService, CarOccupantZoneService carOccupantZoneService)261     public CarPackageManagerService(Context context,
262             CarUxRestrictionsManagerService uxRestrictionsService,
263             CarActivityService activityService, CarOccupantZoneService carOccupantZoneService) {
264         mContext = context;
265         mCarUxRestrictionsService = uxRestrictionsService;
266         mActivityService = activityService;
267         mCarOccupantZoneService = carOccupantZoneService;
268         mPackageManager = mContext.getPackageManager();
269         mActivityManager = mContext.getSystemService(ActivityManager.class);
270         mDisplayManager = mContext.getSystemService(DisplayManager.class);
271         mWindowManagerBinder = ServiceManagerHelper.getService(Context.WINDOW_SERVICE);
272         Resources res = context.getResources();
273         mEnableActivityBlocking = res.getBoolean(R.bool.enableActivityBlockingForSafety);
274         String blockingActivity = res.getString(R.string.activityBlockingActivity);
275         mActivityBlockingActivity = ComponentName.unflattenFromString(blockingActivity);
276         if (mEnableActivityBlocking && mActivityBlockingActivity == null) {
277             Slogf.wtf(TAG, "mActivityBlockingActivity can't be null when enabled");
278         }
279         mAllowedAppInstallSources = Arrays.asList(
280                 res.getStringArray(R.array.allowedAppInstallSources));
281         mVendorServiceController = new VendorServiceController(
282                 mContext, mHandler.getLooper());
283         mPreventTemplatedAppsFromShowingDialog =
284                 res.getBoolean(R.bool.config_preventTemplatedAppsFromShowingDialog);
285         mTemplateActivityClassName = res.getString(R.string.config_template_activity_class_name);
286     }
287 
288 
289     @Override
setAppBlockingPolicy(String packageName, CarAppBlockingPolicy policy, int flags)290     public void setAppBlockingPolicy(String packageName, CarAppBlockingPolicy policy, int flags) {
291         if (Slogf.isLoggable(TAG, Log.DEBUG)) {
292             Slogf.d(TAG, "policy setting from binder call, client:" + packageName);
293         }
294         doSetAppBlockingPolicy(packageName, policy, flags);
295     }
296 
297     /**
298      * Restarts the requested task. If task with {@code taskId} does not exist, do nothing.
299      */
300     @Override
restartTask(int taskId)301     public void restartTask(int taskId) {
302         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.REAL_GET_TASKS)
303                 != PackageManager.PERMISSION_GRANTED) {
304             throw new SecurityException(
305                     "requires permission " + android.Manifest.permission.REAL_GET_TASKS);
306         }
307         mActivityService.restartTask(taskId);
308     }
309 
310     @Override
getCurrentDrivingSafetyRegion()311     public String getCurrentDrivingSafetyRegion() {
312         assertAppBlockingOrDrivingStatePermission();
313         synchronized (mLock) {
314             return mCurrentDrivingSafetyRegion;
315         }
316     }
317 
getComponentNameString(String packageName, String className)318     private String getComponentNameString(String packageName, String className) {
319         return packageName + '/' + className;
320     }
321 
322     @Override
controlOneTimeActivityBlockingBypassingAsUser(String packageName, String activityClassName, boolean bypass, @UserIdInt int userId)323     public void controlOneTimeActivityBlockingBypassingAsUser(String packageName,
324             String activityClassName, boolean bypass, @UserIdInt int userId) {
325         assertAppBlockingPermission();
326         if (!callerCanQueryPackage(packageName)) {
327             throw new SecurityException("cannot query other package");
328         }
329         try {
330             // Read this to check the validity of pkg / activity name. Not checking this can allow
331             // bad apps to be allowed later.
332             CarAppMetadataReader.getSupportedDrivingSafetyRegionsForActivityAsUser(mContext,
333                     packageName, activityClassName, userId);
334         } catch (NameNotFoundException e) {
335             throw new ServiceSpecificException(CarPackageManager.ERROR_CODE_NO_PACKAGE,
336                     e.getMessage());
337         }
338         String componentName = getComponentNameString(packageName, activityClassName);
339         synchronized (mLock) {
340             if (bypass) {
341                 mTempAllowedActivities.add(componentName);
342             } else {
343                 mTempAllowedActivities.remove(componentName);
344             }
345         }
346         if (!bypass) { // block top activities if bypassing is disabled.
347             blockTopActivitiesIfNecessary();
348         }
349     }
350 
351     @GuardedBy("mLock")
resetTempAllowedActivitiesLocked()352     private void resetTempAllowedActivitiesLocked() {
353         mTempAllowedActivities.clear();
354     }
355 
356     @Override
getSupportedDrivingSafetyRegionsForActivityAsUser(String packageName, String activityClassName, @UserIdInt int userId)357     public List<String> getSupportedDrivingSafetyRegionsForActivityAsUser(String packageName,
358             String activityClassName, @UserIdInt int userId) {
359         assertAppBlockingOrDrivingStatePermission();
360         if (!callerCanQueryPackage(packageName)) {
361             throw new SecurityException("cannot query other package");
362         }
363         long token = Binder.clearCallingIdentity();
364         try {
365             return CarAppMetadataReader.getSupportedDrivingSafetyRegionsForActivityAsUser(mContext,
366                     packageName, activityClassName, userId);
367         } catch (NameNotFoundException e) {
368             throw new ServiceSpecificException(CarPackageManager.ERROR_CODE_NO_PACKAGE,
369                     e.getMessage());
370         } finally {
371             Binder.restoreCallingIdentity(token);
372         }
373     }
374 
assertAppBlockingOrDrivingStatePermission()375     private void assertAppBlockingOrDrivingStatePermission() {
376         if (mContext.checkCallingOrSelfPermission(Car.PERMISSION_CONTROL_APP_BLOCKING)
377                 != PackageManager.PERMISSION_GRANTED && mContext.checkCallingOrSelfPermission(
378                 Car.PERMISSION_CAR_DRIVING_STATE) != PackageManager.PERMISSION_GRANTED) {
379             throw new SecurityException(
380                     "requires permission " + Car.PERMISSION_CONTROL_APP_BLOCKING + " or "
381                             + Car.PERMISSION_CAR_DRIVING_STATE);
382         }
383     }
384 
assertAppBlockingPermission()385     private void assertAppBlockingPermission() {
386         if (mContext.checkCallingOrSelfPermission(Car.PERMISSION_CONTROL_APP_BLOCKING)
387                 != PackageManager.PERMISSION_GRANTED) {
388             throw new SecurityException(
389                     "requires permission " + Car.PERMISSION_CONTROL_APP_BLOCKING);
390         }
391     }
392 
doSetAppBlockingPolicy(String packageName, CarAppBlockingPolicy policy, int flags)393     private void doSetAppBlockingPolicy(String packageName, CarAppBlockingPolicy policy,
394             int flags) {
395         assertAppBlockingPermission();
396         CarServiceUtils.assertPackageName(mContext, packageName);
397         if (policy == null) {
398             throw new IllegalArgumentException("policy cannot be null");
399         }
400         if ((flags & CarPackageManager.FLAG_SET_POLICY_ADD) != 0 &&
401                 (flags & CarPackageManager.FLAG_SET_POLICY_REMOVE) != 0) {
402             throw new IllegalArgumentException(
403                     "Cannot set both FLAG_SET_POLICY_ADD and FLAG_SET_POLICY_REMOVE flag");
404         }
405         synchronized (mLock) {
406             if ((flags & CarPackageManager.FLAG_SET_POLICY_WAIT_FOR_CHANGE) != 0) {
407                 mWaitingPolicies.add(policy);
408             }
409         }
410         mHandler.requestUpdatingPolicy(packageName, policy, flags);
411         if ((flags & CarPackageManager.FLAG_SET_POLICY_WAIT_FOR_CHANGE) != 0) {
412             synchronized (mLock) {
413                 try {
414                     while (mWaitingPolicies.contains(policy)) {
415                         mLock.wait();
416                     }
417                 } catch (InterruptedException e) {
418                     // Pass it over binder call
419                     throw new IllegalStateException(
420                             "Interrupted while waiting for policy completion", e);
421                 }
422             }
423         }
424     }
425 
426     @Override
isActivityDistractionOptimized(String packageName, String className)427     public boolean isActivityDistractionOptimized(String packageName, String className) {
428         if (!callerCanQueryPackage(packageName)) return false;
429         assertPackageAndClassName(packageName, className);
430         synchronized (mLock) {
431             if (Slogf.isLoggable(TAG, Log.DEBUG)) {
432                 Slogf.d(TAG, "isActivityDistractionOptimized" + dumpPoliciesLocked(false));
433             }
434 
435             if (mTempAllowedActivities.contains(getComponentNameString(packageName,
436                     className))) {
437                 return true;
438             }
439 
440             for (int i = mTopActivityWithDialogPerDisplay.size() - 1; i >= 0; i--) {
441                 ComponentName activityWithDialog = mTopActivityWithDialogPerDisplay.get(
442                         mTopActivityWithDialogPerDisplay.keyAt(i));
443                 if (activityWithDialog.getClassName().equals(className)
444                         && activityWithDialog.getPackageName().equals(packageName)) {
445                     return false;
446                 }
447             }
448 
449             if (searchFromClientPolicyBlocklistsLocked(packageName)) {
450                 return false;
451             }
452 
453             if (isActivityInClientPolicyAllowlistsLocked(packageName, className)) {
454                 return true;
455             }
456 
457             // Check deny and allow list
458             boolean packageBlocked = mActivityDenylistPackages.contains(packageName);
459             AppBlockingPackageInfoWrapper infoWrapper = mActivityAllowlistMap.get(packageName);
460             if (!packageBlocked && infoWrapper == null) {
461                 // Update cache
462                 updateActivityAllowlistAndDenylistMap(packageName);
463                 packageBlocked = mActivityDenylistPackages.contains(packageName);
464                 infoWrapper = mActivityAllowlistMap.get(packageName);
465             }
466 
467             if (packageBlocked
468                     || !isActivityInMapAndMatching(infoWrapper, packageName, className)) {
469                 return false;
470             }
471 
472             return true;
473         }
474     }
475 
476     @VisibleForTesting
callerCanQueryPackage(String packageName)477     boolean callerCanQueryPackage(String packageName) {
478         int callingUid = Binder.getCallingUid();
479         if (hasPermissionGranted(QUERY_ALL_PACKAGES, callingUid)) {
480             return true;
481         }
482         String[] packages = mPackageManager.getPackagesForUid(callingUid);
483         if (packages != null && packages.length > 0) {
484             for (int i = 0; i < packages.length; i++) {
485                 if (Objects.equals(packageName, packages[i])) {
486                     return true;
487                 }
488             }
489         }
490 
491         Slogf.w(TAG, QUERY_ALL_PACKAGES + " permission is needed to query other packages.");
492 
493         return false;
494     }
495 
hasPermissionGranted(String permission, int uid)496     private static boolean hasPermissionGranted(String permission, int uid) {
497         return ActivityManagerHelper.checkComponentPermission(permission, uid,
498                 /* owningUid= */ Process.INVALID_UID,
499                 /* exported= */ true) == PackageManager.PERMISSION_GRANTED;
500     }
501 
502     @Override
isPendingIntentDistractionOptimized(PendingIntent pendingIntent)503     public boolean isPendingIntentDistractionOptimized(PendingIntent pendingIntent) {
504         if (!pendingIntent.isActivity()) {
505             Slogf.d(TAG, "isPendingIntentDistractionOptimized: Activity not set on the "
506                     + "pending intent.");
507             return false;
508         }
509         List<ResolveInfo> infos = pendingIntent.queryIntentComponents(
510                 PackageManager.MATCH_DEFAULT_ONLY);
511         if (infos.isEmpty()) {
512             Slogf.d(TAG, "isPendingIntentDistractionOptimized: No intent component found for "
513                     + "the pending intent.");
514             return false;
515         }
516         if (infos.size() > 1) {
517             Slogf.d(TAG, "isPendingIntentDistractionOptimized: More than one intent component"
518                     + " found for the pending intent. Considering the first one.");
519         }
520         ActivityInfo activityInfo = infos.get(0).activityInfo;
521         return isActivityDistractionOptimized(activityInfo.packageName, activityInfo.name);
522     }
523 
524     @Override
isServiceDistractionOptimized(String packageName, String className)525     public boolean isServiceDistractionOptimized(String packageName, String className) {
526         if (!callerCanQueryPackage(packageName)) return false;
527 
528         if (packageName == null) {
529             throw new IllegalArgumentException("Package name null");
530         }
531         synchronized (mLock) {
532             if (Slogf.isLoggable(TAG, Log.DEBUG)) {
533                 Slogf.d(TAG, "isServiceDistractionOptimized" + dumpPoliciesLocked(false));
534             }
535 
536             if (searchFromClientPolicyBlocklistsLocked(packageName)) {
537                 return false;
538             }
539 
540             if (searchFromClientPolicyAllowlistsLocked(packageName)) {
541                 return true;
542             }
543 
544             // Check deny and allow list
545             boolean packageBlocked = mActivityDenylistPackages.contains(packageName);
546             AppBlockingPackageInfoWrapper infoWrapper = mActivityAllowlistMap.get(packageName);
547             if (!packageBlocked && infoWrapper == null) {
548                 // Update cache
549                 updateActivityAllowlistAndDenylistMap(packageName);
550                 packageBlocked = mActivityDenylistPackages.contains(packageName);
551                 infoWrapper = mActivityAllowlistMap.get(packageName);
552             }
553 
554             if (packageBlocked || infoWrapper == null || infoWrapper.info == null) {
555                 return false;
556             }
557 
558             return true;
559         }
560     }
561 
562     @Override
isActivityBackedBySafeActivity(ComponentName activityName)563     public boolean isActivityBackedBySafeActivity(ComponentName activityName) {
564         if (activityName == null) return false;
565         if (!callerCanQueryPackage(activityName.getPackageName())) return false;
566 
567         TaskInfo info = mActivityService.getTaskInfoForTopActivity(activityName);
568         if (DBG) {
569             Slogf.d(TAG, "isActivityBackedBySafeActivity: info=%s",
570                     TaskInfoHelper.toString(info));
571         }
572         if (info == null) { // not top in focused stack
573             return true;
574         }
575         if (!isUxRestrictedOnDisplay(TaskInfoHelper.getDisplayId(info))) {
576             return true;
577         }
578         if (info.baseActivity == null
579                 || info.baseActivity.equals(activityName)) {  // nothing below this.
580             return false;
581         }
582         return isActivityDistractionOptimized(info.baseActivity.getPackageName(),
583                 info.baseActivity.getClassName());
584     }
585 
getLooper()586     public Looper getLooper() {
587         return mHandlerThread.getLooper();
588     }
589 
assertPackageAndClassName(String packageName, String className)590     private void assertPackageAndClassName(String packageName, String className) {
591         if (packageName == null) {
592             throw new IllegalArgumentException("Package name null");
593         }
594         if (className == null) {
595             throw new IllegalArgumentException("Class name null");
596         }
597     }
598 
599     @GuardedBy("mLock")
searchFromClientPolicyBlocklistsLocked(String packageName)600     private boolean searchFromClientPolicyBlocklistsLocked(String packageName) {
601         for (ClientPolicy policy : mClientPolicies.values()) {
602             AppBlockingPackageInfoWrapper wrapper = policy.mBlocklistsMap.get(packageName);
603             if (wrapper != null && wrapper.isMatching && wrapper.info != null) {
604                 return true;
605             }
606         }
607 
608         return false;
609     }
610 
611     @GuardedBy("mLock")
searchFromClientPolicyAllowlistsLocked(String packageName)612     private boolean searchFromClientPolicyAllowlistsLocked(String packageName) {
613         for (ClientPolicy policy : mClientPolicies.values()) {
614             AppBlockingPackageInfoWrapper wrapper = policy.mAllowlistsMap.get(packageName);
615             if (wrapper != null && wrapper.isMatching && wrapper.info != null) {
616                 return true;
617             }
618         }
619         return false;
620     }
621 
622     @GuardedBy("mLock")
isActivityInClientPolicyAllowlistsLocked(String packageName, String className)623     private boolean isActivityInClientPolicyAllowlistsLocked(String packageName, String className) {
624         for (ClientPolicy policy : mClientPolicies.values()) {
625             if (isActivityInMapAndMatching(policy.mAllowlistsMap.get(packageName), packageName,
626                     className)) {
627                 return true;
628             }
629         }
630         return false;
631     }
632 
isActivityInMapAndMatching(AppBlockingPackageInfoWrapper wrapper, String packageName, String className)633     private boolean isActivityInMapAndMatching(AppBlockingPackageInfoWrapper wrapper,
634             String packageName, String className) {
635         if (wrapper == null || !wrapper.isMatching) {
636             if (Slogf.isLoggable(TAG, Log.DEBUG)) {
637                 Slogf.d(TAG, "Pkg not in allowlist:" + packageName);
638             }
639             return false;
640         }
641         return wrapper.info.isActivityCovered(className);
642     }
643 
644     @Override
init()645     public void init() {
646         String safetyRegion = SystemProperties.get(PROPERTY_RO_DRIVING_SAFETY_REGION, "");
647         synchronized (mLock) {
648             setDrivingSafetyRegionWithCheckLocked(safetyRegion);
649             mHandler.requestInit();
650         }
651         UserLifecycleEventFilter userSwitchingEventFilter = new UserLifecycleEventFilter.Builder()
652                 .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING).build();
653         CarLocalServices.getService(CarUserService.class).addUserLifecycleListener(
654                 userSwitchingEventFilter, mUserLifecycleListener);
655         CarLocalServices.getService(CarPowerManagementService.class).addPowerPolicyListener(
656                 new CarPowerPolicyFilter.Builder().setComponents(PowerComponent.DISPLAY).build(),
657                 mDisplayPowerPolicyListener);
658     }
659 
660     @Override
release()661     public void release() {
662         CarLocalServices.getService(CarPowerManagementService.class).removePowerPolicyListener(
663                 mDisplayPowerPolicyListener);
664         CarLocalServices.getService(CarUserService.class).removeUserLifecycleListener(
665                 mUserLifecycleListener);
666         synchronized (mLock) {
667             mHandler.requestRelease();
668             // wait for release do be done. This guarantees that init is done.
669             try {
670                 mLock.wait();
671             } catch (InterruptedException e) {
672                 Slogf.e(TAG, "Interrupted wait during release");
673                 Thread.currentThread().interrupt();
674             }
675             mActivityAllowlistMap.clear();
676             mActivityDenylistPackages.clear();
677             mClientPolicies.clear();
678             if (mProxies != null) {
679                 for (AppBlockingPolicyProxy proxy : mProxies) {
680                     proxy.disconnect();
681                 }
682                 mProxies.clear();
683             }
684             mWaitingPolicies.clear();
685             resetTempAllowedActivitiesLocked();
686             mLock.notifyAll();
687         }
688         mContext.unregisterReceiver(mPackageParsingEventReceiver);
689         mActivityService.registerActivityLaunchListener(null);
690         for (int i = 0; i < mUxRestrictionsListeners.size(); i++) {
691             UxRestrictionsListener listener = mUxRestrictionsListeners.valueAt(i);
692             mCarUxRestrictionsService.unregisterUxRestrictionsChangeListener(listener);
693         }
694     }
695 
696     @GuardedBy("mLock")
setDrivingSafetyRegionWithCheckLocked(String region)697     private void setDrivingSafetyRegionWithCheckLocked(String region) {
698         if (region.isEmpty()) {
699             mCurrentDrivingSafetyRegion = CarPackageManager.DRIVING_SAFETY_REGION_ALL;
700         } else {
701             mCurrentDrivingSafetyRegion = region;
702         }
703     }
704 
705     /**
706      * Reset driving stat and all dynamically added allow list so that region information for
707      * all packages are reset. This also resets one time allow list.
708      */
resetDrivingSafetyRegion(@onNull String region)709     public void resetDrivingSafetyRegion(@NonNull String region) {
710         synchronized (mLock) {
711             setDrivingSafetyRegionWithCheckLocked(region);
712             resetTempAllowedActivitiesLocked();
713             mActivityAllowlistMap.clear();
714             mActivityDenylistPackages.clear();
715         }
716     }
717 
718     // run from HandlerThread
doHandleInit()719     private void doHandleInit() {
720         startAppBlockingPolicies();
721         IntentFilter pkgParseIntent = new IntentFilter();
722         for (String action : mPackageManagerActions) {
723             pkgParseIntent.addAction(action);
724         }
725         pkgParseIntent.addDataScheme("package");
726         mContext.registerReceiverForAllUsers(mPackageParsingEventReceiver, pkgParseIntent,
727                 /* broadcastPermission= */ null, /* scheduler= */ null,
728                 Context.RECEIVER_NOT_EXPORTED);
729 
730         // CarOccupantZoneService makes it sure that the default display is a driver display.
731         IntArray displayIdsForDriver = mCarOccupantZoneService.getAllDisplayIdsForDriver(
732                 DISPLAY_TYPE_MAIN);
733 
734         for (int i = 0; i < displayIdsForDriver.size(); ++i) {
735             int displayId = displayIdsForDriver.get(i);
736             UxRestrictionsListener listener = new UxRestrictionsListener(mCarUxRestrictionsService);
737             mUxRestrictionsListeners.put(displayId, listener);
738             mCarUxRestrictionsService.registerUxRestrictionsChangeListener(listener, displayId);
739         }
740         mVendorServiceController.init();
741         mActivityService.registerActivityLaunchListener(mActivityLaunchListener);
742     }
743 
doParseInstalledPackage(String packageName)744     private void doParseInstalledPackage(String packageName) {
745         // Delete the package from allowlist and denylist mapping
746         synchronized (mLock) {
747             mActivityDenylistPackages.remove(packageName);
748             mActivityAllowlistMap.remove(packageName);
749         }
750 
751         // Generate allowlist and denylist mapping for the package
752         updateActivityAllowlistAndDenylistMap(packageName);
753         blockTopActivitiesIfNecessary();
754     }
755 
doHandleRelease()756     private void doHandleRelease() {
757         synchronized (mLock) {
758             mVendorServiceController.release();
759             mLock.notifyAll();
760         }
761     }
762 
doUpdatePolicy(String packageName, CarAppBlockingPolicy policy, int flags)763     private void doUpdatePolicy(String packageName, CarAppBlockingPolicy policy, int flags) {
764         if (Slogf.isLoggable(TAG, Log.DEBUG)) {
765             Slogf.d(TAG, "setting policy from:" + packageName + ",policy:" + policy + ",flags:0x"
766                     + Integer.toHexString(flags));
767         }
768         AppBlockingPackageInfoWrapper[] blocklistWrapper = verifyList(policy.blacklists);
769         AppBlockingPackageInfoWrapper[] allowlistWrapper = verifyList(policy.whitelists);
770         synchronized (mLock) {
771             ClientPolicy clientPolicy = mClientPolicies.get(packageName);
772             if (clientPolicy == null) {
773                 clientPolicy = new ClientPolicy();
774                 mClientPolicies.put(packageName, clientPolicy);
775             }
776             if ((flags & CarPackageManager.FLAG_SET_POLICY_ADD) != 0) {
777                 clientPolicy.addToBlocklists(blocklistWrapper);
778                 clientPolicy.addToAllowlists(allowlistWrapper);
779             } else if ((flags & CarPackageManager.FLAG_SET_POLICY_REMOVE) != 0) {
780                 clientPolicy.removeBlocklists(blocklistWrapper);
781                 clientPolicy.removeAllowlists(allowlistWrapper);
782             } else { //replace.
783                 clientPolicy.replaceBlocklists(blocklistWrapper);
784                 clientPolicy.replaceAllowlists(allowlistWrapper);
785             }
786             if ((flags & CarPackageManager.FLAG_SET_POLICY_WAIT_FOR_CHANGE) != 0) {
787                 mWaitingPolicies.remove(policy);
788                 mLock.notifyAll();
789             }
790             if (Slogf.isLoggable(TAG, Log.DEBUG)) {
791                 Slogf.d(TAG, "policy set:" + dumpPoliciesLocked(false));
792             }
793         }
794         blockTopActivitiesIfNecessary();
795     }
796 
verifyList(AppBlockingPackageInfo[] list)797     private AppBlockingPackageInfoWrapper[] verifyList(AppBlockingPackageInfo[] list) {
798         if (list == null) {
799             return null;
800         }
801         LinkedList<AppBlockingPackageInfoWrapper> wrappers = new LinkedList<>();
802         for (int i = 0; i < list.length; i++) {
803             AppBlockingPackageInfo info = list[i];
804             if (info == null) {
805                 continue;
806             }
807             boolean isMatching = isInstalledPackageMatching(info);
808             wrappers.add(new AppBlockingPackageInfoWrapper(info, isMatching));
809         }
810         return wrappers.toArray(new AppBlockingPackageInfoWrapper[wrappers.size()]);
811     }
812 
isInstalledPackageMatching(AppBlockingPackageInfo info)813     boolean isInstalledPackageMatching(AppBlockingPackageInfo info) {
814         PackageInfo packageInfo;
815         try {
816             packageInfo = mPackageManager.getPackageInfo(info.packageName,
817                     PackageManager.GET_SIGNATURES);
818         } catch (NameNotFoundException e) {
819             return false;
820         }
821         if (packageInfo == null) {
822             return false;
823         }
824         // if it is system app and client specified the flag, do not check signature
825         if ((info.flags & AppBlockingPackageInfo.FLAG_SYSTEM_APP) == 0 ||
826                 (!PackageManagerHelper.isSystemApp(packageInfo.applicationInfo)
827                         && !PackageManagerHelper.isUpdatedSystemApp(packageInfo.applicationInfo))) {
828             Signature[] signatures = packageInfo.signatures;
829             if (!isAnySignatureMatching(signatures, info.signatures)) {
830                 return false;
831             }
832         }
833         int version = packageInfo.versionCode;
834         if (info.minRevisionCode == 0) {
835             if (info.maxRevisionCode == 0) { // all versions
836                 return true;
837             } else { // only max version matters
838                 return info.maxRevisionCode > version;
839             }
840         } else { // min version matters
841             if (info.maxRevisionCode == 0) {
842                 return info.minRevisionCode < version;
843             } else {
844                 return (info.minRevisionCode < version) && (info.maxRevisionCode > version);
845             }
846         }
847     }
848 
849     /**
850      * Any signature from policy matching with package's signatures is treated as matching.
851      */
isAnySignatureMatching(Signature[] fromPackage, Signature[] fromPolicy)852     boolean isAnySignatureMatching(Signature[] fromPackage, Signature[] fromPolicy) {
853         if (fromPackage == null) {
854             return false;
855         }
856         if (fromPolicy == null) {
857             return false;
858         }
859         ArraySet<Signature> setFromPackage = new ArraySet<Signature>();
860         for (Signature sig : fromPackage) {
861             setFromPackage.add(sig);
862         }
863         for (Signature sig : fromPolicy) {
864             if (setFromPackage.contains(sig)) {
865                 return true;
866             }
867         }
868         return false;
869     }
870 
getPackageInfoWrapperForUser(String packageName, @UserIdInt int userId, Map<String, Set<String>> configAllowlist, Map<String, Set<String>> configBlocklist)871     private AppBlockingPackageInfoWrapper getPackageInfoWrapperForUser(String packageName,
872             @UserIdInt int userId, Map<String, Set<String>> configAllowlist,
873             Map<String, Set<String>> configBlocklist) {
874         PackageInfo info;
875         try {
876             info = PackageManagerHelper.getPackageInfoAsUser(mPackageManager, packageName,
877                     PackageManager.GET_SIGNATURES | PackageManager.GET_ACTIVITIES
878                             | PackageManager.MATCH_DIRECT_BOOT_AWARE
879                             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
880                             | PackageManager.MATCH_DISABLED_COMPONENTS,
881                     userId);
882         } catch (NameNotFoundException e) {
883             Slogf.w(TAG, packageName + " not installed! User Id: " + userId);
884             return null;
885         }
886 
887 
888         if (info == null || info.applicationInfo == null) {
889             return null;
890         }
891 
892         int flags = 0;
893         Set<String> activities = new ArraySet<>();
894 
895         if (PackageManagerHelper.isSystemApp(info.applicationInfo)
896                 || PackageManagerHelper.isUpdatedSystemApp(info.applicationInfo)) {
897             flags = AppBlockingPackageInfo.FLAG_SYSTEM_APP;
898         }
899 
900         /* 1. Check if all or some of this app is in the <activityAllowlist> or
901               <systemActivityAllowlist> in config.xml */
902         Set<String> configActivitiesForPackage = configAllowlist.get(info.packageName);
903         if (configActivitiesForPackage != null) {
904             if (Slogf.isLoggable(TAG, Log.DEBUG)) {
905                 Slogf.d(TAG, info.packageName + " allowlisted");
906             }
907 
908             if (configActivitiesForPackage.isEmpty()) {
909                 // Whole Pkg has been allowlisted
910                 flags |= AppBlockingPackageInfo.FLAG_WHOLE_ACTIVITY;
911                 // Add all activities to the allowlist
912                 List<String> activitiesForPackage = getActivitiesInPackage(info);
913                 if (activitiesForPackage != null) {
914                     activities.addAll(activitiesForPackage);
915                 } else {
916                     if (Slogf.isLoggable(TAG, Log.DEBUG)) {
917                         Slogf.d(TAG, info.packageName + ": Activities null");
918                     }
919                 }
920             } else {
921                 if (Slogf.isLoggable(TAG, Log.DEBUG)) {
922                     Slogf.d(TAG, "Partially Allowlisted. WL Activities: "
923                             + configActivitiesForPackage);
924                 }
925                 activities.addAll(configActivitiesForPackage);
926             }
927         }
928         /* 2. If app is not listed in the config.xml check their Manifest meta-data to
929           see if they have any Distraction Optimized(DO) activities.
930           For non system apps, we check if the app install source was a permittable
931           source. This prevents side-loaded apps to fake DO.  Bypass the check
932           for debug builds for development convenience. */
933         if (!isDebugBuild()
934                 && !PackageManagerHelper.isSystemApp(info.applicationInfo)
935                 && !PackageManagerHelper.isUpdatedSystemApp(info.applicationInfo)) {
936             try {
937                 if (mAllowedAppInstallSources != null) {
938                     String installerName = mPackageManager.getInstallerPackageName(
939                             info.packageName);
940                     if (installerName == null || (installerName != null
941                             && !mAllowedAppInstallSources.contains(installerName))) {
942                         Slogf.w(TAG, info.packageName + " not installed from permitted sources "
943                                 + (installerName == null ? "NULL" : installerName));
944                         return null;
945                     }
946                 }
947             } catch (IllegalArgumentException e) {
948                 Slogf.w(TAG, info.packageName + " not installed!");
949                 return null;
950             }
951         }
952 
953         try {
954             String[] doActivities = findDistractionOptimizedActivitiesAsUser(info.packageName,
955                     userId);
956             if (doActivities != null) {
957                 // Some of the activities in this app are Distraction Optimized.
958                 if (Slogf.isLoggable(TAG, Log.DEBUG)) {
959                     for (String activity : doActivities) {
960                         Slogf.d(TAG, "adding " + activity + " from " + info.packageName
961                                 + " to allowlist");
962                     }
963                 }
964 
965                 activities.addAll(Arrays.asList(doActivities));
966             }
967         } catch (NameNotFoundException e) {
968             Slogf.w(TAG, "Error reading metadata: " + info.packageName);
969             return null;
970         }
971 
972         // Nothing to add to allowlist
973         if (activities.isEmpty()) {
974             return null;
975         }
976 
977         /* 3. Check if parsed activity is in <activityBlocklist> in config.xml. Anything
978               in blocklist should not be allowlisted, either as D.O. or by config. */
979         if (configBlocklist.containsKey(info.packageName)) {
980             Set<String> configBlocklistActivities = configBlocklist.get(info.packageName);
981             if (configBlocklistActivities.isEmpty()) {
982                 // Whole package should be blocklisted.
983                 return null;
984             }
985             activities.removeAll(configBlocklistActivities);
986         }
987 
988         Signature[] signatures;
989         signatures = info.signatures;
990         AppBlockingPackageInfo appBlockingInfo = new AppBlockingPackageInfo(info.packageName,
991                 /* minRevisionCode = */ 0, /* maxRevisionCode = */ 0, flags, signatures,
992                 activities.toArray(new String[activities.size()]));
993         AppBlockingPackageInfoWrapper wrapper = new AppBlockingPackageInfoWrapper(
994                 appBlockingInfo, true);
995         return wrapper;
996     }
997 
998     /**
999      * Update map of allowlisted packages and activities of the form {pkgName, Allowlisted
1000      * activities} and set of denylisted packages. The information can come from a configuration XML
1001      * resource or from the apps marking their activities as distraction optimized.
1002      */
updateActivityAllowlistAndDenylistMap(String packageName)1003     private void updateActivityAllowlistAndDenylistMap(String packageName) {
1004         int userId = mActivityManager.getCurrentUser();
1005         Slogf.d(TAG, "Updating allowlist and denylist mapping for package: " + packageName
1006                 + " for UserId: " + userId);
1007         // Get the apps/activities that are allowlisted in the configuration XML resources.
1008         Map<String, Set<String>> configAllowlist = generateConfigAllowlist();
1009         Map<String, Set<String>> configBlocklist = generateConfigBlocklist();
1010 
1011         AppBlockingPackageInfoWrapper wrapper =
1012                 getPackageInfoWrapperForUser(packageName, userId, configAllowlist, configBlocklist);
1013 
1014         if (wrapper == null && userId != UserHandle.SYSTEM.getIdentifier()) {
1015             Slogf.d(TAG, "Updating allowlist and denylist mapping for package: " + packageName
1016                     + " for UserId: " + UserHandle.SYSTEM.getIdentifier());
1017             // check package for system user, in case package is disabled for current user
1018             wrapper = getPackageInfoWrapperForUser(packageName, UserHandle.SYSTEM.getIdentifier(),
1019                     configAllowlist, configBlocklist);
1020         }
1021 
1022         synchronized (mLock) {
1023             if (wrapper != null) {
1024                 if (DBG) {
1025                     Slogf.d(TAG, "Package: " + packageName + " added in allowlist.");
1026                 }
1027                 mActivityAllowlistMap.put(packageName, wrapper);
1028             } else {
1029                 if (DBG) {
1030                     Slogf.d(TAG, "Package: " + packageName + " added in denylist.");
1031                 }
1032                 mActivityDenylistPackages.add(packageName);
1033             }
1034         }
1035     }
1036 
generateConfigAllowlist()1037     private Map<String, Set<String>> generateConfigAllowlist() {
1038         synchronized (mLock) {
1039             if (mConfiguredAllowlistMap != null) return mConfiguredAllowlistMap;
1040 
1041             Map<String, Set<String>> configAllowlist = new HashMap<>();
1042             mConfiguredAllowlist = mContext.getString(R.string.activityAllowlist);
1043             if (mConfiguredAllowlist == null) {
1044                 Slogf.w(TAG, "Allowlist is null.");
1045             }
1046             parseConfigList(mConfiguredAllowlist, configAllowlist);
1047 
1048             mConfiguredSystemAllowlist = mContext.getString(R.string.systemActivityAllowlist);
1049             if (mConfiguredSystemAllowlist == null) {
1050                 Slogf.w(TAG, "System allowlist is null.");
1051             }
1052             parseConfigList(mConfiguredSystemAllowlist, configAllowlist);
1053 
1054             // Add the blocking overlay activity to the allowlist, since that needs to run in a
1055             // restricted state to communicate the reason an app was blocked.
1056             Set<String> defaultActivity = new ArraySet<>();
1057             if (mActivityBlockingActivity != null) {
1058                 defaultActivity.add(mActivityBlockingActivity.getClassName());
1059                 configAllowlist.put(mActivityBlockingActivity.getPackageName(), defaultActivity);
1060             }
1061 
1062             mConfiguredAllowlistMap = configAllowlist;
1063             return configAllowlist;
1064         }
1065     }
1066 
generateConfigBlocklist()1067     private Map<String, Set<String>> generateConfigBlocklist() {
1068         synchronized (mLock) {
1069             if (mConfiguredBlocklistMap != null) return mConfiguredBlocklistMap;
1070 
1071             Map<String, Set<String>> configBlocklist = new HashMap<>();
1072             mConfiguredBlocklist = mContext.getString(R.string.activityDenylist);
1073             if (mConfiguredBlocklist == null) {
1074                 if (Slogf.isLoggable(TAG, Log.DEBUG)) {
1075                     Slogf.d(TAG, "Null blocklist in config");
1076                 }
1077             }
1078             parseConfigList(mConfiguredBlocklist, configBlocklist);
1079 
1080             mConfiguredBlocklistMap = configBlocklist;
1081             return configBlocklist;
1082         }
1083     }
1084 
isDebugBuild()1085     private boolean isDebugBuild() {
1086         return BuildHelper.isUserDebugBuild() || BuildHelper.isEngBuild();
1087     }
1088 
1089     /**
1090      * Parses the given resource and updates the input map of packages and activities.
1091      *
1092      * Key is package name and value is list of activities. Empty set implies whole package is
1093      * included.
1094      *
1095      * When there are multiple entries regarding one package, the entry with
1096      * greater scope wins. Namely if there were 2 entries such that one allowlists
1097      * an activity, and the other allowlists the entire package of the activity,
1098      * the package is allowlisted, regardless of input order.
1099      */
1100     @VisibleForTesting
parseConfigList(String configList, @NonNull Map<String, Set<String>> packageToActivityMap)1101     /* package */ void parseConfigList(String configList,
1102             @NonNull Map<String, Set<String>> packageToActivityMap) {
1103         if (configList == null) {
1104             return;
1105         }
1106         String[] entries = configList.split(PACKAGE_DELIMITER);
1107         for (String entry : entries) {
1108             String[] packageActivityPair = entry.split(PACKAGE_ACTIVITY_DELIMITER);
1109             Set<String> activities = packageToActivityMap.get(packageActivityPair[0]);
1110             boolean newPackage = false;
1111             if (activities == null) {
1112                 activities = new ArraySet<>();
1113                 newPackage = true;
1114                 packageToActivityMap.put(packageActivityPair[0], activities);
1115             }
1116             if (packageActivityPair.length == 1) { // whole package
1117                 activities.clear();
1118             } else if (packageActivityPair.length == 2) {
1119                 // add class name only when the whole package is not allowlisted.
1120                 if (newPackage || (activities.size() > 0)) {
1121                     activities.add(packageActivityPair[1]);
1122                 }
1123             }
1124         }
1125     }
1126 
1127     @Nullable
getActivitiesInPackage(PackageInfo info)1128     private List<String> getActivitiesInPackage(PackageInfo info) {
1129         if (info == null || info.activities == null) {
1130             return null;
1131         }
1132         List<String> activityList = new ArrayList<>();
1133         for (ActivityInfo aInfo : info.activities) {
1134             activityList.add(aInfo.name);
1135         }
1136         return activityList;
1137     }
1138 
1139     /**
1140      * Checks if there are any {@link CarAppBlockingPolicyService} and creates a proxy to
1141      * bind to them and retrieve the {@link CarAppBlockingPolicy}
1142      */
1143     @VisibleForTesting
startAppBlockingPolicies()1144     public void startAppBlockingPolicies() {
1145         Intent policyIntent = new Intent();
1146         policyIntent.setAction(CarAppBlockingPolicyService.SERVICE_INTERFACE);
1147         List<ResolveInfo> policyInfos = mPackageManager.queryIntentServices(policyIntent, 0);
1148         if (policyInfos == null) { //no need to wait for service binding and retrieval.
1149             return;
1150         }
1151         LinkedList<AppBlockingPolicyProxy> proxies = new LinkedList<>();
1152         for (ResolveInfo resolveInfo : policyInfos) {
1153             ServiceInfo serviceInfo = resolveInfo.serviceInfo;
1154             if (serviceInfo == null) {
1155                 continue;
1156             }
1157             if (serviceInfo.isEnabled()) {
1158                 if (mPackageManager.checkPermission(Car.PERMISSION_CONTROL_APP_BLOCKING,
1159                         serviceInfo.packageName) != PackageManager.PERMISSION_GRANTED) {
1160                     continue;
1161                 }
1162                 Slogf.i(TAG, "found policy holding service:" + serviceInfo);
1163                 AppBlockingPolicyProxy proxy = new AppBlockingPolicyProxy(this, mContext,
1164                         serviceInfo);
1165                 proxy.connect();
1166                 proxies.add(proxy);
1167             }
1168         }
1169         synchronized (mLock) {
1170             mProxies = proxies;
1171         }
1172     }
1173 
onPolicyConnectionAndSet(AppBlockingPolicyProxy proxy, CarAppBlockingPolicy policy)1174     public void onPolicyConnectionAndSet(AppBlockingPolicyProxy proxy,
1175             CarAppBlockingPolicy policy) {
1176         doHandlePolicyConnection(proxy, policy);
1177     }
1178 
onPolicyConnectionFailure(AppBlockingPolicyProxy proxy)1179     public void onPolicyConnectionFailure(AppBlockingPolicyProxy proxy) {
1180         doHandlePolicyConnection(proxy, null);
1181     }
1182 
doHandlePolicyConnection(AppBlockingPolicyProxy proxy, CarAppBlockingPolicy policy)1183     private void doHandlePolicyConnection(AppBlockingPolicyProxy proxy,
1184             CarAppBlockingPolicy policy) {
1185         synchronized (mLock) {
1186             if (mProxies == null) {
1187                 proxy.disconnect();
1188                 return;
1189             }
1190             mProxies.remove(proxy);
1191             if (mProxies.size() == 0) {
1192                 mProxies = null;
1193             }
1194         }
1195         try {
1196             if (policy != null) {
1197                 if (Slogf.isLoggable(TAG, Log.DEBUG)) {
1198                     Slogf.d(TAG, "policy setting from policy service:" + proxy.getPackageName());
1199                 }
1200                 doSetAppBlockingPolicy(proxy.getPackageName(), policy, 0);
1201             }
1202         } finally {
1203             proxy.disconnect();
1204         }
1205     }
1206 
1207     @Override
1208     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)1209     public void dump(IndentingPrintWriter writer) {
1210         synchronized (mLock) {
1211             writer.println("*CarPackageManagerService*");
1212             writer.println("mEnableActivityBlocking:" + mEnableActivityBlocking);
1213             writer.println("mPreventTemplatedAppsFromShowingDialog:"
1214                     + mPreventTemplatedAppsFromShowingDialog);
1215             writer.println("mTemplateActivityClassName:" + mTemplateActivityClassName);
1216             List<String> restrictions = new ArrayList<>(mUxRestrictionsListeners.size());
1217             for (int i = 0; i < mUxRestrictionsListeners.size(); i++) {
1218                 int displayId = mUxRestrictionsListeners.keyAt(i);
1219                 UxRestrictionsListener listener = mUxRestrictionsListeners.valueAt(i);
1220                 restrictions.add(String.format("Display %d is %s",
1221                         displayId, (listener.isRestricted() ? "restricted" : "unrestricted")));
1222             }
1223             writer.println("Display Restrictions:\n" + String.join("\n", restrictions));
1224             writer.println(" Blocked activity log:");
1225             mBlockedActivityLogs.dump(writer);
1226             writer.print(dumpPoliciesLocked(true));
1227             writer.print("mCurrentDrivingSafetyRegion:");
1228             writer.println(mCurrentDrivingSafetyRegion);
1229             writer.print("mTempAllowedActivities:");
1230             writer.println(mTempAllowedActivities);
1231             writer.println("Car service overlay packages property name: "
1232                     + PackageManagerHelper.PROPERTY_CAR_SERVICE_OVERLAY_PACKAGES);
1233             writer.println("Car service overlay packages: "
1234                     + SystemProperties.get(
1235                             PackageManagerHelper.PROPERTY_CAR_SERVICE_OVERLAY_PACKAGES,
1236                             /* default= */ null));
1237         }
1238     }
1239 
1240     @GuardedBy("mLock")
dumpPoliciesLocked(boolean dumpAll)1241     private String dumpPoliciesLocked(boolean dumpAll) {
1242         StringBuilder sb = new StringBuilder();
1243         if (dumpAll) {
1244             sb.append("**System allowlist**\n");
1245             for (AppBlockingPackageInfoWrapper wrapper : mActivityAllowlistMap.values()) {
1246                 sb.append(wrapper.toString() + "\n");
1247             }
1248         }
1249         sb.append("**Client Policies**\n");
1250         for (Entry<String, ClientPolicy> entry : mClientPolicies.entrySet()) {
1251             sb.append("Client:" + entry.getKey() + "\n");
1252             sb.append("  allowlists:\n");
1253             for (AppBlockingPackageInfoWrapper wrapper : entry.getValue().mAllowlistsMap.values()) {
1254                 sb.append(wrapper.toString() + "\n");
1255             }
1256             sb.append("  blocklists:\n");
1257             for (AppBlockingPackageInfoWrapper wrapper : entry.getValue().mBlocklistsMap.values()) {
1258                 sb.append(wrapper.toString() + "\n");
1259             }
1260         }
1261         sb.append("**Unprocessed policy services**\n");
1262         if (mProxies != null) {
1263             for (AppBlockingPolicyProxy proxy : mProxies) {
1264                 sb.append(proxy.toString() + "\n");
1265             }
1266         }
1267         sb.append("**Allowlist string in resource**\n");
1268         sb.append(mConfiguredAllowlist + "\n");
1269 
1270         sb.append("**System allowlist string in resource**\n");
1271         sb.append(mConfiguredSystemAllowlist + "\n");
1272 
1273         sb.append("**Blocklist string in resource**\n");
1274         sb.append(mConfiguredBlocklist + "\n");
1275 
1276         sb.append("**Allowlist map from resource**\n");
1277         sb.append(mConfiguredAllowlistMap + "\n");
1278 
1279         sb.append("**Blocklist from resource**\n");
1280         sb.append(mConfiguredBlocklist + "\n");
1281 
1282         return sb.toString();
1283     }
1284 
1285     /**
1286      * Returns whether UX restrictions is required for display.
1287      *
1288      * Non-physical display will use restrictions for {@link Display#DEFAULT_DISPLAY}.
1289      */
isUxRestrictedOnDisplay(int displayId)1290     private boolean isUxRestrictedOnDisplay(int displayId) {
1291         UxRestrictionsListener listenerForTopTaskDisplay;
1292         if (mUxRestrictionsListeners.indexOfKey(displayId) < 0) {
1293             listenerForTopTaskDisplay = mUxRestrictionsListeners.get(Display.DEFAULT_DISPLAY);
1294             if (listenerForTopTaskDisplay == null) {
1295                 // This should never happen.
1296                 Slogf.e(TAG, "Missing listener for default display.");
1297                 return true;
1298             }
1299         } else {
1300             listenerForTopTaskDisplay = mUxRestrictionsListeners.get(displayId);
1301         }
1302 
1303         return listenerForTopTaskDisplay.isRestricted();
1304     }
1305 
blockTopActivitiesIfNecessary()1306     private void blockTopActivitiesIfNecessary() {
1307         List<? extends TaskInfo> visibleTasks = mActivityService.getVisibleTasks();
1308         mBlockedDisplayIds.clear();
1309         for (TaskInfo topTask : visibleTasks) {
1310             if (topTask == null) {
1311                 Slogf.e(TAG, "Top tasks contains null.");
1312                 continue;
1313             }
1314 
1315             int displayIdOfTask = TaskInfoHelper.getDisplayId(topTask);
1316             if (mBlockedDisplayIds.indexOfKey(displayIdOfTask) != -1) {
1317                 if (DBG) {
1318                     Slogf.d(TAG, "This display has already been blocked.");
1319                 }
1320                 continue;
1321             }
1322 
1323             boolean blocked = blockTopActivityIfNecessary(topTask);
1324             if (blocked) {
1325                 mBlockedDisplayIds.append(displayIdOfTask, true);
1326             }
1327         }
1328     }
1329 
1330     /**
1331      * @return {@code True} if the {@code topTask} was blocked, {@code False} otherwise.
1332      */
blockTopActivityIfNecessary(TaskInfo topTask)1333     private boolean blockTopActivityIfNecessary(TaskInfo topTask) {
1334         int displayId = TaskInfoHelper.getDisplayId(topTask);
1335         synchronized (mLock) {
1336             if (!Objects.equals(mActivityBlockingActivity, topTask.topActivity)
1337                     && mTopActivityWithDialogPerDisplay.contains(displayId)
1338                     && !topTask.topActivity.equals(
1339                             mTopActivityWithDialogPerDisplay.get(displayId))) {
1340                 // Clear top activity-with-dialog if the activity has changed on this display.
1341                 mTopActivityWithDialogPerDisplay.remove(displayId);
1342             }
1343         }
1344         if (isUxRestrictedOnDisplay(displayId)) {
1345             return doBlockTopActivityIfNotAllowed(displayId, topTask);
1346         }
1347         return false;
1348     }
1349 
1350     /**
1351      * @return {@code True} if the {@code topTask} was blocked, {@code False} otherwise.
1352      */
doBlockTopActivityIfNotAllowed(int displayId, TaskInfo topTask)1353     private boolean doBlockTopActivityIfNotAllowed(int displayId, TaskInfo topTask) {
1354         if (topTask.topActivity == null) {
1355             return false;
1356         }
1357         if (topTask.topActivity.equals(mActivityBlockingActivity)) {
1358             mBlockingActivityLaunchTimes.put(displayId, 0);
1359             mBlockingActivityTargets.put(displayId, null);
1360             return false;
1361         }
1362         boolean allowed = isActivityAllowed(topTask);
1363         if (Slogf.isLoggable(TAG, Log.DEBUG)) {
1364             Slogf.d(TAG, "new activity:" + topTask.toString() + " allowed:" + allowed);
1365         }
1366         if (allowed) {
1367             return false;
1368         }
1369         if (!mEnableActivityBlocking) {
1370             Slogf.d(TAG, "Current activity " + topTask.topActivity
1371                     + " not allowed, blocking disabled.");
1372             return false;
1373         }
1374         if (Slogf.isLoggable(TAG, Log.DEBUG)) {
1375             Slogf.d(TAG, "Current activity " + topTask.topActivity
1376                     + " not allowed, will block.");
1377         }
1378         // TaskMonitor based on TaskOrganizer reflects only the actually launched tasks,
1379         // (TaskStackChangeListener reflects the internal state of ActivityTaskManagerService)
1380         // So it takes sometime to recognize the ActivityBlockingActivity is shown.
1381         // This guard is to prevent from launching ABA repeatedly until it is shown.
1382         ComponentName blockingActivityTarget = mBlockingActivityTargets.get(displayId);
1383         if (topTask.topActivity.equals(blockingActivityTarget)) {
1384             long blockingActivityLaunchTime = mBlockingActivityLaunchTimes.get(displayId);
1385             if (SystemClock.uptimeMillis() - blockingActivityLaunchTime < ABA_LAUNCH_TIMEOUT_MS) {
1386                 Slogf.d(TAG, "Waiting for BlockingActivity to be shown: displayId=%d", displayId);
1387                 return false;
1388             }
1389         }
1390 
1391         // Figure out the root task of blocked task.
1392         ComponentName rootTaskActivityName = topTask.baseActivity;
1393 
1394         boolean isRootDO = false;
1395         if (rootTaskActivityName != null) {
1396             isRootDO = isActivityDistractionOptimized(
1397                     rootTaskActivityName.getPackageName(), rootTaskActivityName.getClassName());
1398         }
1399 
1400         Intent newActivityIntent = createBlockingActivityIntent(
1401                 mActivityBlockingActivity, TaskInfoHelper.getDisplayId(topTask),
1402                 topTask.topActivity.flattenToShortString(), topTask.taskId,
1403                 rootTaskActivityName.flattenToString(), isRootDO);
1404 
1405         // Intent contains all info to debug what is blocked - log into both logcat and dumpsys.
1406         String log = "Starting blocking activity with intent: " + newActivityIntent.toUri(0);
1407         if (Slogf.isLoggable(TAG, Log.INFO)) {
1408             Slogf.i(TAG, log);
1409         }
1410         mBlockedActivityLogs.log(log);
1411         mBlockingActivityLaunchTimes.put(displayId, SystemClock.uptimeMillis());
1412         mBlockingActivityTargets.put(displayId, topTask.topActivity);
1413         mActivityService.blockActivity(topTask, newActivityIntent);
1414         return true;
1415     }
1416 
isActivityAllowed(TaskInfo topTaskInfoContainer)1417     private boolean isActivityAllowed(TaskInfo topTaskInfoContainer) {
1418         ComponentName activityName = topTaskInfoContainer.topActivity;
1419         boolean isDistractionOptimized = isActivityDistractionOptimized(
1420                 activityName.getPackageName(),
1421                 activityName.getClassName());
1422         if (!isDistractionOptimized) {
1423             return false;
1424         }
1425         return !(mPreventTemplatedAppsFromShowingDialog
1426                 && isTemplateActivity(activityName)
1427                 && isActivityShowingADialogOnDisplay(activityName,
1428                         TaskInfoHelper.getDisplayId(topTaskInfoContainer)));
1429     }
1430 
isTemplateActivity(ComponentName activityName)1431     private boolean isTemplateActivity(ComponentName activityName) {
1432         // TODO(b/191263486): Finalise on how to detect the templated activities.
1433         return activityName.getClassName().equals(mTemplateActivityClassName);
1434     }
1435 
isActivityShowingADialogOnDisplay(ComponentName activityName, int displayId)1436     private boolean isActivityShowingADialogOnDisplay(ComponentName activityName, int displayId) {
1437         String output = dumpWindows();
1438         List<WindowDumpParser.Window> appWindows =
1439                 WindowDumpParser.getParsedAppWindows(output, activityName.getPackageName());
1440         // TODO(b/192354699): Handle case where an activity can have multiple instances on the same
1441         //  display.
1442         int totalAppWindows = appWindows.size();
1443         String firstActivityRecord = null;
1444         int numTopActivityAppWindowsOnDisplay = 0;
1445         for (int i = 0; i < totalAppWindows; i++) {
1446             WindowDumpParser.Window appWindow = appWindows.get(i);
1447             if (appWindow.getDisplayId() != displayId) {
1448                 continue;
1449             }
1450             if (TextUtils.isEmpty(appWindow.getActivityRecord())) {
1451                 continue;
1452             }
1453             if (firstActivityRecord == null) {
1454                 firstActivityRecord = appWindow.getActivityRecord();
1455             }
1456             if (firstActivityRecord.equals(appWindow.getActivityRecord())) {
1457                 numTopActivityAppWindowsOnDisplay++;
1458             }
1459         }
1460         Slogf.d(TAG, "Top activity =  " + activityName);
1461         Slogf.d(TAG, "Number of app widows of top activity = " + numTopActivityAppWindowsOnDisplay);
1462         boolean isShowingADialog = numTopActivityAppWindowsOnDisplay > 1;
1463         synchronized (mLock) {
1464             if (isShowingADialog) {
1465                 mTopActivityWithDialogPerDisplay.put(displayId, activityName);
1466             } else {
1467                 mTopActivityWithDialogPerDisplay.remove(displayId);
1468             }
1469         }
1470         return isShowingADialog;
1471     }
1472 
dumpWindows()1473     private String dumpWindows() {
1474         try {
1475             ParcelFileDescriptor[] fileDescriptors = ParcelFileDescriptor.createSocketPair();
1476             mWindowManagerBinder.dump(
1477                     fileDescriptors[0].getFileDescriptor(), WINDOW_DUMP_ARGUMENTS);
1478             fileDescriptors[0].close();
1479             StringBuilder outputBuilder = new StringBuilder();
1480             try (BufferedReader reader = new BufferedReader(
1481                     new FileReader(fileDescriptors[1].getFileDescriptor()))) {
1482                 String line;
1483                 while ((line = reader.readLine()) != null) {
1484                     outputBuilder.append(line).append("\n");
1485                 }
1486             }
1487             fileDescriptors[1].close();
1488             return outputBuilder.toString();
1489         } catch (IOException | RemoteException e) {
1490             throw new RuntimeException(e);
1491         }
1492     }
1493 
1494     /**
1495      * Creates an intent to start blocking activity.
1496      *
1497      * @param blockingActivity the activity to launch
1498      * @param blockedActivity  the activity being blocked
1499      * @param blockedTaskId    the blocked task id, which contains the blocked activity
1500      * @param taskRootActivity root activity of the blocked task
1501      * @param isRootDo         denotes if the root activity is distraction optimised
1502      * @return an intent to launch the blocking activity.
1503      */
createBlockingActivityIntent(ComponentName blockingActivity, int displayId, String blockedActivity, int blockedTaskId, String taskRootActivity, boolean isRootDo)1504     private static Intent createBlockingActivityIntent(ComponentName blockingActivity,
1505             int displayId, String blockedActivity, int blockedTaskId, String taskRootActivity,
1506             boolean isRootDo) {
1507         Intent newActivityIntent = new Intent();
1508         newActivityIntent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
1509         newActivityIntent.setComponent(blockingActivity);
1510         newActivityIntent.putExtra(
1511                 BLOCKING_INTENT_EXTRA_DISPLAY_ID, displayId);
1512         newActivityIntent.putExtra(
1513                 BLOCKING_INTENT_EXTRA_BLOCKED_ACTIVITY_NAME, blockedActivity);
1514         newActivityIntent.putExtra(
1515                 BLOCKING_INTENT_EXTRA_BLOCKED_TASK_ID, blockedTaskId);
1516         newActivityIntent.putExtra(
1517                 BLOCKING_INTENT_EXTRA_ROOT_ACTIVITY_NAME, taskRootActivity);
1518         newActivityIntent.putExtra(
1519                 BLOCKING_INTENT_EXTRA_IS_ROOT_ACTIVITY_DO, isRootDo);
1520 
1521         return newActivityIntent;
1522     }
1523 
1524     /**
1525      * Enable/Disable activity blocking by correspondingly enabling/disabling broadcasting UXR
1526      * changes in {@link CarUxRestrictionsManagerService}. This is only available in
1527      * engineering builds for development convenience.
1528      */
1529     @Override
setEnableActivityBlocking(boolean enable)1530     public void setEnableActivityBlocking(boolean enable) {
1531         if (!isDebugBuild()) {
1532             Slogf.e(TAG, "Cannot enable/disable activity blocking");
1533             return;
1534         }
1535 
1536         // Check if the caller has the same signature as that of the car service.
1537         if (mPackageManager.checkSignatures(Process.myUid(), Binder.getCallingUid())
1538                 != PackageManager.SIGNATURE_MATCH) {
1539             throw new SecurityException(
1540                     "Caller " + mPackageManager.getNameForUid(Binder.getCallingUid())
1541                     + " does not have the right signature");
1542         }
1543         mCarUxRestrictionsService.setUxRChangeBroadcastEnabled(enable);
1544     }
1545 
1546     @Override
getTargetCarVersion(String packageName)1547     public CarVersion getTargetCarVersion(String packageName) {
1548         return getTargetCarVersion(Binder.getCallingUserHandle(), packageName);
1549     }
1550 
1551     @Override
getSelfTargetCarVersion(String packageName)1552     public CarVersion getSelfTargetCarVersion(String packageName) {
1553         Utils.checkCalledByPackage(mContext, packageName);
1554 
1555         return getTargetCarVersion(Binder.getCallingUserHandle(), packageName);
1556     }
1557 
1558     /**
1559      * Public, as it's also used by {@code ICarImpl}.
1560      */
getTargetCarVersion(UserHandle user, String packageName)1561     public CarVersion getTargetCarVersion(UserHandle user, String packageName) {
1562         Context context = mContext.createContextAsUser(user, /* flags= */ 0);
1563         return getTargetCarVersion(context, packageName);
1564     }
1565 
1566     /**
1567      * Used by {@code CarShellCommand} as well.
1568      */
1569     @Nullable
getTargetCarVersion(Context context, String packageName)1570     public static CarVersion getTargetCarVersion(Context context, String packageName) {
1571         String permission = android.Manifest.permission.QUERY_ALL_PACKAGES;
1572         if (context.checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
1573             Slogf.w(TAG, "getTargetCarVersion(%s): UID %d doesn't have %s permission",
1574                     packageName, Binder.getCallingUid(), permission);
1575             throw new SecurityException("requires permission " + permission);
1576         }
1577         ApplicationInfo info = null;
1578         try {
1579             info = context.getPackageManager().getApplicationInfo(packageName,
1580                     PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA));
1581         } catch (NameNotFoundException e) {
1582             if (DBG) {
1583                 Slogf.d(TAG, "getTargetCarVersion(%s, %s): not found: %s", context.getUser(),
1584                         packageName, e);
1585             }
1586             throw new ServiceSpecificException(CarPackageManager.ERROR_CODE_NO_PACKAGE,
1587                     e.getMessage());
1588         }
1589         return CarVersionParser.getTargetCarVersion(info);
1590     }
1591 
1592     /**
1593      * Get the distraction optimized activities for the given package.
1594      *
1595      * @param pkgName Name of the package
1596      * @return Array of the distraction optimized activities in the package
1597      */
1598     @Nullable
getDistractionOptimizedActivities(String pkgName)1599     public String[] getDistractionOptimizedActivities(String pkgName) {
1600         try {
1601             return findDistractionOptimizedActivitiesAsUser(pkgName,
1602                     mActivityManager.getCurrentUser());
1603         } catch (NameNotFoundException e) {
1604             return null;
1605         }
1606     }
1607 
findDistractionOptimizedActivitiesAsUser(String pkgName, int userId)1608     private String[] findDistractionOptimizedActivitiesAsUser(String pkgName, int userId)
1609             throws NameNotFoundException {
1610         String regionString;
1611         synchronized (mLock) {
1612             regionString = mCurrentDrivingSafetyRegion;
1613         }
1614         return CarAppMetadataReader.findDistractionOptimizedActivitiesAsUser(mContext, pkgName,
1615                 userId, regionString);
1616     }
1617 
1618     /**
1619      * Reading policy and setting policy can take time. Run it in a separate handler thread.
1620      */
1621     private static final class PackageHandler extends Handler {
1622         private static final String TAG = CarLog.tagFor(CarPackageManagerService.class);
1623 
1624         private static final int MSG_INIT = 0;
1625         private static final int MSG_PARSE_PKG = 1;
1626         private static final int MSG_UPDATE_POLICY = 2;
1627         private static final int MSG_RELEASE = 3;
1628 
1629         private final WeakReference<CarPackageManagerService> mService;
1630 
PackageHandler(Looper looper, CarPackageManagerService service)1631         private PackageHandler(Looper looper, CarPackageManagerService service) {
1632             super(looper);
1633             mService = new WeakReference<CarPackageManagerService>(service);
1634         }
1635 
requestInit()1636         private void requestInit() {
1637             Message msg = obtainMessage(MSG_INIT);
1638             sendMessage(msg);
1639         }
1640 
requestRelease()1641         private void requestRelease() {
1642             removeMessages(MSG_UPDATE_POLICY);
1643             Message msg = obtainMessage(MSG_RELEASE);
1644             sendMessage(msg);
1645         }
1646 
requestUpdatingPolicy(String packageName, CarAppBlockingPolicy policy, int flags)1647         private void requestUpdatingPolicy(String packageName, CarAppBlockingPolicy policy,
1648                 int flags) {
1649             Pair<String, CarAppBlockingPolicy> pair = new Pair<>(packageName, policy);
1650             Message msg = obtainMessage(MSG_UPDATE_POLICY, flags, 0, pair);
1651             sendMessage(msg);
1652         }
1653 
requestParsingInstalledPkg(String packageName)1654         private void requestParsingInstalledPkg(String packageName) {
1655             Message msg = obtainMessage(MSG_PARSE_PKG, packageName);
1656             sendMessage(msg);
1657         }
1658 
1659         @Override
handleMessage(Message msg)1660         public void handleMessage(Message msg) {
1661             CarPackageManagerService service = mService.get();
1662             if (service == null) {
1663                 Slogf.i(TAG, "handleMessage null service");
1664                 return;
1665             }
1666             switch (msg.what) {
1667                 case MSG_INIT:
1668                     service.doHandleInit();
1669                     break;
1670                 case MSG_PARSE_PKG:
1671                     service.doParseInstalledPackage((String) msg.obj);
1672                     break;
1673                 case MSG_UPDATE_POLICY:
1674                     Pair<String, CarAppBlockingPolicy> pair =
1675                             (Pair<String, CarAppBlockingPolicy>) msg.obj;
1676                     service.doUpdatePolicy(pair.first, pair.second, msg.arg1);
1677                     break;
1678                 case MSG_RELEASE:
1679                     service.doHandleRelease();
1680                     break;
1681             }
1682         }
1683     }
1684 
1685     private static class AppBlockingPackageInfoWrapper {
1686         private final AppBlockingPackageInfo info;
1687         /**
1688          * Whether the current info is matching with the target package in system. Mismatch can
1689          * happen for version out of range or signature mismatch.
1690          */
1691         private boolean isMatching;
1692 
AppBlockingPackageInfoWrapper(AppBlockingPackageInfo info, boolean isMatching)1693         private AppBlockingPackageInfoWrapper(AppBlockingPackageInfo info, boolean isMatching) {
1694             this.info = info;
1695             this.isMatching = isMatching;
1696         }
1697 
1698         @Override
toString()1699         public String toString() {
1700             return "AppBlockingPackageInfoWrapper [info=" + info + ", isMatching=" + isMatching +
1701                     "]";
1702         }
1703     }
1704 
1705     /**
1706      * Client policy holder per each client. Should be accessed with CarpackageManagerService.this
1707      * held.
1708      */
1709     private static class ClientPolicy {
1710         private final HashMap<String, AppBlockingPackageInfoWrapper> mAllowlistsMap =
1711                 new HashMap<>();
1712         private final HashMap<String, AppBlockingPackageInfoWrapper> mBlocklistsMap =
1713                 new HashMap<>();
1714 
replaceAllowlists(AppBlockingPackageInfoWrapper[] allowlists)1715         private void replaceAllowlists(AppBlockingPackageInfoWrapper[] allowlists) {
1716             mAllowlistsMap.clear();
1717             addToAllowlists(allowlists);
1718         }
1719 
addToAllowlists(AppBlockingPackageInfoWrapper[] allowlists)1720         private void addToAllowlists(AppBlockingPackageInfoWrapper[] allowlists) {
1721             if (allowlists == null) {
1722                 return;
1723             }
1724             for (AppBlockingPackageInfoWrapper wrapper : allowlists) {
1725                 if (wrapper != null) {
1726                     mAllowlistsMap.put(wrapper.info.packageName, wrapper);
1727                 }
1728             }
1729         }
1730 
removeAllowlists(AppBlockingPackageInfoWrapper[] allowlists)1731         private void removeAllowlists(AppBlockingPackageInfoWrapper[] allowlists) {
1732             if (allowlists == null) {
1733                 return;
1734             }
1735             for (AppBlockingPackageInfoWrapper wrapper : allowlists) {
1736                 if (wrapper != null) {
1737                     mAllowlistsMap.remove(wrapper.info.packageName);
1738                 }
1739             }
1740         }
1741 
replaceBlocklists(AppBlockingPackageInfoWrapper[] blocklists)1742         private void replaceBlocklists(AppBlockingPackageInfoWrapper[] blocklists) {
1743             mBlocklistsMap.clear();
1744             addToBlocklists(blocklists);
1745         }
1746 
addToBlocklists(AppBlockingPackageInfoWrapper[] blocklists)1747         private void addToBlocklists(AppBlockingPackageInfoWrapper[] blocklists) {
1748             if (blocklists == null) {
1749                 return;
1750             }
1751             for (AppBlockingPackageInfoWrapper wrapper : blocklists) {
1752                 if (wrapper != null) {
1753                     mBlocklistsMap.put(wrapper.info.packageName, wrapper);
1754                 }
1755             }
1756         }
1757 
removeBlocklists(AppBlockingPackageInfoWrapper[] blocklists)1758         private void removeBlocklists(AppBlockingPackageInfoWrapper[] blocklists) {
1759             if (blocklists == null) {
1760                 return;
1761             }
1762             for (AppBlockingPackageInfoWrapper wrapper : blocklists) {
1763                 if (wrapper != null) {
1764                     mBlocklistsMap.remove(wrapper.info.packageName);
1765                 }
1766             }
1767         }
1768     }
1769 
1770     private class ActivityLaunchListener implements CarActivityService.ActivityLaunchListener {
1771         @Override
onActivityLaunch(TaskInfo topTask)1772         public void onActivityLaunch(TaskInfo topTask) {
1773             if (topTask == null) {
1774                 Slogf.e(TAG, "Received callback with null top task.");
1775                 return;
1776             }
1777             blockTopActivityIfNecessary(topTask);
1778         }
1779     }
1780 
1781     /**
1782      * Listens to the UX restrictions from {@link CarUxRestrictionsManagerService} and initiates
1783      * checking if the foreground Activity should be blocked.
1784      */
1785     private class UxRestrictionsListener extends ICarUxRestrictionsChangeListener.Stub {
1786         @GuardedBy("mLock")
1787         @Nullable
1788         private CarUxRestrictions mCurrentUxRestrictions;
1789         private final CarUxRestrictionsManagerService uxRestrictionsService;
1790 
UxRestrictionsListener(CarUxRestrictionsManagerService service)1791         public UxRestrictionsListener(CarUxRestrictionsManagerService service) {
1792             uxRestrictionsService = service;
1793         }
1794 
1795         @Override
onUxRestrictionsChanged(CarUxRestrictions restrictions)1796         public void onUxRestrictionsChanged(CarUxRestrictions restrictions) {
1797             if (Slogf.isLoggable(TAG, Log.DEBUG)) {
1798                 Slogf.d(TAG, "Received uxr restrictions: "
1799                         + restrictions.isRequiresDistractionOptimization() + " : "
1800                         + restrictions.getActiveRestrictions());
1801             }
1802 
1803             synchronized (mLock) {
1804                 mCurrentUxRestrictions = new CarUxRestrictions(restrictions);
1805             }
1806             checkIfTopActivityNeedsBlocking();
1807         }
1808 
checkIfTopActivityNeedsBlocking()1809         private void checkIfTopActivityNeedsBlocking() {
1810             boolean shouldCheck = false;
1811             synchronized (mLock) {
1812                 if (mCurrentUxRestrictions != null
1813                         && mCurrentUxRestrictions.isRequiresDistractionOptimization()) {
1814                     shouldCheck = true;
1815                 }
1816             }
1817             if (Slogf.isLoggable(TAG, Log.DEBUG)) {
1818                 Slogf.d(TAG, "Should check top tasks?: " + shouldCheck);
1819             }
1820             if (shouldCheck) {
1821                 // Loop over all top tasks to ensure tasks on virtual display can also be blocked.
1822                 blockTopActivitiesIfNecessary();
1823             }
1824         }
1825 
isRestricted()1826         private boolean isRestricted() {
1827             // if current restrictions is null, try querying the service, once.
1828             synchronized (mLock) {
1829                 if (mCurrentUxRestrictions == null) {
1830                     mCurrentUxRestrictions = uxRestrictionsService.getCurrentUxRestrictions();
1831                 }
1832                 if (mCurrentUxRestrictions != null) {
1833                     return mCurrentUxRestrictions.isRequiresDistractionOptimization();
1834                 }
1835             }
1836 
1837             // If restriction information is still not available (could happen during bootup),
1838             // return not restricted.  This maintains parity with previous implementation but needs
1839             // a revisit as we test more.
1840             return false;
1841         }
1842     }
1843 
1844     /**
1845      * Called when a window change event is received by the {@link CarSafetyAccessibilityService}.
1846      */
1847     @VisibleForTesting
onWindowChangeEvent(@onNull AccessibilityEvent event)1848     void onWindowChangeEvent(@NonNull AccessibilityEvent event) {
1849         Slogf.d(TAG, "onWindowChange event received");
1850         boolean receivedFromActivityBlockingActivity =
1851                 event.getPackageName() != null && event.getClassName() != null
1852                         && mActivityBlockingActivity.getPackageName().contentEquals(
1853                         event.getPackageName())
1854                         && mActivityBlockingActivity.getClassName().contentEquals(
1855                         event.getClassName());
1856         if (!receivedFromActivityBlockingActivity) {
1857             mHandler.post(() -> blockTopActivitiesIfNecessary());
1858         } else {
1859             Slogf.d(TAG, "Discarded onWindowChangeEvent received from "
1860                     + "ActivityBlockingActivity");
1861         }
1862     }
1863 
1864     /**
1865      * Listens to the package install/uninstall events to know when to initiate parsing
1866      * installed packages.
1867      */
1868     private class PackageParsingEventReceiver extends BroadcastReceiver {
1869         @Override
onReceive(Context context, Intent intent)1870         public void onReceive(Context context, Intent intent) {
1871             if (intent == null || intent.getAction() == null) {
1872                 return;
1873             }
1874             if (Slogf.isLoggable(TAG, Log.DEBUG)) {
1875                 Slogf.d(TAG, "PackageParsingEventReceiver Received " + intent.getAction());
1876             }
1877             String action = intent.getAction();
1878             if (isPackageManagerAction(action)) {
1879                 // send a delayed message so if we received multiple related intents, we parse
1880                 // only once.
1881                 logEventChange(intent);
1882                 String packageName = getPackageName(intent);
1883                 mHandler.requestParsingInstalledPkg(packageName);
1884             }
1885         }
1886 
getPackageName(Intent intent)1887         private String getPackageName(Intent intent) {
1888             // For mPackageManagerActions, data should contain package name.
1889             String dataString = intent.getDataString();
1890             if (dataString == null) return null;
1891 
1892             String scheme = intent.getScheme();
1893             if (!scheme.equals("package")) return null;
1894 
1895             String[] splitData = intent.getDataString().split(":");
1896             if (splitData.length < 2) return null;
1897 
1898             return splitData[1];
1899         }
1900 
isPackageManagerAction(String action)1901         private boolean isPackageManagerAction(String action) {
1902             return mPackageManagerActions.contains(action);
1903         }
1904 
1905         /**
1906          * Convenience log function to log what changed.  Logs only when more debug logs
1907          * are needed - DBG_POLICY_CHECK needs to be true
1908          */
logEventChange(Intent intent)1909         private void logEventChange(Intent intent) {
1910             if (intent == null) {
1911                 return;
1912             }
1913             if (Slogf.isLoggable(TAG, Log.DEBUG)) {
1914                 String packageName = intent.getData().getSchemeSpecificPart();
1915                 Slogf.d(TAG, "Pkg Changed:" + packageName);
1916                 String action = intent.getAction();
1917                 if (action == null) {
1918                     return;
1919                 }
1920                 if (action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
1921                     Slogf.d(TAG, "Changed components");
1922                     String[] cc = intent
1923                             .getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
1924                     if (cc != null) {
1925                         for (String c : cc) {
1926                             Slogf.d(TAG, c);
1927                         }
1928                     }
1929                 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
1930                         || action.equals(Intent.ACTION_PACKAGE_ADDED)) {
1931                     Slogf.d(TAG, action + " Replacing?: "
1932                             + intent.getBooleanExtra(Intent.EXTRA_REPLACING, false));
1933                 }
1934             }
1935         }
1936     }
1937 }
1938