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 android.annotation.Nullable; 20 import android.app.ActivityManager.StackInfo; 21 import android.car.Car; 22 import android.car.content.pm.AppBlockingPackageInfo; 23 import android.car.content.pm.CarAppBlockingPolicy; 24 import android.car.content.pm.CarAppBlockingPolicyService; 25 import android.car.content.pm.CarPackageManager; 26 import android.car.content.pm.ICarPackageManager; 27 import android.car.drivingstate.CarUxRestrictions; 28 import android.car.drivingstate.ICarUxRestrictionsChangeListener; 29 import android.content.BroadcastReceiver; 30 import android.content.ComponentName; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.pm.ActivityInfo; 35 import android.content.pm.PackageInfo; 36 import android.content.pm.PackageManager; 37 import android.content.pm.PackageManager.NameNotFoundException; 38 import android.content.pm.ResolveInfo; 39 import android.content.pm.ServiceInfo; 40 import android.content.pm.Signature; 41 import android.content.res.Resources; 42 import android.os.Binder; 43 import android.os.Handler; 44 import android.os.HandlerThread; 45 import android.os.Looper; 46 import android.os.Message; 47 import android.os.Process; 48 import android.text.format.DateFormat; 49 import android.util.ArraySet; 50 import android.util.Log; 51 import android.util.Pair; 52 53 import com.android.car.CarLog; 54 import com.android.car.CarServiceBase; 55 import com.android.car.CarServiceUtils; 56 import com.android.car.CarUxRestrictionsManagerService; 57 import com.android.car.R; 58 import com.android.car.SystemActivityMonitoringService; 59 import com.android.car.SystemActivityMonitoringService.TopTaskInfoContainer; 60 import com.android.internal.annotations.GuardedBy; 61 import com.android.internal.annotations.VisibleForTesting; 62 63 import java.io.PrintWriter; 64 import java.util.ArrayList; 65 import java.util.Arrays; 66 import java.util.HashMap; 67 import java.util.LinkedList; 68 import java.util.List; 69 import java.util.Map; 70 import java.util.Map.Entry; 71 import java.util.Set; 72 73 public class CarPackageManagerService extends ICarPackageManager.Stub implements CarServiceBase { 74 private static final boolean DBG_POLICY_SET = false; 75 private static final boolean DBG_POLICY_CHECK = false; 76 private static final boolean DBG_POLICY_ENFORCEMENT = false; 77 // Delimiters to parse packages and activities in the configuration XML resource. 78 private static final String PACKAGE_DELIMITER = ","; 79 private static final String PACKAGE_ACTIVITY_DELIMITER = "/"; 80 private static final int LOG_SIZE = 20; 81 82 private final Context mContext; 83 private final SystemActivityMonitoringService mSystemActivityMonitoringService; 84 private final PackageManager mPackageManager; 85 86 private final HandlerThread mHandlerThread; 87 private final PackageHandler mHandler; 88 89 // For dumpsys logging. 90 private final LinkedList<String> mBlockedActivityLogs = new LinkedList<>(); 91 92 // Store the white list and black list strings from the resource file. 93 private String mConfiguredWhitelist; 94 private String mConfiguredBlacklist; 95 /** 96 * Hold policy set from policy service or client. 97 * Key: packageName of policy service 98 */ 99 @GuardedBy("this") 100 private final HashMap<String, ClientPolicy> mClientPolicies = new HashMap<>(); 101 @GuardedBy("this") 102 private HashMap<String, AppBlockingPackageInfoWrapper> mActivityWhitelistMap = new HashMap<>(); 103 // The list corresponding to the one configured in <activityBlacklist> 104 @GuardedBy("this") 105 private HashMap<String, AppBlockingPackageInfoWrapper> mActivityBlacklistMap = new HashMap<>(); 106 @GuardedBy("this") 107 private LinkedList<AppBlockingPolicyProxy> mProxies; 108 109 @GuardedBy("this") 110 private final LinkedList<CarAppBlockingPolicy> mWaitingPolicies = new LinkedList<>(); 111 112 private final CarUxRestrictionsManagerService mCarUxRestrictionsService; 113 private boolean mEnableActivityBlocking; 114 private final ComponentName mActivityBlockingActivity; 115 116 private final ActivityLaunchListener mActivityLaunchListener = new ActivityLaunchListener(); 117 private final UxRestrictionsListener mUxRestrictionsListener; 118 119 // Information related to when the installed packages should be parsed for building a white and 120 // black list 121 private final List<String> mPackageManagerActions = Arrays.asList( 122 Intent.ACTION_PACKAGE_ADDED, 123 Intent.ACTION_PACKAGE_CHANGED, 124 Intent.ACTION_PACKAGE_DATA_CLEARED, 125 Intent.ACTION_PACKAGE_REMOVED, 126 Intent.ACTION_PACKAGE_REPLACED, 127 Intent.ACTION_PACKAGE_FULLY_REMOVED); 128 129 private final PackageParsingEventReceiver mPackageParsingEventReceiver = 130 new PackageParsingEventReceiver(); 131 private final BootEventReceiver mBootEventReceiver = new BootEventReceiver(); 132 133 // To track if the packages have been parsed for building white/black lists. If we haven't had 134 // received any intents (boot complete or package changed), then the white list is null leading 135 // to blocking everything. So, no blocking until we have had a chance to parse the packages. 136 private boolean mHasParsedPackages; 137 // To track if we received the boot complete intent. 138 private boolean mBootLockedIntentRx; 139 140 public CarPackageManagerService(Context context, 141 CarUxRestrictionsManagerService uxRestrictionsService, 142 SystemActivityMonitoringService systemActivityMonitoringService) { 143 mContext = context; 144 mCarUxRestrictionsService = uxRestrictionsService; 145 mSystemActivityMonitoringService = systemActivityMonitoringService; 146 mPackageManager = mContext.getPackageManager(); 147 mUxRestrictionsListener = new UxRestrictionsListener(uxRestrictionsService); 148 mHandlerThread = new HandlerThread(CarLog.TAG_PACKAGE); 149 mHandlerThread.start(); 150 mHandler = new PackageHandler(mHandlerThread.getLooper()); 151 Resources res = context.getResources(); 152 mEnableActivityBlocking = res.getBoolean(R.bool.enableActivityBlockingForSafety); 153 String blockingActivity = res.getString(R.string.activityBlockingActivity); 154 mActivityBlockingActivity = ComponentName.unflattenFromString(blockingActivity); 155 } 156 157 @Override 158 public void setAppBlockingPolicy(String packageName, CarAppBlockingPolicy policy, int flags) { 159 if (DBG_POLICY_SET) { 160 Log.i(CarLog.TAG_PACKAGE, "policy setting from binder call, client:" + packageName); 161 } 162 doSetAppBlockingPolicy(packageName, policy, flags, true /*setNow*/); 163 } 164 165 /** 166 * Restarts the requested task. If task with {@code taskId} does not exist, do nothing. 167 */ 168 @Override 169 public void restartTask(int taskId) { 170 mSystemActivityMonitoringService.restartTask(taskId); 171 } 172 173 private void doSetAppBlockingPolicy(String packageName, CarAppBlockingPolicy policy, int flags, 174 boolean setNow) { 175 if (mContext.checkCallingOrSelfPermission(Car.PERMISSION_CONTROL_APP_BLOCKING) 176 != PackageManager.PERMISSION_GRANTED) { 177 throw new SecurityException( 178 "requires permission " + Car.PERMISSION_CONTROL_APP_BLOCKING); 179 } 180 CarServiceUtils.assertPackageName(mContext, packageName); 181 if (policy == null) { 182 throw new IllegalArgumentException("policy cannot be null"); 183 } 184 if ((flags & CarPackageManager.FLAG_SET_POLICY_ADD) != 0 && 185 (flags & CarPackageManager.FLAG_SET_POLICY_REMOVE) != 0) { 186 throw new IllegalArgumentException( 187 "Cannot set both FLAG_SET_POLICY_ADD and FLAG_SET_POLICY_REMOVE flag"); 188 } 189 mHandler.requestUpdatingPolicy(packageName, policy, flags); 190 if (setNow) { 191 mHandler.requestPolicySetting(); 192 if ((flags & CarPackageManager.FLAG_SET_POLICY_WAIT_FOR_CHANGE) != 0) { 193 synchronized (policy) { 194 try { 195 policy.wait(); 196 } catch (InterruptedException e) { 197 } 198 } 199 } 200 } 201 } 202 203 @Override 204 public boolean isActivityDistractionOptimized(String packageName, String className) { 205 assertPackageAndClassName(packageName, className); 206 synchronized (this) { 207 if (DBG_POLICY_CHECK) { 208 Log.i(CarLog.TAG_PACKAGE, "isActivityDistractionOptimized" 209 + dumpPoliciesLocked(false)); 210 } 211 AppBlockingPackageInfo info = searchFromBlacklistsLocked(packageName); 212 if (info != null) { 213 return false; 214 } 215 return isActivityInWhitelistsLocked(packageName, className); 216 } 217 } 218 219 @Override 220 public boolean isServiceDistractionOptimized(String packageName, String className) { 221 if (packageName == null) { 222 throw new IllegalArgumentException("Package name null"); 223 } 224 synchronized (this) { 225 if (DBG_POLICY_CHECK) { 226 Log.i(CarLog.TAG_PACKAGE, "isServiceDistractionOptimized" 227 + dumpPoliciesLocked(false)); 228 } 229 AppBlockingPackageInfo info = searchFromBlacklistsLocked(packageName); 230 if (info != null) { 231 return false; 232 } 233 info = searchFromWhitelistsLocked(packageName); 234 if (info != null) { 235 return true; 236 } 237 } 238 return false; 239 } 240 241 @Override 242 public boolean isActivityBackedBySafeActivity(ComponentName activityName) { 243 if (!mUxRestrictionsListener.isRestricted()) { 244 return true; 245 } 246 StackInfo info = mSystemActivityMonitoringService.getFocusedStackForTopActivity( 247 activityName); 248 if (info == null) { // not top in focused stack 249 return true; 250 } 251 if (info.taskNames.length <= 1) { // nothing below this. 252 return false; 253 } 254 ComponentName activityBehind = ComponentName.unflattenFromString( 255 info.taskNames[info.taskNames.length - 2]); 256 return isActivityDistractionOptimized(activityBehind.getPackageName(), 257 activityBehind.getClassName()); 258 } 259 260 public Looper getLooper() { 261 return mHandlerThread.getLooper(); 262 } 263 264 private void assertPackageAndClassName(String packageName, String className) { 265 if (packageName == null) { 266 throw new IllegalArgumentException("Package name null"); 267 } 268 if (className == null) { 269 throw new IllegalArgumentException("Class name null"); 270 } 271 } 272 273 @GuardedBy("this") 274 private AppBlockingPackageInfo searchFromBlacklistsLocked(String packageName) { 275 for (ClientPolicy policy : mClientPolicies.values()) { 276 AppBlockingPackageInfoWrapper wrapper = policy.blacklistsMap.get(packageName); 277 if (wrapper != null && wrapper.isMatching) { 278 return wrapper.info; 279 } 280 } 281 AppBlockingPackageInfoWrapper wrapper = mActivityBlacklistMap.get(packageName); 282 return (wrapper != null) ? wrapper.info : null; 283 } 284 285 @GuardedBy("this") 286 private AppBlockingPackageInfo searchFromWhitelistsLocked(String packageName) { 287 for (ClientPolicy policy : mClientPolicies.values()) { 288 AppBlockingPackageInfoWrapper wrapper = policy.whitelistsMap.get(packageName); 289 if (wrapper != null && wrapper.isMatching) { 290 return wrapper.info; 291 } 292 } 293 AppBlockingPackageInfoWrapper wrapper = mActivityWhitelistMap.get(packageName); 294 return (wrapper != null) ? wrapper.info : null; 295 } 296 297 @GuardedBy("this") 298 private boolean isActivityInWhitelistsLocked(String packageName, String className) { 299 for (ClientPolicy policy : mClientPolicies.values()) { 300 if (isActivityInMapAndMatching(policy.whitelistsMap, packageName, className)) { 301 return true; 302 } 303 } 304 return isActivityInMapAndMatching(mActivityWhitelistMap, packageName, className); 305 } 306 307 private boolean isActivityInMapAndMatching(HashMap<String, AppBlockingPackageInfoWrapper> map, 308 String packageName, String className) { 309 AppBlockingPackageInfoWrapper wrapper = map.get(packageName); 310 if (wrapper == null || !wrapper.isMatching) { 311 if (DBG_POLICY_CHECK) { 312 Log.d(CarLog.TAG_PACKAGE, "Pkg not in whitelist:" + packageName); 313 } 314 return false; 315 } 316 return wrapper.info.isActivityCovered(className); 317 } 318 319 @Override 320 public void init() { 321 synchronized (this) { 322 mHandler.requestInit(); 323 } 324 } 325 326 @Override 327 public void release() { 328 synchronized (this) { 329 mHandler.requestRelease(); 330 // wait for release do be done. This guarantees that init is done. 331 try { 332 wait(); 333 } catch (InterruptedException e) { 334 } 335 mHasParsedPackages = false; 336 mActivityWhitelistMap.clear(); 337 mActivityBlacklistMap.clear(); 338 mClientPolicies.clear(); 339 if (mProxies != null) { 340 for (AppBlockingPolicyProxy proxy : mProxies) { 341 proxy.disconnect(); 342 } 343 mProxies.clear(); 344 } 345 wakeupClientsWaitingForPolicySetitngLocked(); 346 } 347 mContext.unregisterReceiver(mPackageParsingEventReceiver); 348 mContext.unregisterReceiver(mBootEventReceiver); 349 mCarUxRestrictionsService.unregisterUxRestrictionsChangeListener(mUxRestrictionsListener); 350 mSystemActivityMonitoringService.registerActivityLaunchListener(null); 351 } 352 353 // run from HandlerThread 354 private void doHandleInit() { 355 startAppBlockingPolicies(); 356 IntentFilter bootIntent = new IntentFilter(); 357 bootIntent.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED); 358 mContext.registerReceiver(mBootEventReceiver, bootIntent); 359 IntentFilter pkgParseIntent = new IntentFilter(); 360 for (String action : mPackageManagerActions) { 361 pkgParseIntent.addAction(action); 362 } 363 pkgParseIntent.addDataScheme("package"); 364 mContext.registerReceiver(mPackageParsingEventReceiver, pkgParseIntent); 365 try { 366 mCarUxRestrictionsService.registerUxRestrictionsChangeListener(mUxRestrictionsListener); 367 } catch (IllegalArgumentException e) { 368 // can happen while mocking is going on while init is still done. 369 Log.w(CarLog.TAG_PACKAGE, "sensor subscription failed", e); 370 return; 371 } 372 mSystemActivityMonitoringService.registerActivityLaunchListener( 373 mActivityLaunchListener); 374 } 375 376 private void doParseInstalledPackages() { 377 generateActivityWhitelistMap(); 378 generateActivityBlacklistMap(); 379 synchronized (this) { 380 mHasParsedPackages = true; 381 } 382 mUxRestrictionsListener.checkIfTopActivityNeedsBlocking(); 383 } 384 385 private synchronized void doHandleRelease() { 386 notifyAll(); 387 } 388 389 @GuardedBy("this") 390 private void wakeupClientsWaitingForPolicySetitngLocked() { 391 for (CarAppBlockingPolicy waitingPolicy : mWaitingPolicies) { 392 synchronized (waitingPolicy) { 393 waitingPolicy.notifyAll(); 394 } 395 } 396 mWaitingPolicies.clear(); 397 } 398 399 private void doSetPolicy() { 400 synchronized (this) { 401 wakeupClientsWaitingForPolicySetitngLocked(); 402 } 403 blockTopActivitiesIfNecessary(); 404 } 405 406 private void doUpdatePolicy(String packageName, CarAppBlockingPolicy policy, int flags) { 407 if (DBG_POLICY_SET) { 408 Log.i(CarLog.TAG_PACKAGE, "setting policy from:" + packageName + ",policy:" + policy + 409 ",flags:0x" + Integer.toHexString(flags)); 410 } 411 AppBlockingPackageInfoWrapper[] blacklistWrapper = verifyList(policy.blacklists); 412 AppBlockingPackageInfoWrapper[] whitelistWrapper = verifyList(policy.whitelists); 413 synchronized (this) { 414 ClientPolicy clientPolicy = mClientPolicies.get(packageName); 415 if (clientPolicy == null) { 416 clientPolicy = new ClientPolicy(); 417 mClientPolicies.put(packageName, clientPolicy); 418 } 419 if ((flags & CarPackageManager.FLAG_SET_POLICY_ADD) != 0) { 420 clientPolicy.addToBlacklists(blacklistWrapper); 421 clientPolicy.addToWhitelists(whitelistWrapper); 422 } else if ((flags & CarPackageManager.FLAG_SET_POLICY_REMOVE) != 0) { 423 clientPolicy.removeBlacklists(blacklistWrapper); 424 clientPolicy.removeWhitelists(whitelistWrapper); 425 } else { //replace. 426 clientPolicy.replaceBlacklists(blacklistWrapper); 427 clientPolicy.replaceWhitelists(whitelistWrapper); 428 } 429 if ((flags & CarPackageManager.FLAG_SET_POLICY_WAIT_FOR_CHANGE) != 0) { 430 mWaitingPolicies.add(policy); 431 } 432 if (DBG_POLICY_SET) { 433 Log.i(CarLog.TAG_PACKAGE, "policy set:" + dumpPoliciesLocked(false)); 434 } 435 } 436 blockTopActivitiesIfNecessary(); 437 } 438 439 private AppBlockingPackageInfoWrapper[] verifyList(AppBlockingPackageInfo[] list) { 440 if (list == null) { 441 return null; 442 } 443 LinkedList<AppBlockingPackageInfoWrapper> wrappers = new LinkedList<>(); 444 for (int i = 0; i < list.length; i++) { 445 AppBlockingPackageInfo info = list[i]; 446 if (info == null) { 447 continue; 448 } 449 boolean isMatching = isInstalledPackageMatching(info); 450 wrappers.add(new AppBlockingPackageInfoWrapper(info, isMatching)); 451 } 452 return wrappers.toArray(new AppBlockingPackageInfoWrapper[wrappers.size()]); 453 } 454 455 boolean isInstalledPackageMatching(AppBlockingPackageInfo info) { 456 PackageInfo packageInfo = null; 457 try { 458 packageInfo = mPackageManager.getPackageInfo(info.packageName, 459 PackageManager.GET_SIGNATURES); 460 } catch (NameNotFoundException e) { 461 return false; 462 } 463 if (packageInfo == null) { 464 return false; 465 } 466 // if it is system app and client specified the flag, do not check signature 467 if ((info.flags & AppBlockingPackageInfo.FLAG_SYSTEM_APP) == 0 || 468 (!packageInfo.applicationInfo.isSystemApp() && 469 !packageInfo.applicationInfo.isUpdatedSystemApp())) { 470 Signature[] signatires = packageInfo.signatures; 471 if (!isAnySignatureMatching(signatires, info.signatures)) { 472 return false; 473 } 474 } 475 int version = packageInfo.versionCode; 476 if (info.minRevisionCode == 0) { 477 if (info.maxRevisionCode == 0) { // all versions 478 return true; 479 } else { // only max version matters 480 return info.maxRevisionCode > version; 481 } 482 } else { // min version matters 483 if (info.maxRevisionCode == 0) { 484 return info.minRevisionCode < version; 485 } else { 486 return (info.minRevisionCode < version) && (info.maxRevisionCode > version); 487 } 488 } 489 } 490 491 /** 492 * Any signature from policy matching with package's signatures is treated as matching. 493 */ 494 boolean isAnySignatureMatching(Signature[] fromPackage, Signature[] fromPolicy) { 495 if (fromPackage == null) { 496 return false; 497 } 498 if (fromPolicy == null) { 499 return false; 500 } 501 ArraySet<Signature> setFromPackage = new ArraySet<Signature>(); 502 for (Signature sig : fromPackage) { 503 setFromPackage.add(sig); 504 } 505 for (Signature sig : fromPolicy) { 506 if (setFromPackage.contains(sig)) { 507 return true; 508 } 509 } 510 return false; 511 } 512 513 /** 514 * Generate a map of whitelisted packages and activities of the form {pkgName, Whitelisted 515 * activities}. The whitelist information can come from a configuration XML resource or from 516 * the apps marking their activities as distraction optimized. 517 */ 518 private void generateActivityWhitelistMap() { 519 HashMap<String, AppBlockingPackageInfoWrapper> activityWhitelist = new HashMap<>(); 520 mConfiguredWhitelist = mContext.getString(R.string.activityWhitelist); 521 if (mConfiguredWhitelist == null) { 522 if (DBG_POLICY_CHECK) { 523 Log.d(CarLog.TAG_PACKAGE, "Null whitelist in config"); 524 } 525 return; 526 } 527 // Get the apps/activities that are whitelisted in the configuration XML resource 528 HashMap<String, Set<String>> configWhitelist = parseConfiglist(mConfiguredWhitelist); 529 if (configWhitelist == null) { 530 if (DBG_POLICY_CHECK) { 531 Log.w(CarLog.TAG_PACKAGE, "White list null. No apps whitelisted"); 532 } 533 return; 534 } 535 // Add the blocking overlay activity to the whitelist, since that needs to run in a 536 // restricted state to communicate the reason an app was blocked. 537 Set<String> defaultActivity = new ArraySet<>(); 538 defaultActivity.add(mActivityBlockingActivity.getClassName()); 539 configWhitelist.put(mActivityBlockingActivity.getPackageName(), defaultActivity); 540 541 List<PackageInfo> packages = mPackageManager.getInstalledPackages( 542 PackageManager.GET_SIGNATURES | PackageManager.GET_ACTIVITIES 543 | PackageManager.MATCH_DIRECT_BOOT_AWARE 544 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 545 546 for (PackageInfo info : packages) { 547 if (info.applicationInfo == null) { 548 continue; 549 } 550 551 int flags = 0; 552 String[] activities = null; 553 554 if (info.applicationInfo.isSystemApp() 555 || info.applicationInfo.isUpdatedSystemApp()) { 556 flags = AppBlockingPackageInfo.FLAG_SYSTEM_APP; 557 } 558 559 /* 1. Check if all or some of this app is in the <activityWhitelist> 560 in config.xml */ 561 Set<String> configActivitiesForPackage = configWhitelist.get(info.packageName); 562 if (configActivitiesForPackage != null) { 563 if (DBG_POLICY_CHECK) { 564 Log.d(CarLog.TAG_PACKAGE, info.packageName + " whitelisted"); 565 } 566 if (configActivitiesForPackage.size() == 0) { 567 // Whole Pkg has been whitelisted 568 flags |= AppBlockingPackageInfo.FLAG_WHOLE_ACTIVITY; 569 // Add all activities to the whitelist 570 activities = getActivitiesInPackage(info); 571 if (activities == null && DBG_POLICY_CHECK) { 572 Log.d(CarLog.TAG_PACKAGE, info.packageName + ": Activities null"); 573 } 574 } else { 575 if (DBG_POLICY_CHECK) { 576 Log.d(CarLog.TAG_PACKAGE, "Partially Whitelisted. WL Activities:"); 577 for (String a : configActivitiesForPackage) { 578 Log.d(CarLog.TAG_PACKAGE, a); 579 } 580 } 581 activities = configActivitiesForPackage.toArray( 582 new String[configActivitiesForPackage.size()]); 583 } 584 } else { 585 /* 2. If app is not listed in the config.xml check their Manifest meta-data to 586 see if they have any Distraction Optimized(DO) activities */ 587 try { 588 activities = CarAppMetadataReader.findDistractionOptimizedActivities( 589 mContext, 590 info.packageName); 591 } catch (NameNotFoundException e) { 592 Log.w(CarLog.TAG_PACKAGE, "Error reading metadata: " + info.packageName); 593 continue; 594 } 595 if (activities != null) { 596 // Some of the activities in this app are Distraction Optimized. 597 if (DBG_POLICY_CHECK) { 598 for (String activity : activities) { 599 Log.d(CarLog.TAG_PACKAGE, 600 "adding " + activity + " from " + info.packageName 601 + " to whitelist"); 602 } 603 } 604 } 605 } 606 // Nothing to add to whitelist 607 if (activities == null) { 608 continue; 609 } 610 611 Signature[] signatures; 612 signatures = info.signatures; 613 AppBlockingPackageInfo appBlockingInfo = new AppBlockingPackageInfo( 614 info.packageName, 0, 0, flags, signatures, activities); 615 AppBlockingPackageInfoWrapper wrapper = new AppBlockingPackageInfoWrapper( 616 appBlockingInfo, true); 617 activityWhitelist.put(info.packageName, wrapper); 618 } 619 synchronized (this) { 620 mActivityWhitelistMap.clear(); 621 mActivityWhitelistMap.putAll(activityWhitelist); 622 } 623 } 624 625 /** 626 * Generate a map of blacklisted packages and activities of the form {pkgName, Blacklisted 627 * activities}. The blacklist information comes from a configuration XML resource. 628 */ 629 private void generateActivityBlacklistMap() { 630 HashMap<String, AppBlockingPackageInfoWrapper> activityBlacklist = new HashMap<>(); 631 mConfiguredBlacklist = mContext.getString(R.string.activityBlacklist); 632 if (mConfiguredBlacklist == null) { 633 if (DBG_POLICY_CHECK) { 634 Log.d(CarLog.TAG_PACKAGE, "Null blacklist in config"); 635 } 636 return; 637 } 638 Map<String, Set<String>> configBlacklist = parseConfiglist(mConfiguredBlacklist); 639 if (configBlacklist == null) { 640 if (DBG_POLICY_CHECK) { 641 Log.w(CarLog.TAG_PACKAGE, "Black list null. No apps blacklisted"); 642 } 643 return; 644 } 645 646 for (String pkg : configBlacklist.keySet()) { 647 int flags = 0; 648 PackageInfo pkgInfo; 649 String[] activities; 650 try { 651 pkgInfo = mPackageManager.getPackageInfo( 652 pkg, PackageManager.GET_ACTIVITIES 653 | PackageManager.GET_SIGNING_CERTIFICATES 654 | PackageManager.MATCH_DIRECT_BOOT_AWARE 655 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 656 } catch (NameNotFoundException e) { 657 Log.e(CarLog.TAG_PACKAGE, pkg + " not found to blacklist " + e); 658 continue; 659 } 660 661 if (pkgInfo.applicationInfo.isSystemApp() 662 || pkgInfo.applicationInfo.isUpdatedSystemApp()) { 663 flags |= AppBlockingPackageInfo.FLAG_SYSTEM_APP; 664 } 665 Set<String> configActivities = configBlacklist.get(pkg); 666 if (configActivities.size() == 0) { 667 // whole package 668 flags |= AppBlockingPackageInfo.FLAG_WHOLE_ACTIVITY; 669 activities = getActivitiesInPackage(pkgInfo); 670 } else { 671 activities = configActivities.toArray(new String[configActivities.size()]); 672 } 673 674 if (activities == null) { 675 continue; 676 } 677 AppBlockingPackageInfo appBlockingInfo = new AppBlockingPackageInfo(pkg, 0, 0, flags, 678 pkgInfo.signatures, activities); 679 AppBlockingPackageInfoWrapper wrapper = new AppBlockingPackageInfoWrapper( 680 appBlockingInfo, true); 681 activityBlacklist.put(pkg, wrapper); 682 } 683 synchronized (this) { 684 mActivityBlacklistMap.clear(); 685 mActivityBlacklistMap.putAll(activityBlacklist); 686 } 687 } 688 689 /** 690 * Parses the given resource and returns a map of packages and activities. 691 * Key is package name and value is list of activities. Empty list implies whole package is 692 * included. 693 */ 694 @Nullable 695 private HashMap<String, Set<String>> parseConfiglist(String configList) { 696 if (configList == null) { 697 return null; 698 } 699 HashMap<String, Set<String>> packageToActivityMap = new HashMap<>(); 700 String[] entries = configList.split(PACKAGE_DELIMITER); 701 for (String entry : entries) { 702 String[] packageActivityPair = entry.split(PACKAGE_ACTIVITY_DELIMITER); 703 Set<String> activities = packageToActivityMap.get(packageActivityPair[0]); 704 boolean newPackage = false; 705 if (activities == null) { 706 activities = new ArraySet<>(); 707 newPackage = true; 708 packageToActivityMap.put(packageActivityPair[0], activities); 709 } 710 if (packageActivityPair.length == 1) { // whole package 711 activities.clear(); 712 } else if (packageActivityPair.length == 2) { 713 // add class name only when the whole package is not whitelisted. 714 if (newPackage || (activities.size() > 0)) { 715 activities.add(packageActivityPair[1]); 716 } 717 } 718 } 719 return packageToActivityMap; 720 } 721 722 @Nullable 723 private String[] getActivitiesInPackage(PackageInfo info) { 724 if (info == null || info.activities == null) { 725 return null; 726 } 727 List<String> activityList = new ArrayList<>(); 728 for (ActivityInfo aInfo : info.activities) { 729 activityList.add(aInfo.name); 730 } 731 return activityList.toArray(new String[activityList.size()]); 732 } 733 734 /** 735 * Checks if there are any {@link CarAppBlockingPolicyService} and creates a proxy to 736 * bind to them and retrieve the {@link CarAppBlockingPolicy} 737 */ 738 @VisibleForTesting 739 public void startAppBlockingPolicies() { 740 Intent policyIntent = new Intent(); 741 policyIntent.setAction(CarAppBlockingPolicyService.SERVICE_INTERFACE); 742 List<ResolveInfo> policyInfos = mPackageManager.queryIntentServices(policyIntent, 0); 743 if (policyInfos == null) { //no need to wait for service binding and retrieval. 744 mHandler.requestPolicySetting(); 745 return; 746 } 747 LinkedList<AppBlockingPolicyProxy> proxies = new LinkedList<>(); 748 for (ResolveInfo resolveInfo : policyInfos) { 749 ServiceInfo serviceInfo = resolveInfo.serviceInfo; 750 if (serviceInfo == null) { 751 continue; 752 } 753 if (serviceInfo.isEnabled()) { 754 if (mPackageManager.checkPermission(Car.PERMISSION_CONTROL_APP_BLOCKING, 755 serviceInfo.packageName) != PackageManager.PERMISSION_GRANTED) { 756 continue; 757 } 758 Log.i(CarLog.TAG_PACKAGE, "found policy holding service:" + serviceInfo); 759 AppBlockingPolicyProxy proxy = new AppBlockingPolicyProxy(this, mContext, 760 serviceInfo); 761 proxy.connect(); 762 proxies.add(proxy); 763 } 764 } 765 synchronized (this) { 766 mProxies = proxies; 767 } 768 } 769 770 public void onPolicyConnectionAndSet(AppBlockingPolicyProxy proxy, 771 CarAppBlockingPolicy policy) { 772 doHandlePolicyConnection(proxy, policy); 773 } 774 775 public void onPolicyConnectionFailure(AppBlockingPolicyProxy proxy) { 776 doHandlePolicyConnection(proxy, null); 777 } 778 779 private void doHandlePolicyConnection(AppBlockingPolicyProxy proxy, 780 CarAppBlockingPolicy policy) { 781 boolean shouldSetPolicy = false; 782 synchronized (this) { 783 if (mProxies == null) { 784 proxy.disconnect(); 785 return; 786 } 787 mProxies.remove(proxy); 788 if (mProxies.size() == 0) { 789 shouldSetPolicy = true; 790 mProxies = null; 791 } 792 } 793 try { 794 if (policy != null) { 795 if (DBG_POLICY_SET) { 796 Log.i(CarLog.TAG_PACKAGE, "policy setting from policy service:" + 797 proxy.getPackageName()); 798 } 799 doSetAppBlockingPolicy(proxy.getPackageName(), policy, 0, false /*setNow*/); 800 } 801 } finally { 802 proxy.disconnect(); 803 if (shouldSetPolicy) { 804 mHandler.requestPolicySetting(); 805 } 806 } 807 } 808 809 @Override 810 public void dump(PrintWriter writer) { 811 synchronized (this) { 812 writer.println("*PackageManagementService*"); 813 writer.println("mEnableActivityBlocking:" + mEnableActivityBlocking); 814 writer.println("mHasParsedPackages:" + mHasParsedPackages); 815 writer.println("mBootLockedIntentRx:" + mBootLockedIntentRx); 816 writer.println("ActivityRestricted:" + mUxRestrictionsListener.isRestricted()); 817 writer.println(String.join("\n", mBlockedActivityLogs)); 818 writer.print(dumpPoliciesLocked(true)); 819 } 820 } 821 822 @GuardedBy("this") 823 private String dumpPoliciesLocked(boolean dumpAll) { 824 StringBuilder sb = new StringBuilder(); 825 if (dumpAll) { 826 sb.append("**System white list**\n"); 827 for (AppBlockingPackageInfoWrapper wrapper : mActivityWhitelistMap.values()) { 828 sb.append(wrapper.toString() + "\n"); 829 } 830 sb.append("**System Black list**\n"); 831 for (AppBlockingPackageInfoWrapper wrapper : mActivityBlacklistMap.values()) { 832 sb.append(wrapper.toString() + "\n"); 833 } 834 } 835 sb.append("**Client Policies**\n"); 836 for (Entry<String, ClientPolicy> entry : mClientPolicies.entrySet()) { 837 sb.append("Client:" + entry.getKey() + "\n"); 838 sb.append(" whitelists:\n"); 839 for (AppBlockingPackageInfoWrapper wrapper : entry.getValue().whitelistsMap.values()) { 840 sb.append(wrapper.toString() + "\n"); 841 } 842 sb.append(" blacklists:\n"); 843 for (AppBlockingPackageInfoWrapper wrapper : entry.getValue().blacklistsMap.values()) { 844 sb.append(wrapper.toString() + "\n"); 845 } 846 } 847 sb.append("**Unprocessed policy services**\n"); 848 if (mProxies != null) { 849 for (AppBlockingPolicyProxy proxy : mProxies) { 850 sb.append(proxy.toString() + "\n"); 851 } 852 } 853 sb.append("**Whitelist string in resource**\n"); 854 sb.append(mConfiguredWhitelist + "\n"); 855 856 sb.append("**Blacklist string in resource**\n"); 857 sb.append(mConfiguredBlacklist + "\n"); 858 859 return sb.toString(); 860 } 861 862 private void blockTopActivityIfNecessary(TopTaskInfoContainer topTask) { 863 boolean restricted = mUxRestrictionsListener.isRestricted(); 864 if (!restricted) { 865 return; 866 } 867 doBlockTopActivityIfNotAllowed(topTask); 868 } 869 870 private void doBlockTopActivityIfNotAllowed(TopTaskInfoContainer topTask) { 871 if (topTask.topActivity == null) { 872 return; 873 } 874 boolean allowed = isActivityDistractionOptimized( 875 topTask.topActivity.getPackageName(), 876 topTask.topActivity.getClassName()); 877 if (DBG_POLICY_ENFORCEMENT) { 878 Log.i(CarLog.TAG_PACKAGE, "new activity:" + topTask.toString() + " allowed:" + allowed); 879 } 880 if (allowed) { 881 return; 882 } 883 synchronized (this) { 884 if (!mEnableActivityBlocking) { 885 Log.d(CarLog.TAG_PACKAGE, "Current activity " + topTask.topActivity + 886 " not allowed, blocking disabled. Number of tasks in stack:" 887 + topTask.stackInfo.taskIds.length); 888 return; 889 } 890 } 891 if (DBG_POLICY_CHECK) { 892 Log.i(CarLog.TAG_PACKAGE, "Current activity " + topTask.topActivity + 893 " not allowed, will block, number of tasks in stack:" + 894 topTask.stackInfo.taskIds.length); 895 } 896 StringBuilder blockedActivityLog = new StringBuilder(); 897 Intent newActivityIntent = new Intent(); 898 newActivityIntent.setComponent(mActivityBlockingActivity); 899 newActivityIntent.putExtra( 900 ActivityBlockingActivity.INTENT_KEY_BLOCKED_ACTIVITY, 901 topTask.topActivity.flattenToString()); 902 blockedActivityLog.append("Blocked activity ") 903 .append(topTask.topActivity.flattenToShortString()) 904 .append(". Task id ").append(topTask.taskId); 905 906 // If root activity of blocked task is DO, also pass its task id into blocking activity, 907 // which uses the id to display a button for restarting the blocked task. 908 for (int i = 0; i < topTask.stackInfo.taskIds.length; i++) { 909 // topTask.taskId is the task that should be blocked. 910 if (topTask.stackInfo.taskIds[i] == topTask.taskId) { 911 // stackInfo represents an ActivityStack. Its fields taskIds and taskNames 912 // are 1:1 mapped, where taskNames is the name of root activity in this task. 913 String taskRootActivity = topTask.stackInfo.taskNames[i]; 914 915 ComponentName rootActivityName = ComponentName.unflattenFromString( 916 taskRootActivity); 917 if (isActivityDistractionOptimized( 918 rootActivityName.getPackageName(), rootActivityName.getClassName())) { 919 newActivityIntent.putExtra( 920 ActivityBlockingActivity.EXTRA_BLOCKED_TASK, topTask.taskId); 921 if (Log.isLoggable(CarLog.TAG_PACKAGE, Log.INFO)) { 922 Log.i(CarLog.TAG_PACKAGE, "Blocked task " + topTask.taskId 923 + " has DO root activity " + taskRootActivity); 924 } 925 blockedActivityLog.append(". Root DO activity ") 926 .append(rootActivityName.flattenToShortString()); 927 } 928 break; 929 } 930 } 931 addLog(blockedActivityLog.toString()); 932 mSystemActivityMonitoringService.blockActivity(topTask, newActivityIntent); 933 } 934 935 private void blockTopActivitiesIfNecessary() { 936 boolean restricted = mUxRestrictionsListener.isRestricted(); 937 if (!restricted) { 938 return; 939 } 940 List<TopTaskInfoContainer> topTasks = mSystemActivityMonitoringService.getTopTasks(); 941 for (TopTaskInfoContainer topTask : topTasks) { 942 doBlockTopActivityIfNotAllowed(topTask); 943 } 944 } 945 946 @Override 947 public synchronized void setEnableActivityBlocking(boolean enable) { 948 // Check if the caller has the same signature as that of the car service. 949 if (mPackageManager.checkSignatures(Process.myUid(), Binder.getCallingUid()) 950 != PackageManager.SIGNATURE_MATCH) { 951 throw new SecurityException( 952 "Caller " + mPackageManager.getNameForUid(Binder.getCallingUid()) 953 + " does not have the right signature"); 954 } 955 mEnableActivityBlocking = enable; 956 } 957 958 /** 959 * Get the distraction optimized activities for the given package. 960 * 961 * @param pkgName Name of the package 962 * @return Array of the distraction optimized activities in the package 963 */ 964 @Nullable 965 public String[] getDistractionOptimizedActivities(String pkgName) { 966 try { 967 return CarAppMetadataReader.findDistractionOptimizedActivities(mContext, pkgName); 968 } catch (NameNotFoundException e) { 969 return null; 970 } 971 } 972 973 /** 974 * Append one line of log for dumpsys. 975 * 976 * <p>Maintains the size of log by {@link #LOG_SIZE} and appends tag and timestamp to the line. 977 */ 978 private void addLog(String log) { 979 while (mBlockedActivityLogs.size() >= LOG_SIZE) { 980 mBlockedActivityLogs.remove(); 981 } 982 StringBuffer sb = new StringBuffer() 983 .append(CarLog.TAG_PACKAGE).append(':') 984 .append(DateFormat.format( 985 "MM-dd HH:mm:ss", System.currentTimeMillis())).append(": ") 986 .append(log); 987 mBlockedActivityLogs.add(sb.toString()); 988 } 989 990 /** 991 * Reading policy and setting policy can take time. Run it in a separate handler thread. 992 */ 993 private class PackageHandler extends Handler { 994 private final int MSG_INIT = 0; 995 private final int MSG_PARSE_PKG = 1; 996 private final int MSG_SET_POLICY = 2; 997 private final int MSG_UPDATE_POLICY = 3; 998 private final int MSG_RELEASE = 4; 999 1000 private PackageHandler(Looper looper) { 1001 super(looper); 1002 } 1003 1004 private void requestInit() { 1005 Message msg = obtainMessage(MSG_INIT); 1006 sendMessage(msg); 1007 } 1008 1009 private void requestRelease() { 1010 removeMessages(MSG_INIT); 1011 removeMessages(MSG_SET_POLICY); 1012 removeMessages(MSG_UPDATE_POLICY); 1013 Message msg = obtainMessage(MSG_RELEASE); 1014 sendMessage(msg); 1015 } 1016 1017 private void requestPolicySetting() { 1018 Message msg = obtainMessage(MSG_SET_POLICY); 1019 sendMessage(msg); 1020 } 1021 1022 private void requestUpdatingPolicy(String packageName, CarAppBlockingPolicy policy, 1023 int flags) { 1024 Pair<String, CarAppBlockingPolicy> pair = new Pair<>(packageName, policy); 1025 Message msg = obtainMessage(MSG_UPDATE_POLICY, flags, 0, pair); 1026 sendMessage(msg); 1027 } 1028 1029 private void requestParsingInstalledPkgs(long delayMs) { 1030 Message msg = obtainMessage(MSG_PARSE_PKG); 1031 if (delayMs == 0) { 1032 sendMessage(msg); 1033 } else { 1034 sendMessageDelayed(msg, delayMs); 1035 } 1036 } 1037 1038 @Override 1039 public void handleMessage(Message msg) { 1040 switch (msg.what) { 1041 case MSG_INIT: 1042 doHandleInit(); 1043 break; 1044 case MSG_PARSE_PKG: 1045 removeMessages(MSG_PARSE_PKG); 1046 doParseInstalledPackages(); 1047 break; 1048 case MSG_SET_POLICY: 1049 doSetPolicy(); 1050 break; 1051 case MSG_UPDATE_POLICY: 1052 Pair<String, CarAppBlockingPolicy> pair = 1053 (Pair<String, CarAppBlockingPolicy>) msg.obj; 1054 doUpdatePolicy(pair.first, pair.second, msg.arg1); 1055 break; 1056 case MSG_RELEASE: 1057 doHandleRelease(); 1058 break; 1059 } 1060 } 1061 } 1062 1063 private static class AppBlockingPackageInfoWrapper { 1064 private final AppBlockingPackageInfo info; 1065 /** 1066 * Whether the current info is matching with the target package in system. Mismatch can 1067 * happen for version out of range or signature mismatch. 1068 */ 1069 private boolean isMatching; 1070 1071 private AppBlockingPackageInfoWrapper(AppBlockingPackageInfo info, boolean isMatching) { 1072 this.info = info; 1073 this.isMatching = isMatching; 1074 } 1075 1076 @Override 1077 public String toString() { 1078 return "AppBlockingPackageInfoWrapper [info=" + info + ", isMatching=" + isMatching + 1079 "]"; 1080 } 1081 } 1082 1083 /** 1084 * Client policy holder per each client. Should be accessed with CarpackageManagerService.this 1085 * held. 1086 */ 1087 private static class ClientPolicy { 1088 private final HashMap<String, AppBlockingPackageInfoWrapper> whitelistsMap = 1089 new HashMap<>(); 1090 private final HashMap<String, AppBlockingPackageInfoWrapper> blacklistsMap = 1091 new HashMap<>(); 1092 1093 private void replaceWhitelists(AppBlockingPackageInfoWrapper[] whitelists) { 1094 whitelistsMap.clear(); 1095 addToWhitelists(whitelists); 1096 } 1097 1098 private void addToWhitelists(AppBlockingPackageInfoWrapper[] whitelists) { 1099 if (whitelists == null) { 1100 return; 1101 } 1102 for (AppBlockingPackageInfoWrapper wrapper : whitelists) { 1103 if (wrapper != null) { 1104 whitelistsMap.put(wrapper.info.packageName, wrapper); 1105 } 1106 } 1107 } 1108 1109 private void removeWhitelists(AppBlockingPackageInfoWrapper[] whitelists) { 1110 if (whitelists == null) { 1111 return; 1112 } 1113 for (AppBlockingPackageInfoWrapper wrapper : whitelists) { 1114 if (wrapper != null) { 1115 whitelistsMap.remove(wrapper.info.packageName); 1116 } 1117 } 1118 } 1119 1120 private void replaceBlacklists(AppBlockingPackageInfoWrapper[] blacklists) { 1121 blacklistsMap.clear(); 1122 addToBlacklists(blacklists); 1123 } 1124 1125 private void addToBlacklists(AppBlockingPackageInfoWrapper[] blacklists) { 1126 if (blacklists == null) { 1127 return; 1128 } 1129 for (AppBlockingPackageInfoWrapper wrapper : blacklists) { 1130 if (wrapper != null) { 1131 blacklistsMap.put(wrapper.info.packageName, wrapper); 1132 } 1133 } 1134 } 1135 1136 private void removeBlacklists(AppBlockingPackageInfoWrapper[] blacklists) { 1137 if (blacklists == null) { 1138 return; 1139 } 1140 for (AppBlockingPackageInfoWrapper wrapper : blacklists) { 1141 if (wrapper != null) { 1142 blacklistsMap.remove(wrapper.info.packageName); 1143 } 1144 } 1145 } 1146 } 1147 1148 private class ActivityLaunchListener 1149 implements SystemActivityMonitoringService.ActivityLaunchListener { 1150 @Override 1151 public void onActivityLaunch(TopTaskInfoContainer topTask) { 1152 blockTopActivityIfNecessary(topTask); 1153 } 1154 } 1155 1156 /** 1157 * Listens to the UX restrictions from {@link CarUxRestrictionsManagerService} and initiates 1158 * checking if the foreground Activity should be blocked. 1159 */ 1160 private class UxRestrictionsListener extends ICarUxRestrictionsChangeListener.Stub { 1161 @GuardedBy("this") 1162 @Nullable 1163 private CarUxRestrictions mCurrentUxRestrictions; 1164 private final CarUxRestrictionsManagerService uxRestrictionsService; 1165 1166 public UxRestrictionsListener(CarUxRestrictionsManagerService service) { 1167 uxRestrictionsService = service; 1168 mCurrentUxRestrictions = uxRestrictionsService.getCurrentUxRestrictions(); 1169 } 1170 1171 @Override 1172 public void onUxRestrictionsChanged(CarUxRestrictions restrictions) { 1173 if (DBG_POLICY_CHECK) { 1174 Log.d(CarLog.TAG_PACKAGE, "Received uxr restrictions: " 1175 + restrictions.isRequiresDistractionOptimization() 1176 + " : " + restrictions.getActiveRestrictions()); 1177 } 1178 // We are not handling the restrictions until we know what is allowed and what is not. 1179 // This is to handle some situations, where car service is ready and getting sensor 1180 // data but we haven't received the boot complete intents. 1181 if (!mHasParsedPackages) { 1182 return; 1183 } 1184 1185 synchronized (this) { 1186 mCurrentUxRestrictions = new CarUxRestrictions(restrictions); 1187 } 1188 checkIfTopActivityNeedsBlocking(); 1189 } 1190 1191 private void checkIfTopActivityNeedsBlocking() { 1192 boolean shouldCheck = false; 1193 synchronized (this) { 1194 if (mCurrentUxRestrictions != null 1195 && mCurrentUxRestrictions.isRequiresDistractionOptimization()) { 1196 shouldCheck = true; 1197 } 1198 } 1199 if (DBG_POLICY_CHECK) { 1200 Log.d(CarLog.TAG_PACKAGE, "block?: " + shouldCheck); 1201 } 1202 if (shouldCheck) { 1203 blockTopActivitiesIfNecessary(); 1204 } 1205 } 1206 1207 private synchronized boolean isRestricted() { 1208 // if current restrictions is null, try querying the service, once. 1209 if (mCurrentUxRestrictions == null) { 1210 mCurrentUxRestrictions = uxRestrictionsService.getCurrentUxRestrictions(); 1211 } 1212 if (mCurrentUxRestrictions != null) { 1213 return mCurrentUxRestrictions.isRequiresDistractionOptimization(); 1214 } 1215 // If restriction information is still not available (could happen during bootup), 1216 // return not restricted. This maintains parity with previous implementation but needs 1217 // a revisit as we test more. 1218 return false; 1219 } 1220 } 1221 1222 /** 1223 * Listens to the Boot intent to initiate parsing installed packages. 1224 */ 1225 private class BootEventReceiver extends BroadcastReceiver { 1226 @Override 1227 public void onReceive(Context context, Intent intent) { 1228 if (intent == null || intent.getAction() == null) { 1229 return; 1230 } 1231 if (DBG_POLICY_CHECK) { 1232 Log.d(CarLog.TAG_PACKAGE, "BootEventReceiver Received " + intent.getAction()); 1233 } 1234 if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(intent.getAction())) { 1235 mHandler.requestParsingInstalledPkgs(0); 1236 mBootLockedIntentRx = true; 1237 } 1238 } 1239 } 1240 1241 /** 1242 * Listens to the package install/uninstall events to know when to initiate parsing 1243 * installed packages. 1244 */ 1245 private class PackageParsingEventReceiver extends BroadcastReceiver { 1246 private static final long PACKAGE_PARSING_DELAY_MS = 500; 1247 1248 @Override 1249 public void onReceive(Context context, Intent intent) { 1250 if (intent == null || intent.getAction() == null) { 1251 return; 1252 } 1253 if (DBG_POLICY_CHECK) { 1254 Log.d(CarLog.TAG_PACKAGE, 1255 "PackageParsingEventReceiver Received " + intent.getAction()); 1256 } 1257 String action = intent.getAction(); 1258 if (isPackageManagerAction(action)) { 1259 // send a delayed message so if we received multiple related intents, we parse 1260 // only once. 1261 logEventChange(intent); 1262 mHandler.requestParsingInstalledPkgs(PACKAGE_PARSING_DELAY_MS); 1263 } 1264 } 1265 1266 private boolean isPackageManagerAction(String action) { 1267 return mPackageManagerActions.indexOf(action) != -1; 1268 } 1269 1270 /** 1271 * Convenience log function to log what changed. Logs only when more debug logs 1272 * are needed - DBG_POLICY_CHECK needs to be true 1273 */ 1274 private void logEventChange(Intent intent) { 1275 if (!DBG_POLICY_CHECK || intent == null) { 1276 return; 1277 } 1278 1279 String packageName = intent.getData().getSchemeSpecificPart(); 1280 Log.d(CarLog.TAG_PACKAGE, "Pkg Changed:" + packageName); 1281 String action = intent.getAction(); 1282 if (action == null) { 1283 return; 1284 } 1285 if (action.equals(Intent.ACTION_PACKAGE_CHANGED)) { 1286 Log.d(CarLog.TAG_PACKAGE, "Changed components"); 1287 String[] cc = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); 1288 if (cc != null) { 1289 for (String c : cc) { 1290 Log.d(CarLog.TAG_PACKAGE, c); 1291 } 1292 } 1293 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) 1294 || action.equals(Intent.ACTION_PACKAGE_ADDED)) { 1295 Log.d(CarLog.TAG_PACKAGE, action + " Replacing?: " + intent.getBooleanExtra( 1296 Intent.EXTRA_REPLACING, false)); 1297 } 1298 } 1299 } 1300 } 1301