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