1 /* 2 * Copyright (C) 2021 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.tv.settings.library.device.apps; 18 19 import android.annotation.IntDef; 20 import android.app.ActivityManager; 21 import android.app.AppGlobals; 22 import android.app.Application; 23 import android.app.usage.StorageStats; 24 import android.app.usage.StorageStatsManager; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.IPackageManager; 31 import android.content.pm.IPackageStatsObserver; 32 import android.content.pm.ModuleInfo; 33 import android.content.pm.PackageManager; 34 import android.content.pm.PackageManager.NameNotFoundException; 35 import android.content.pm.PackageStats; 36 import android.content.pm.ParceledListSlice; 37 import android.content.pm.ResolveInfo; 38 import android.content.pm.UserInfo; 39 import android.graphics.drawable.Drawable; 40 import android.net.Uri; 41 import android.os.Handler; 42 import android.os.HandlerThread; 43 import android.os.Looper; 44 import android.os.Message; 45 import android.os.Process; 46 import android.os.RemoteException; 47 import android.os.SystemClock; 48 import android.os.UserHandle; 49 import android.os.UserManager; 50 import android.text.format.Formatter; 51 import android.util.Log; 52 import android.util.SparseArray; 53 54 import com.android.internal.annotations.VisibleForTesting; 55 import com.android.internal.util.ArrayUtils; 56 import com.android.tv.settings.library.util.LibUtils; 57 import com.android.tv.settings.library.util.ThreadUtils; 58 import com.android.tv.settings.library.util.lifecycle.Lifecycle; 59 import com.android.tv.settings.library.util.lifecycle.LifecycleObserver; 60 import com.android.tv.settings.library.util.lifecycle.events.OnDestroy; 61 import com.android.tv.settings.library.util.lifecycle.events.OnPause; 62 import com.android.tv.settings.library.util.lifecycle.events.OnResume; 63 64 import java.io.File; 65 import java.io.IOException; 66 import java.lang.annotation.Retention; 67 import java.lang.annotation.RetentionPolicy; 68 import java.lang.ref.WeakReference; 69 import java.text.Collator; 70 import java.text.Normalizer; 71 import java.text.Normalizer.Form; 72 import java.util.ArrayList; 73 import java.util.Collections; 74 import java.util.Comparator; 75 import java.util.HashMap; 76 import java.util.HashSet; 77 import java.util.List; 78 import java.util.Objects; 79 import java.util.UUID; 80 import java.util.regex.Pattern; 81 82 /** 83 * Keeps track of information about all installed applications, lazy-loading 84 * as needed. 85 */ 86 public class ApplicationsState { 87 private static final String TAG = "ApplicationsState"; 88 89 public static final int SIZE_UNKNOWN = -1; 90 public static final int SIZE_INVALID = -2; 91 92 // TODO(b/187728742): Migrate to use one flag. 93 private static final boolean DEBUG = false; 94 private static final boolean DEBUG_LOCKING = false; 95 private static final Object sLock = new Object(); 96 private static final Pattern REMOVE_DIACRITICALS_PATTERN 97 = Pattern.compile("\\p{InCombiningDiacriticalMarks}+"); 98 99 @VisibleForTesting 100 static ApplicationsState sInstance; 101 getInstance(Application app)102 public static ApplicationsState getInstance(Application app) { 103 return getInstance(app, AppGlobals.getPackageManager()); 104 } 105 106 @VisibleForTesting getInstance(Application app, IPackageManager iPackageManager)107 static ApplicationsState getInstance(Application app, IPackageManager iPackageManager) { 108 synchronized (sLock) { 109 if (sInstance == null) { 110 sInstance = new ApplicationsState(app, iPackageManager); 111 } 112 return sInstance; 113 } 114 } 115 116 final Context mContext; 117 final PackageManager mPm; 118 final IPackageManager mIpm; 119 final UserManager mUm; 120 final StorageStatsManager mStats; 121 final int mAdminRetrieveFlags; 122 final int mRetrieveFlags; 123 ApplicationsState.PackageIntentReceiver 124 mPackageIntentReceiver; 125 126 boolean mResumed; 127 boolean mHaveDisabledApps; 128 boolean mHaveInstantApps; 129 130 // Information about all applications. Synchronize on mEntriesMap 131 // to protect access to these. 132 final ArrayList<ApplicationsState.Session> mSessions = new ArrayList<>(); 133 final ArrayList<ApplicationsState.Session> mRebuildingSessions = new ArrayList<>(); 134 private InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges(); 135 // Map: userid => (Map: package name => AppEntry) 136 final SparseArray<HashMap<String, ApplicationsState.AppEntry>> mEntriesMap = 137 new SparseArray<>(); 138 final ArrayList<ApplicationsState.AppEntry> mAppEntries = new ArrayList<>(); 139 List<ApplicationInfo> mApplications = new ArrayList<>(); 140 long mCurId = 1; 141 UUID mCurComputingSizeUuid; 142 String mCurComputingSizePkg; 143 int mCurComputingSizeUserId; 144 boolean mSessionsChanged; 145 // Maps all installed modules on the system to whether they're hidden or not. 146 final HashMap<String, Boolean> mSystemModules = new HashMap<>(); 147 148 // Temporary for dispatching session callbacks. Only touched by main thread. 149 final ArrayList<WeakReference<ApplicationsState.Session>> mActiveSessions = new ArrayList<>(); 150 151 final HandlerThread mThread; 152 final ApplicationsState.BackgroundHandler 153 mBackgroundHandler; 154 final ApplicationsState.MainHandler 155 mMainHandler = new ApplicationsState.MainHandler(Looper.getMainLooper()); 156 157 /** Requests that the home app is loaded. */ 158 public static final int FLAG_SESSION_REQUEST_HOME_APP = 1 << 0; 159 160 /** Requests that icons are loaded. */ 161 public static final int FLAG_SESSION_REQUEST_ICONS = 1 << 1; 162 163 /** Requests that sizes are loaded. */ 164 public static final int FLAG_SESSION_REQUEST_SIZES = 1 << 2; 165 166 /** Requests that launcher intents are resolved. */ 167 public static final int FLAG_SESSION_REQUEST_LAUNCHER = 1 << 3; 168 169 /** Requests that leanback launcher intents are resolved. */ 170 public static final int FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER = 1 << 4; 171 172 /** 173 * Flags to configure the session to request various types of info. 174 */ 175 @IntDef( value = { 176 FLAG_SESSION_REQUEST_HOME_APP, 177 FLAG_SESSION_REQUEST_ICONS, 178 FLAG_SESSION_REQUEST_SIZES, 179 FLAG_SESSION_REQUEST_LAUNCHER, 180 FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER 181 }) 182 @Retention(RetentionPolicy.SOURCE) 183 public @interface SessionFlags { 184 } 185 186 @VisibleForTesting setInterestingConfigChanges(InterestingConfigChanges interestingConfigChanges)187 void setInterestingConfigChanges(InterestingConfigChanges interestingConfigChanges) { 188 mInterestingConfigChanges = interestingConfigChanges; 189 } 190 191 @ApplicationsState.SessionFlags 192 public static final int DEFAULT_SESSION_FLAGS = 193 FLAG_SESSION_REQUEST_HOME_APP | FLAG_SESSION_REQUEST_ICONS | 194 FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER; 195 ApplicationsState(Application app, IPackageManager iPackageManager)196 private ApplicationsState(Application app, IPackageManager iPackageManager) { 197 mContext = app; 198 mPm = mContext.getPackageManager(); 199 mIpm = iPackageManager; 200 mUm = mContext.getSystemService(UserManager.class); 201 mStats = mContext.getSystemService(StorageStatsManager.class); 202 for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) { 203 mEntriesMap.put(userId, new HashMap<>()); 204 } 205 206 mThread = new HandlerThread("ApplicationsState.Loader"); 207 mThread.start(); 208 mBackgroundHandler = new ApplicationsState.BackgroundHandler(mThread.getLooper()); 209 210 // Only the owner can see all apps. 211 mAdminRetrieveFlags = PackageManager.MATCH_ANY_USER | 212 PackageManager.MATCH_DISABLED_COMPONENTS | 213 PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; 214 mRetrieveFlags = PackageManager.MATCH_DISABLED_COMPONENTS | 215 PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; 216 217 final List<ModuleInfo> moduleInfos = mPm.getInstalledModules(0 /* flags */); 218 for (ModuleInfo info : moduleInfos) { 219 mSystemModules.put(info.getPackageName(), info.isHidden()); 220 } 221 222 /** 223 * This is a trick to prevent the foreground thread from being delayed. 224 * The problem is that Dalvik monitors are initially spin locks, to keep 225 * them lightweight. This leads to unfair contention -- Even though the 226 * background thread only holds the lock for a short amount of time, if 227 * it keeps running and locking again it can prevent the main thread from 228 * acquiring its lock for a long time... sometimes even > 5 seconds 229 * (leading to an ANR). 230 * 231 * Dalvik will promote a monitor to a "real" lock if it detects enough 232 * contention on it. It doesn't figure this out fast enough for us 233 * here, though, so this little trick will force it to turn into a real 234 * lock immediately. 235 */ 236 synchronized (mEntriesMap) { 237 try { 238 mEntriesMap.wait(1); 239 } catch (InterruptedException e) { 240 } 241 } 242 } 243 getBackgroundLooper()244 public Looper getBackgroundLooper() { 245 return mThread.getLooper(); 246 } 247 newSession( ApplicationsState.Callbacks callbacks)248 public ApplicationsState.Session newSession( 249 ApplicationsState.Callbacks callbacks) { 250 return newSession(callbacks, null); 251 } 252 newSession( ApplicationsState.Callbacks callbacks, Lifecycle lifecycle)253 public ApplicationsState.Session newSession( 254 ApplicationsState.Callbacks callbacks, Lifecycle lifecycle) { 255 ApplicationsState.Session 256 s = new ApplicationsState.Session(callbacks, lifecycle); 257 synchronized (mEntriesMap) { 258 mSessions.add(s); 259 } 260 return s; 261 } 262 doResumeIfNeededLocked()263 void doResumeIfNeededLocked() { 264 if (mResumed) { 265 return; 266 } 267 mResumed = true; 268 if (mPackageIntentReceiver == null) { 269 mPackageIntentReceiver = new ApplicationsState.PackageIntentReceiver(); 270 mPackageIntentReceiver.registerReceiver(); 271 } 272 273 final List<ApplicationInfo> prevApplications = mApplications; 274 mApplications = new ArrayList<>(); 275 for (UserInfo user : mUm.getProfiles(UserHandle.myUserId())) { 276 try { 277 // If this user is new, it needs a map created. 278 if (mEntriesMap.indexOfKey(user.id) < 0) { 279 mEntriesMap.put(user.id, new HashMap<>()); 280 } 281 @SuppressWarnings("unchecked") 282 ParceledListSlice<ApplicationInfo> list = 283 mIpm.getInstalledApplications( 284 user.isAdmin() ? mAdminRetrieveFlags : mRetrieveFlags, 285 user.id); 286 mApplications.addAll(list.getList()); 287 } catch (Exception e) { 288 Log.e(TAG, "Error during doResumeIfNeededLocked", e); 289 } 290 } 291 292 if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) { 293 // If an interesting part of the configuration has changed, we 294 // should completely reload the app entries. 295 clearEntries(); 296 } else { 297 for (int i = 0; i < mAppEntries.size(); i++) { 298 mAppEntries.get(i).sizeStale = true; 299 } 300 } 301 302 mHaveDisabledApps = false; 303 mHaveInstantApps = false; 304 for (int i = 0; i < mApplications.size(); i++) { 305 final ApplicationInfo info = mApplications.get(i); 306 // Need to trim out any applications that are disabled by 307 // something different than the user. 308 if (!info.enabled) { 309 if (info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { 310 mApplications.remove(i); 311 i--; 312 continue; 313 } 314 mHaveDisabledApps = true; 315 } 316 if (isHiddenModule(info.packageName)) { 317 mApplications.remove(i--); 318 continue; 319 } 320 if (!mHaveInstantApps && AppUtils.isInstant(info)) { 321 mHaveInstantApps = true; 322 } 323 324 int userId = UserHandle.getUserId(info.uid); 325 final ApplicationsState.AppEntry entry = mEntriesMap.get(userId).get(info.packageName); 326 if (entry != null) { 327 entry.info = info; 328 } 329 } 330 331 if (anyAppIsRemoved(prevApplications, mApplications)) { 332 // some apps have been uninstalled. 333 clearEntries(); 334 } 335 mCurComputingSizePkg = null; 336 if (!mBackgroundHandler.hasMessages( 337 ApplicationsState.BackgroundHandler.MSG_LOAD_ENTRIES)) { 338 mBackgroundHandler.sendEmptyMessage( 339 ApplicationsState.BackgroundHandler.MSG_LOAD_ENTRIES); 340 } 341 } 342 343 /* The original design is mAppEntries.size() > mApplications.size(). 344 It's correct if there is only the owner user and only one app is removed. 345 Problem 1: 346 If there is a user profile, the size of mAppEntries < mApplications is normal because 347 the number of app entries on UI (mAppEntries) should be equal to the number of apps got 348 from PMS (mApplications). 349 350 owner only case: 351 mApplications: user 0: 191 352 mAppEntries : user 0: 191 353 total mAppEntries: 191, mApplications: 191 354 If an app is removed, cached mAppEntries: 191 , mApplications: 191 -> 190, it is detected 355 as the number of apps becomes less. 356 357 If there is a work profile, mAppEntries removes some apps that are not installed for the 358 owner user. 359 360 For example, in the following case, 6 apps are removed from mAppEntries for the owner. 361 mApplications: user 0: 197, user 10: 189 => total 386 362 mAppEntries : user 0: 191, user 10: 189 => total 380 363 If an app is removed, cached mAppEntries: 380 , mApplications: 386 -> 385, the size of 364 mAppEntries is still not larger than mApplications, then does not clear mAppEntries. 365 366 Problem 2: 367 If remove an app and add another app outside Settings (e.g. Play Store) and back to 368 Settings, the amount of apps are not changed, it causes the entries keep the removed app. 369 370 Another case, if adding more apps than removing apps (e.g. add 2 apps and remove 1 app), 371 the final number of apps (mApplications) is even increased, 372 373 Therefore, should not only count on number of apps to determine any app is removed. 374 Compare the change of applications instead. 375 */ anyAppIsRemoved(List<ApplicationInfo> prevApplications, List<ApplicationInfo> applications)376 private static boolean anyAppIsRemoved(List<ApplicationInfo> prevApplications, 377 List<ApplicationInfo> applications) { 378 379 // No cache 380 if (prevApplications.size() == 0) { 381 return false; 382 } 383 384 if (applications.size() < prevApplications.size()) { 385 return true; 386 } 387 388 // build package sets of all applications <userId, HashSet of packages> 389 final HashMap<String, HashSet<String>> packageMap = new HashMap<>(); 390 for (ApplicationInfo application : applications) { 391 final String userId = String.valueOf(UserHandle.getUserId(application.uid)); 392 393 HashSet<String> appPackages = packageMap.get(userId); 394 if (appPackages == null) { 395 appPackages = new HashSet<>(); 396 packageMap.put(userId, appPackages); 397 } 398 if (hasFlag(application.flags, ApplicationInfo.FLAG_INSTALLED)) { 399 appPackages.add(application.packageName); 400 } 401 } 402 403 // detect any previous app is removed 404 for (ApplicationInfo prevApplication : prevApplications) { 405 if (!hasFlag(prevApplication.flags, ApplicationInfo.FLAG_INSTALLED)) { 406 continue; 407 } 408 final String userId = String.valueOf(UserHandle.getUserId(prevApplication.uid)); 409 410 final HashSet<String> packagesSet = packageMap.get(userId); 411 if (packagesSet == null || !packagesSet.remove(prevApplication.packageName)) { 412 return true; 413 } 414 } 415 416 return false; 417 } 418 419 @VisibleForTesting clearEntries()420 void clearEntries() { 421 for (int i = 0; i < mEntriesMap.size(); i++) { 422 mEntriesMap.valueAt(i).clear(); 423 } 424 mAppEntries.clear(); 425 } 426 haveDisabledApps()427 public boolean haveDisabledApps() { 428 return mHaveDisabledApps; 429 } 430 haveInstantApps()431 public boolean haveInstantApps() { 432 return mHaveInstantApps; 433 } 434 isHiddenModule(String packageName)435 boolean isHiddenModule(String packageName) { 436 Boolean isHidden = mSystemModules.get(packageName); 437 if (isHidden == null) { 438 return false; 439 } 440 441 return isHidden; 442 } 443 isSystemModule(String packageName)444 boolean isSystemModule(String packageName) { 445 return mSystemModules.containsKey(packageName); 446 } 447 doPauseIfNeededLocked()448 void doPauseIfNeededLocked() { 449 if (!mResumed) { 450 return; 451 } 452 for (int i = 0; i < mSessions.size(); i++) { 453 if (mSessions.get(i).mResumed) { 454 return; 455 } 456 } 457 doPauseLocked(); 458 } 459 doPauseLocked()460 void doPauseLocked() { 461 mResumed = false; 462 if (mPackageIntentReceiver != null) { 463 mPackageIntentReceiver.unregisterReceiver(); 464 mPackageIntentReceiver = null; 465 } 466 } 467 getEntry(String packageName, int userId)468 public ApplicationsState.AppEntry getEntry(String packageName, int userId) { 469 if (DEBUG_LOCKING) Log.v(TAG, "getEntry about to acquire lock..."); 470 synchronized (mEntriesMap) { 471 ApplicationsState.AppEntry entry = mEntriesMap.get(userId).get(packageName); 472 if (entry == null) { 473 ApplicationInfo info = getAppInfoLocked(packageName, userId); 474 if (info == null) { 475 try { 476 info = mIpm.getApplicationInfo(packageName, 0, userId); 477 } catch (RemoteException e) { 478 Log.w(TAG, "getEntry couldn't reach PackageManager", e); 479 return null; 480 } 481 } 482 if (info != null) { 483 entry = getEntryLocked(info); 484 } 485 } 486 if (DEBUG_LOCKING) Log.v(TAG, "...getEntry releasing lock"); 487 return entry; 488 } 489 } 490 getAppInfoLocked(String pkg, int userId)491 private ApplicationInfo getAppInfoLocked(String pkg, int userId) { 492 for (int i = 0; i < mApplications.size(); i++) { 493 ApplicationInfo info = mApplications.get(i); 494 if (pkg.equals(info.packageName) 495 && userId == UserHandle.getUserId(info.uid)) { 496 return info; 497 } 498 } 499 return null; 500 } 501 ensureIcon(ApplicationsState.AppEntry entry)502 public void ensureIcon(ApplicationsState.AppEntry entry) { 503 if (entry.icon != null) { 504 return; 505 } 506 synchronized (entry) { 507 entry.ensureIconLocked(mContext); 508 } 509 } 510 511 /** 512 * To generate and cache the label description. 513 * 514 * @param entry contain the entries of an app 515 */ ensureLabelDescription( ApplicationsState.AppEntry entry)516 public void ensureLabelDescription( 517 ApplicationsState.AppEntry entry) { 518 if (entry.labelDescription != null) { 519 return; 520 } 521 synchronized (entry) { 522 entry.ensureLabelDescriptionLocked(mContext); 523 } 524 } 525 requestSize(String packageName, int userId)526 public void requestSize(String packageName, int userId) { 527 if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock..."); 528 synchronized (mEntriesMap) { 529 ApplicationsState.AppEntry entry = mEntriesMap.get(userId).get(packageName); 530 if (entry != null && hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)) { 531 mBackgroundHandler.post( 532 () -> { 533 try { 534 final StorageStats stats = 535 mStats.queryStatsForPackage( 536 entry.info.storageUuid, 537 packageName, 538 UserHandle.of(userId)); 539 final long cacheQuota = 540 mStats.getCacheQuotaBytes( 541 entry.info.storageUuid.toString(), entry.info.uid); 542 final PackageStats legacy = new PackageStats(packageName, userId); 543 legacy.codeSize = stats.getAppBytes(); 544 legacy.dataSize = stats.getDataBytes(); 545 legacy.cacheSize = Math.min(stats.getCacheBytes(), cacheQuota); 546 try { 547 mBackgroundHandler.mStatsObserver.onGetStatsCompleted( 548 legacy, true); 549 } catch (RemoteException ignored) { 550 } 551 } catch (NameNotFoundException | IOException e) { 552 Log.w(TAG, "Failed to query stats: " + e); 553 try { 554 mBackgroundHandler.mStatsObserver.onGetStatsCompleted( 555 null, false); 556 } catch (RemoteException ignored) { 557 } 558 } 559 }); 560 } 561 if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock"); 562 } 563 } 564 sumCacheSizes()565 long sumCacheSizes() { 566 long sum = 0; 567 if (DEBUG_LOCKING) Log.v(TAG, "sumCacheSizes about to acquire lock..."); 568 synchronized (mEntriesMap) { 569 if (DEBUG_LOCKING) Log.v(TAG, "-> sumCacheSizes now has lock"); 570 for (int i = mAppEntries.size() - 1; i >= 0; i--) { 571 sum += mAppEntries.get(i).cacheSize; 572 } 573 if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock"); 574 } 575 return sum; 576 } 577 indexOfApplicationInfoLocked(String pkgName, int userId)578 int indexOfApplicationInfoLocked(String pkgName, int userId) { 579 for (int i = mApplications.size() - 1; i >= 0; i--) { 580 ApplicationInfo appInfo = mApplications.get(i); 581 if (appInfo.packageName.equals(pkgName) 582 && UserHandle.getUserId(appInfo.uid) == userId) { 583 return i; 584 } 585 } 586 return -1; 587 } 588 addPackage(String pkgName, int userId)589 void addPackage(String pkgName, int userId) { 590 try { 591 synchronized (mEntriesMap) { 592 if (DEBUG_LOCKING) Log.v(TAG, "addPackage acquired lock"); 593 if (DEBUG) Log.i(TAG, "Adding package " + pkgName); 594 if (!mResumed) { 595 // If we are not resumed, we will do a full query the 596 // next time we resume, so there is no reason to do work 597 // here. 598 if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: not resumed"); 599 return; 600 } 601 if (indexOfApplicationInfoLocked(pkgName, userId) >= 0) { 602 if (DEBUG) Log.i(TAG, "Package already exists!"); 603 if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: already exists"); 604 return; 605 } 606 ApplicationInfo info = mIpm.getApplicationInfo(pkgName, 607 mUm.isUserAdmin(userId) ? mAdminRetrieveFlags : mRetrieveFlags, 608 userId); 609 if (info == null) { 610 return; 611 } 612 if (!info.enabled) { 613 if (info.enabledSetting 614 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { 615 return; 616 } 617 mHaveDisabledApps = true; 618 } 619 if (AppUtils.isInstant(info)) { 620 mHaveInstantApps = true; 621 } 622 mApplications.add(info); 623 if (!mBackgroundHandler.hasMessages( 624 ApplicationsState.BackgroundHandler.MSG_LOAD_ENTRIES)) { 625 mBackgroundHandler.sendEmptyMessage( 626 ApplicationsState.BackgroundHandler.MSG_LOAD_ENTRIES); 627 } 628 if (!mMainHandler.hasMessages( 629 ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED)) { 630 mMainHandler.sendEmptyMessage( 631 ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED); 632 } 633 if (DEBUG_LOCKING) Log.v(TAG, "addPackage releasing lock"); 634 } 635 } catch (RemoteException e) { 636 } 637 } 638 removePackage(String pkgName, int userId)639 public void removePackage(String pkgName, int userId) { 640 synchronized (mEntriesMap) { 641 if (DEBUG_LOCKING) Log.v(TAG, "removePackage acquired lock"); 642 int idx = indexOfApplicationInfoLocked(pkgName, userId); 643 if (DEBUG) Log.i(TAG, "removePackage: " + pkgName + " @ " + idx); 644 if (idx >= 0) { 645 ApplicationsState.AppEntry entry = mEntriesMap.get(userId).get(pkgName); 646 if (DEBUG) Log.i(TAG, "removePackage: " + entry); 647 if (entry != null) { 648 mEntriesMap.get(userId).remove(pkgName); 649 mAppEntries.remove(entry); 650 } 651 ApplicationInfo info = mApplications.get(idx); 652 mApplications.remove(idx); 653 if (!info.enabled) { 654 mHaveDisabledApps = false; 655 for (ApplicationInfo otherInfo : mApplications) { 656 if (!otherInfo.enabled) { 657 mHaveDisabledApps = true; 658 break; 659 } 660 } 661 } 662 if (AppUtils.isInstant(info)) { 663 mHaveInstantApps = false; 664 for (ApplicationInfo otherInfo : mApplications) { 665 if (AppUtils.isInstant(otherInfo)) { 666 mHaveInstantApps = true; 667 break; 668 } 669 } 670 } 671 if (!mMainHandler.hasMessages( 672 ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED)) { 673 mMainHandler.sendEmptyMessage( 674 ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED); 675 } 676 } 677 if (DEBUG_LOCKING) Log.v(TAG, "removePackage releasing lock"); 678 } 679 } 680 invalidatePackage(String pkgName, int userId)681 public void invalidatePackage(String pkgName, int userId) { 682 removePackage(pkgName, userId); 683 addPackage(pkgName, userId); 684 } 685 addUser(int userId)686 private void addUser(int userId) { 687 final int[] profileIds = mUm.getProfileIdsWithDisabled(UserHandle.myUserId()); 688 if (ArrayUtils.contains(profileIds, userId)) { 689 synchronized (mEntriesMap) { 690 mEntriesMap.put(userId, new HashMap<String, ApplicationsState.AppEntry>()); 691 if (mResumed) { 692 // If resumed, Manually pause, then cause a resume to repopulate the app list. 693 // This is the simplest way to reload the packages so that the new user 694 // is included. Otherwise the list will be repopulated on next resume. 695 doPauseLocked(); 696 doResumeIfNeededLocked(); 697 } 698 if (!mMainHandler.hasMessages( 699 ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED)) { 700 mMainHandler.sendEmptyMessage( 701 ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED); 702 } 703 } 704 } 705 } 706 removeUser(int userId)707 private void removeUser(int userId) { 708 synchronized (mEntriesMap) { 709 HashMap<String, ApplicationsState.AppEntry> userMap = mEntriesMap.get(userId); 710 if (userMap != null) { 711 for (ApplicationsState.AppEntry appEntry : userMap.values()) { 712 mAppEntries.remove(appEntry); 713 mApplications.remove(appEntry.info); 714 } 715 mEntriesMap.remove(userId); 716 if (!mMainHandler.hasMessages( 717 ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED)) { 718 mMainHandler.sendEmptyMessage( 719 ApplicationsState.MainHandler.MSG_PACKAGE_LIST_CHANGED); 720 } 721 } 722 } 723 } 724 getEntryLocked(ApplicationInfo info)725 private ApplicationsState.AppEntry getEntryLocked(ApplicationInfo info) { 726 int userId = UserHandle.getUserId(info.uid); 727 ApplicationsState.AppEntry entry = mEntriesMap.get(userId).get(info.packageName); 728 if (DEBUG) { 729 Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry); 730 } 731 if (entry == null) { 732 if (isHiddenModule(info.packageName)) { 733 if (DEBUG) { 734 Log.i(TAG, "No AppEntry for " + info.packageName + " (hidden module)"); 735 } 736 return null; 737 } 738 if (DEBUG) { 739 Log.i(TAG, "Creating AppEntry for " + info.packageName); 740 } 741 entry = new ApplicationsState.AppEntry(mContext, info, mCurId++); 742 mEntriesMap.get(userId).put(info.packageName, entry); 743 mAppEntries.add(entry); 744 } else if (entry.info != info) { 745 entry.info = info; 746 } 747 return entry; 748 } 749 750 // -------------------------------------------------------------- 751 getTotalInternalSize(PackageStats ps)752 private long getTotalInternalSize(PackageStats ps) { 753 if (ps != null) { 754 // We subtract the cache size because the system can clear it automatically and 755 // |dataSize| is a superset of |cacheSize|. 756 return ps.codeSize + ps.dataSize - ps.cacheSize; 757 } 758 return SIZE_INVALID; 759 } 760 getTotalExternalSize(PackageStats ps)761 private long getTotalExternalSize(PackageStats ps) { 762 if (ps != null) { 763 // We also include the cache size here because for non-emulated 764 // we don't automatically clean cache files. 765 return ps.externalCodeSize + ps.externalDataSize 766 + ps.externalCacheSize 767 + ps.externalMediaSize + ps.externalObbSize; 768 } 769 return SIZE_INVALID; 770 } 771 getSizeStr(long size)772 private String getSizeStr(long size) { 773 if (size >= 0) { 774 return Formatter.formatFileSize(mContext, size); 775 } 776 return null; 777 } 778 rebuildActiveSessions()779 void rebuildActiveSessions() { 780 synchronized (mEntriesMap) { 781 if (!mSessionsChanged) { 782 return; 783 } 784 mActiveSessions.clear(); 785 for (int i = 0; i < mSessions.size(); i++) { 786 ApplicationsState.Session s = mSessions.get(i); 787 if (s.mResumed) { 788 mActiveSessions.add(new WeakReference<>(s)); 789 } 790 } 791 } 792 } 793 normalize(String str)794 public static String normalize(String str) { 795 String tmp = Normalizer.normalize(str, Form.NFD); 796 return REMOVE_DIACRITICALS_PATTERN.matcher(tmp) 797 .replaceAll("").toLowerCase(); 798 } 799 800 public class Session implements LifecycleObserver, OnResume, OnPause, OnDestroy { 801 802 final ApplicationsState.Callbacks mCallbacks; 803 boolean mResumed; 804 805 // Rebuilding of app list. Synchronized on mRebuildSync. 806 final Object mRebuildSync = new Object(); 807 boolean mRebuildRequested; 808 boolean mRebuildAsync; 809 ApplicationsState.AppFilter mRebuildFilter; 810 Comparator<ApplicationsState.AppEntry> mRebuildComparator; 811 ArrayList<ApplicationsState.AppEntry> mRebuildResult; 812 ArrayList<ApplicationsState.AppEntry> mLastAppList; 813 boolean mRebuildForeground; 814 815 private final boolean mHasLifecycle; 816 @ApplicationsState.SessionFlags 817 private int mFlags = DEFAULT_SESSION_FLAGS; 818 Session(ApplicationsState.Callbacks callbacks, Lifecycle lifecycle)819 Session(ApplicationsState.Callbacks callbacks, Lifecycle lifecycle) { 820 mCallbacks = callbacks; 821 if (lifecycle != null) { 822 lifecycle.addObserver(this); 823 mHasLifecycle = true; 824 } else { 825 mHasLifecycle = false; 826 } 827 } 828 829 @ApplicationsState.SessionFlags getSessionFlags()830 public int getSessionFlags() { 831 return mFlags; 832 } 833 setSessionFlags(@pplicationsState.SessionFlags int flags)834 public void setSessionFlags(@ApplicationsState.SessionFlags int flags) { 835 mFlags = flags; 836 } 837 onResume()838 public void onResume() { 839 if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock..."); 840 synchronized (mEntriesMap) { 841 if (!mResumed) { 842 mResumed = true; 843 mSessionsChanged = true; 844 doPauseLocked(); 845 doResumeIfNeededLocked(); 846 } 847 } 848 if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock"); 849 } 850 onPause()851 public void onPause() { 852 if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock..."); 853 synchronized (mEntriesMap) { 854 if (mResumed) { 855 mResumed = false; 856 mSessionsChanged = true; 857 mBackgroundHandler.removeMessages( 858 ApplicationsState.BackgroundHandler.MSG_REBUILD_LIST, this); 859 doPauseIfNeededLocked(); 860 } 861 if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock"); 862 } 863 } 864 getAllApps()865 public ArrayList<ApplicationsState.AppEntry> getAllApps() { 866 synchronized (mEntriesMap) { 867 return new ArrayList<>(mAppEntries); 868 } 869 } 870 871 // Creates a new list of app entries with the given filter and comparator. rebuild( ApplicationsState.AppFilter filter, Comparator<ApplicationsState.AppEntry> comparator)872 public ArrayList<ApplicationsState.AppEntry> rebuild( 873 ApplicationsState.AppFilter filter, 874 Comparator<ApplicationsState.AppEntry> comparator) { 875 return rebuild(filter, comparator, true); 876 } 877 rebuild( ApplicationsState.AppFilter filter, Comparator<ApplicationsState.AppEntry> comparator, boolean foreground)878 public ArrayList<ApplicationsState.AppEntry> rebuild( 879 ApplicationsState.AppFilter filter, 880 Comparator<ApplicationsState.AppEntry> comparator, 881 boolean foreground) { 882 synchronized (mRebuildSync) { 883 synchronized (mRebuildingSessions) { 884 mRebuildingSessions.add(this); 885 mRebuildRequested = true; 886 mRebuildAsync = true; 887 mRebuildFilter = filter; 888 mRebuildComparator = comparator; 889 mRebuildForeground = foreground; 890 mRebuildResult = null; 891 if (!mBackgroundHandler.hasMessages( 892 ApplicationsState.BackgroundHandler.MSG_REBUILD_LIST)) { 893 Message msg = mBackgroundHandler.obtainMessage( 894 ApplicationsState.BackgroundHandler.MSG_REBUILD_LIST); 895 mBackgroundHandler.sendMessage(msg); 896 } 897 } 898 899 return null; 900 } 901 } 902 handleRebuildList()903 void handleRebuildList() { 904 ApplicationsState.AppFilter filter; 905 Comparator<ApplicationsState.AppEntry> comparator; 906 907 if (!mResumed) { 908 return; 909 } 910 synchronized (mRebuildSync) { 911 if (!mRebuildRequested) { 912 return; 913 } 914 915 filter = mRebuildFilter; 916 comparator = mRebuildComparator; 917 mRebuildRequested = false; 918 mRebuildFilter = null; 919 mRebuildComparator = null; 920 if (mRebuildForeground) { 921 Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); 922 mRebuildForeground = false; 923 } 924 } 925 926 if (filter != null) { 927 filter.init(mContext); 928 } 929 930 final List<ApplicationsState.AppEntry> apps; 931 synchronized (mEntriesMap) { 932 apps = new ArrayList<>(mAppEntries); 933 } 934 935 ArrayList<ApplicationsState.AppEntry> filteredApps = new ArrayList<>(); 936 if (DEBUG) { 937 Log.i(TAG, "Rebuilding..."); 938 } 939 for (ApplicationsState.AppEntry entry : apps) { 940 if (entry != null && (filter == null || filter.filterApp(entry))) { 941 synchronized (mEntriesMap) { 942 if (DEBUG_LOCKING) { 943 Log.v(TAG, "rebuild acquired lock"); 944 } 945 if (comparator != null) { 946 // Only need the label if we are going to be sorting. 947 entry.ensureLabel(mContext); 948 } 949 if (DEBUG) { 950 Log.i(TAG, "Using " + entry.info.packageName + ": " + entry); 951 } 952 filteredApps.add(entry); 953 if (DEBUG_LOCKING) { 954 Log.v(TAG, "rebuild releasing lock"); 955 } 956 } 957 } 958 } 959 960 if (comparator != null) { 961 synchronized (mEntriesMap) { 962 // Locking to ensure that the background handler does not mutate 963 // the size of AppEntries used for ordering while sorting. 964 Collections.sort(filteredApps, comparator); 965 } 966 } 967 968 synchronized (mRebuildSync) { 969 if (!mRebuildRequested) { 970 mLastAppList = filteredApps; 971 if (!mRebuildAsync) { 972 mRebuildResult = filteredApps; 973 mRebuildSync.notifyAll(); 974 } else { 975 if (!mMainHandler.hasMessages( 976 ApplicationsState.MainHandler.MSG_REBUILD_COMPLETE, this)) { 977 Message msg = mMainHandler.obtainMessage( 978 ApplicationsState.MainHandler.MSG_REBUILD_COMPLETE, this); 979 mMainHandler.sendMessage(msg); 980 } 981 } 982 } 983 } 984 985 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 986 } 987 onDestroy()988 public void onDestroy() { 989 if (!mHasLifecycle) { 990 // TODO: Legacy, remove this later once all usages are switched to Lifecycle 991 onPause(); 992 } 993 synchronized (mEntriesMap) { 994 mSessions.remove(this); 995 } 996 } 997 } 998 999 class MainHandler extends Handler { 1000 static final int MSG_REBUILD_COMPLETE = 1; 1001 static final int MSG_PACKAGE_LIST_CHANGED = 2; 1002 static final int MSG_PACKAGE_ICON_CHANGED = 3; 1003 static final int MSG_PACKAGE_SIZE_CHANGED = 4; 1004 static final int MSG_ALL_SIZES_COMPUTED = 5; 1005 static final int MSG_RUNNING_STATE_CHANGED = 6; 1006 static final int MSG_LAUNCHER_INFO_CHANGED = 7; 1007 static final int MSG_LOAD_ENTRIES_COMPLETE = 8; 1008 MainHandler(Looper looper)1009 public MainHandler(Looper looper) { 1010 super(looper); 1011 } 1012 1013 @Override handleMessage(Message msg)1014 public void handleMessage(Message msg) { 1015 rebuildActiveSessions(); 1016 switch (msg.what) { 1017 case MSG_REBUILD_COMPLETE: { 1018 ApplicationsState.Session 1019 s = (ApplicationsState.Session) msg.obj; 1020 for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) { 1021 final ApplicationsState.Session session = sessionRef.get(); 1022 if (session != null && session == s) { 1023 s.mCallbacks.onRebuildComplete(s.mLastAppList); 1024 } 1025 } 1026 } 1027 break; 1028 case MSG_PACKAGE_LIST_CHANGED: { 1029 for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) { 1030 final ApplicationsState.Session session = sessionRef.get(); 1031 if (session != null) { 1032 session.mCallbacks.onPackageListChanged(); 1033 } 1034 } 1035 } 1036 break; 1037 case MSG_PACKAGE_ICON_CHANGED: { 1038 for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) { 1039 final ApplicationsState.Session session = sessionRef.get(); 1040 if (session != null) { 1041 session.mCallbacks.onPackageIconChanged(); 1042 } 1043 } 1044 } 1045 break; 1046 case MSG_PACKAGE_SIZE_CHANGED: { 1047 for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) { 1048 final ApplicationsState.Session session = sessionRef.get(); 1049 if (session != null) { 1050 session.mCallbacks.onPackageSizeChanged( 1051 (String) msg.obj); 1052 } 1053 } 1054 } 1055 break; 1056 case MSG_ALL_SIZES_COMPUTED: { 1057 for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) { 1058 final ApplicationsState.Session session = sessionRef.get(); 1059 if (session != null) { 1060 session.mCallbacks.onAllSizesComputed(); 1061 } 1062 } 1063 } 1064 break; 1065 case MSG_RUNNING_STATE_CHANGED: { 1066 for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) { 1067 final ApplicationsState.Session session = sessionRef.get(); 1068 if (session != null) { 1069 session.mCallbacks.onRunningStateChanged( 1070 msg.arg1 != 0); 1071 } 1072 } 1073 } 1074 break; 1075 case MSG_LAUNCHER_INFO_CHANGED: { 1076 for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) { 1077 final ApplicationsState.Session session = sessionRef.get(); 1078 if (session != null) { 1079 session.mCallbacks.onLauncherInfoChanged(); 1080 } 1081 } 1082 } 1083 break; 1084 case MSG_LOAD_ENTRIES_COMPLETE: { 1085 for (WeakReference<ApplicationsState.Session> sessionRef : mActiveSessions) { 1086 final ApplicationsState.Session session = sessionRef.get(); 1087 if (session != null) { 1088 session.mCallbacks.onLoadEntriesCompleted(); 1089 } 1090 } 1091 } 1092 break; 1093 } 1094 } 1095 } 1096 1097 private class BackgroundHandler extends Handler { 1098 static final int MSG_REBUILD_LIST = 1; 1099 static final int MSG_LOAD_ENTRIES = 2; 1100 static final int MSG_LOAD_HOME_APP = 3; 1101 static final int MSG_LOAD_LAUNCHER = 4; 1102 static final int MSG_LOAD_LEANBACK_LAUNCHER = 5; 1103 static final int MSG_LOAD_ICONS = 6; 1104 static final int MSG_LOAD_SIZES = 7; 1105 1106 boolean mRunning; 1107 BackgroundHandler(Looper looper)1108 BackgroundHandler(Looper looper) { 1109 super(looper); 1110 } 1111 1112 @Override handleMessage(Message msg)1113 public void handleMessage(Message msg) { 1114 // Always try rebuilding list first thing, if needed. 1115 ArrayList<ApplicationsState.Session> rebuildingSessions = null; 1116 synchronized (mRebuildingSessions) { 1117 if (mRebuildingSessions.size() > 0) { 1118 rebuildingSessions = new ArrayList<ApplicationsState.Session>( 1119 mRebuildingSessions); 1120 mRebuildingSessions.clear(); 1121 } 1122 } 1123 if (rebuildingSessions != null) { 1124 for (ApplicationsState.Session session : rebuildingSessions) { 1125 session.handleRebuildList(); 1126 } 1127 } 1128 1129 int flags = getCombinedSessionFlags(mSessions); 1130 1131 switch (msg.what) { 1132 case MSG_REBUILD_LIST: { 1133 } 1134 break; 1135 case MSG_LOAD_ENTRIES: { 1136 int numDone = 0; 1137 synchronized (mEntriesMap) { 1138 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES acquired lock"); 1139 for (int i = 0; i < mApplications.size() && numDone < 6; i++) { 1140 if (!mRunning) { 1141 mRunning = true; 1142 Message m = mMainHandler.obtainMessage( 1143 ApplicationsState.MainHandler.MSG_RUNNING_STATE_CHANGED, 1); 1144 mMainHandler.sendMessage(m); 1145 } 1146 ApplicationInfo info = mApplications.get(i); 1147 int userId = UserHandle.getUserId(info.uid); 1148 if (mEntriesMap.get(userId).get(info.packageName) == null) { 1149 numDone++; 1150 getEntryLocked(info); 1151 } 1152 if (userId != 0 && mEntriesMap.indexOfKey(0) >= 0) { 1153 // If this app is for a profile and we are on the owner, remove 1154 // the owner entry if it isn't installed. This will prevent 1155 // duplicates of work only apps showing up as 'not installed 1156 // for this user'. 1157 // Note: This depends on us traversing the users in order, which 1158 // happens because of the way we generate the list in 1159 // doResumeIfNeededLocked. 1160 ApplicationsState.AppEntry 1161 entry = mEntriesMap.get(0).get(info.packageName); 1162 if (entry != null && !hasFlag(entry.info.flags, 1163 ApplicationInfo.FLAG_INSTALLED)) { 1164 mEntriesMap.get(0).remove(info.packageName); 1165 mAppEntries.remove(entry); 1166 } 1167 } 1168 } 1169 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock"); 1170 } 1171 1172 if (numDone >= 6) { 1173 sendEmptyMessage(MSG_LOAD_ENTRIES); 1174 } else { 1175 if (!mMainHandler.hasMessages( 1176 ApplicationsState.MainHandler.MSG_LOAD_ENTRIES_COMPLETE)) { 1177 mMainHandler.sendEmptyMessage( 1178 ApplicationsState.MainHandler.MSG_LOAD_ENTRIES_COMPLETE); 1179 } 1180 sendEmptyMessage(MSG_LOAD_HOME_APP); 1181 } 1182 } 1183 break; 1184 case MSG_LOAD_HOME_APP: { 1185 if (hasFlag(flags, FLAG_SESSION_REQUEST_HOME_APP)) { 1186 final List<ResolveInfo> homeActivities = new ArrayList<>(); 1187 mPm.getHomeActivities(homeActivities); 1188 synchronized (mEntriesMap) { 1189 final int entryCount = mEntriesMap.size(); 1190 for (int i = 0; i < entryCount; i++) { 1191 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock"); 1192 final HashMap<String, ApplicationsState.AppEntry> userEntries = 1193 mEntriesMap.valueAt( 1194 i); 1195 for (ResolveInfo activity : homeActivities) { 1196 String packageName = activity.activityInfo.packageName; 1197 ApplicationsState.AppEntry 1198 entry = userEntries.get(packageName); 1199 if (entry != null) { 1200 entry.isHomeApp = true; 1201 } 1202 } 1203 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock"); 1204 } 1205 } 1206 } 1207 sendEmptyMessage(MSG_LOAD_LAUNCHER); 1208 } 1209 break; 1210 case MSG_LOAD_LAUNCHER: 1211 case MSG_LOAD_LEANBACK_LAUNCHER: { 1212 if ((msg.what == MSG_LOAD_LAUNCHER && 1213 hasFlag(flags, FLAG_SESSION_REQUEST_LAUNCHER)) 1214 || (msg.what == MSG_LOAD_LEANBACK_LAUNCHER && 1215 hasFlag(flags, FLAG_SESSION_REQUEST_LEANBACK_LAUNCHER))) { 1216 1217 Intent launchIntent = new Intent(Intent.ACTION_MAIN, null); 1218 launchIntent.addCategory(msg.what == MSG_LOAD_LAUNCHER 1219 ? Intent.CATEGORY_LAUNCHER : Intent.CATEGORY_LEANBACK_LAUNCHER); 1220 for (int i = 0; i < mEntriesMap.size(); i++) { 1221 int userId = mEntriesMap.keyAt(i); 1222 // If we do not specify MATCH_DIRECT_BOOT_AWARE or 1223 // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags 1224 // according to the user's lock state. When the user is locked, 1225 // components with ComponentInfo#directBootAware == false will be 1226 // filtered. W should explicitly include both direct boot aware and 1227 // unaware component here. 1228 List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser( 1229 launchIntent, 1230 PackageManager.MATCH_DISABLED_COMPONENTS 1231 | PackageManager.MATCH_DIRECT_BOOT_AWARE 1232 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 1233 userId 1234 ); 1235 synchronized (mEntriesMap) { 1236 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock"); 1237 HashMap<String, ApplicationsState.AppEntry> userEntries = 1238 mEntriesMap.valueAt(i); 1239 final int N = intents.size(); 1240 for (int j = 0; j < N; j++) { 1241 ResolveInfo resolveInfo = intents.get(j); 1242 String packageName = resolveInfo.activityInfo.packageName; 1243 ApplicationsState.AppEntry 1244 entry = userEntries.get(packageName); 1245 if (entry != null) { 1246 entry.hasLauncherEntry = true; 1247 entry.launcherEntryEnabled |= 1248 resolveInfo.activityInfo.enabled; 1249 } else { 1250 Log.w(TAG, "Cannot find pkg: " + packageName 1251 + " on user " + userId); 1252 } 1253 } 1254 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock"); 1255 } 1256 } 1257 1258 if (!mMainHandler.hasMessages( 1259 ApplicationsState.MainHandler.MSG_LAUNCHER_INFO_CHANGED)) { 1260 mMainHandler.sendEmptyMessage( 1261 ApplicationsState.MainHandler.MSG_LAUNCHER_INFO_CHANGED); 1262 } 1263 } 1264 if (msg.what == MSG_LOAD_LAUNCHER) { 1265 sendEmptyMessage(MSG_LOAD_LEANBACK_LAUNCHER); 1266 } else { 1267 sendEmptyMessage(MSG_LOAD_ICONS); 1268 } 1269 } 1270 break; 1271 case MSG_LOAD_ICONS: { 1272 if (hasFlag(flags, FLAG_SESSION_REQUEST_ICONS)) { 1273 int numDone = 0; 1274 synchronized (mEntriesMap) { 1275 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock"); 1276 for (int i = 0; i < mAppEntries.size() && numDone < 2; i++) { 1277 ApplicationsState.AppEntry 1278 entry = mAppEntries.get(i); 1279 if (entry.icon == null || !entry.mounted) { 1280 synchronized (entry) { 1281 if (entry.ensureIconLocked(mContext)) { 1282 if (!mRunning) { 1283 mRunning = true; 1284 Message m = mMainHandler.obtainMessage( 1285 ApplicationsState.MainHandler 1286 .MSG_RUNNING_STATE_CHANGED, 1287 1); 1288 mMainHandler.sendMessage(m); 1289 } 1290 numDone++; 1291 } 1292 } 1293 } 1294 } 1295 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock"); 1296 } 1297 if (numDone > 0) { 1298 if (!mMainHandler.hasMessages( 1299 ApplicationsState.MainHandler.MSG_PACKAGE_ICON_CHANGED)) { 1300 mMainHandler.sendEmptyMessage( 1301 ApplicationsState.MainHandler.MSG_PACKAGE_ICON_CHANGED); 1302 } 1303 } 1304 if (numDone >= 2) { 1305 sendEmptyMessage(MSG_LOAD_ICONS); 1306 break; 1307 } 1308 } 1309 sendEmptyMessage(MSG_LOAD_SIZES); 1310 } 1311 break; 1312 case MSG_LOAD_SIZES: { 1313 if (hasFlag(flags, FLAG_SESSION_REQUEST_SIZES)) { 1314 synchronized (mEntriesMap) { 1315 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock"); 1316 if (mCurComputingSizePkg != null) { 1317 if (DEBUG_LOCKING) { 1318 Log.v(TAG, 1319 "MSG_LOAD_SIZES releasing: currently computing"); 1320 } 1321 return; 1322 } 1323 1324 long now = SystemClock.uptimeMillis(); 1325 for (int i = 0; i < mAppEntries.size(); i++) { 1326 ApplicationsState.AppEntry 1327 entry = mAppEntries.get(i); 1328 if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED) 1329 && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) { 1330 if (entry.sizeLoadStart == 0 || 1331 (entry.sizeLoadStart < (now - 20 * 1000))) { 1332 if (!mRunning) { 1333 mRunning = true; 1334 Message m = mMainHandler.obtainMessage( 1335 ApplicationsState.MainHandler 1336 .MSG_RUNNING_STATE_CHANGED, 1337 1); 1338 mMainHandler.sendMessage(m); 1339 } 1340 entry.sizeLoadStart = now; 1341 mCurComputingSizeUuid = entry.info.storageUuid; 1342 mCurComputingSizePkg = entry.info.packageName; 1343 mCurComputingSizeUserId = UserHandle.getUserId( 1344 entry.info.uid); 1345 1346 mBackgroundHandler.post(() -> { 1347 try { 1348 final StorageStats stats = 1349 mStats.queryStatsForPackage( 1350 mCurComputingSizeUuid, 1351 mCurComputingSizePkg, 1352 UserHandle.of( 1353 mCurComputingSizeUserId)); 1354 final PackageStats legacy = new PackageStats( 1355 mCurComputingSizePkg, 1356 mCurComputingSizeUserId); 1357 legacy.codeSize = stats.getAppBytes(); 1358 legacy.dataSize = stats.getDataBytes(); 1359 legacy.cacheSize = stats.getCacheBytes(); 1360 try { 1361 mStatsObserver.onGetStatsCompleted(legacy, 1362 true); 1363 } catch (RemoteException ignored) { 1364 } 1365 } catch (NameNotFoundException | IOException e) { 1366 Log.w(TAG, "Failed to query stats: " + e); 1367 try { 1368 mStatsObserver.onGetStatsCompleted(null, false); 1369 } catch (RemoteException ignored) { 1370 } 1371 } 1372 1373 }); 1374 } 1375 if (DEBUG_LOCKING) { 1376 Log.v(TAG, 1377 "MSG_LOAD_SIZES releasing: now computing"); 1378 } 1379 return; 1380 } 1381 } 1382 if (!mMainHandler.hasMessages( 1383 ApplicationsState.MainHandler.MSG_ALL_SIZES_COMPUTED)) { 1384 mMainHandler.sendEmptyMessage( 1385 ApplicationsState.MainHandler.MSG_ALL_SIZES_COMPUTED); 1386 mRunning = false; 1387 Message m = mMainHandler.obtainMessage( 1388 ApplicationsState.MainHandler.MSG_RUNNING_STATE_CHANGED, 0); 1389 mMainHandler.sendMessage(m); 1390 } 1391 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock"); 1392 } 1393 } 1394 } 1395 break; 1396 } 1397 } 1398 1399 @ApplicationsState.SessionFlags getCombinedSessionFlags(List<ApplicationsState.Session> sessions)1400 private int getCombinedSessionFlags(List<ApplicationsState.Session> sessions) { 1401 synchronized (mEntriesMap) { 1402 int flags = 0; 1403 for (ApplicationsState.Session session : sessions) { 1404 flags |= session.mFlags; 1405 } 1406 return flags; 1407 } 1408 } 1409 1410 final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() { 1411 public void onGetStatsCompleted(PackageStats stats, boolean succeeded) { 1412 if (!succeeded) { 1413 // There is no meaningful information in stats if the call failed. 1414 return; 1415 } 1416 1417 boolean sizeChanged = false; 1418 synchronized (mEntriesMap) { 1419 if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock"); 1420 HashMap<String, ApplicationsState.AppEntry> userMap = mEntriesMap.get( 1421 stats.userHandle); 1422 if (userMap == null) { 1423 // The user must have been removed. 1424 return; 1425 } 1426 ApplicationsState.AppEntry entry = userMap.get(stats.packageName); 1427 if (entry != null) { 1428 synchronized (entry) { 1429 entry.sizeStale = false; 1430 entry.sizeLoadStart = 0; 1431 long externalCodeSize = stats.externalCodeSize 1432 + stats.externalObbSize; 1433 long externalDataSize = stats.externalDataSize 1434 + stats.externalMediaSize; 1435 long newSize = externalCodeSize + externalDataSize 1436 + getTotalInternalSize(stats); 1437 if (entry.size != newSize || 1438 entry.cacheSize != stats.cacheSize || 1439 entry.codeSize != stats.codeSize || 1440 entry.dataSize != stats.dataSize || 1441 entry.externalCodeSize != externalCodeSize || 1442 entry.externalDataSize != externalDataSize || 1443 entry.externalCacheSize != stats.externalCacheSize) { 1444 entry.size = newSize; 1445 entry.cacheSize = stats.cacheSize; 1446 entry.codeSize = stats.codeSize; 1447 entry.dataSize = stats.dataSize; 1448 entry.externalCodeSize = externalCodeSize; 1449 entry.externalDataSize = externalDataSize; 1450 entry.externalCacheSize = stats.externalCacheSize; 1451 entry.sizeStr = getSizeStr(entry.size); 1452 entry.internalSize = getTotalInternalSize(stats); 1453 entry.internalSizeStr = getSizeStr(entry.internalSize); 1454 entry.externalSize = getTotalExternalSize(stats); 1455 entry.externalSizeStr = getSizeStr(entry.externalSize); 1456 if (DEBUG) { 1457 Log.i(TAG, "Set size of " + entry.label + " " + entry 1458 + ": " + entry.sizeStr); 1459 } 1460 sizeChanged = true; 1461 } 1462 } 1463 if (sizeChanged) { 1464 Message msg = mMainHandler.obtainMessage( 1465 ApplicationsState.MainHandler.MSG_PACKAGE_SIZE_CHANGED, 1466 stats.packageName); 1467 mMainHandler.sendMessage(msg); 1468 } 1469 } 1470 if (mCurComputingSizePkg != null 1471 && (mCurComputingSizePkg.equals(stats.packageName) 1472 && mCurComputingSizeUserId == stats.userHandle)) { 1473 mCurComputingSizePkg = null; 1474 sendEmptyMessage(MSG_LOAD_SIZES); 1475 } 1476 if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock"); 1477 } 1478 } 1479 }; 1480 } 1481 1482 /** 1483 * Receives notifications when applications are added/removed. 1484 */ 1485 private class PackageIntentReceiver extends BroadcastReceiver { registerReceiver()1486 void registerReceiver() { 1487 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); 1488 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 1489 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 1490 filter.addDataScheme("package"); 1491 mContext.registerReceiver(this, filter); 1492 // Register for events related to sdcard installation. 1493 IntentFilter sdFilter = new IntentFilter(); 1494 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 1495 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 1496 mContext.registerReceiver(this, sdFilter); 1497 // Register for events related to user creation/deletion. 1498 IntentFilter userFilter = new IntentFilter(); 1499 userFilter.addAction(Intent.ACTION_USER_ADDED); 1500 userFilter.addAction(Intent.ACTION_USER_REMOVED); 1501 mContext.registerReceiver(this, userFilter); 1502 } 1503 unregisterReceiver()1504 void unregisterReceiver() { 1505 mContext.unregisterReceiver(this); 1506 } 1507 1508 @Override onReceive(Context context, Intent intent)1509 public void onReceive(Context context, Intent intent) { 1510 String actionStr = intent.getAction(); 1511 if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) { 1512 Uri data = intent.getData(); 1513 String pkgName = data.getEncodedSchemeSpecificPart(); 1514 for (int i = 0; i < mEntriesMap.size(); i++) { 1515 addPackage(pkgName, mEntriesMap.keyAt(i)); 1516 } 1517 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) { 1518 Uri data = intent.getData(); 1519 String pkgName = data.getEncodedSchemeSpecificPart(); 1520 for (int i = 0; i < mEntriesMap.size(); i++) { 1521 removePackage(pkgName, mEntriesMap.keyAt(i)); 1522 } 1523 } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) { 1524 Uri data = intent.getData(); 1525 String pkgName = data.getEncodedSchemeSpecificPart(); 1526 for (int i = 0; i < mEntriesMap.size(); i++) { 1527 invalidatePackage(pkgName, mEntriesMap.keyAt(i)); 1528 } 1529 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) || 1530 Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) { 1531 // When applications become available or unavailable (perhaps because 1532 // the SD card was inserted or ejected) we need to refresh the 1533 // AppInfo with new label, icon and size information as appropriate 1534 // given the newfound (un)availability of the application. 1535 // A simple way to do that is to treat the refresh as a package 1536 // removal followed by a package addition. 1537 String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 1538 if (pkgList == null || pkgList.length == 0) { 1539 // Ignore 1540 return; 1541 } 1542 boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr); 1543 if (avail) { 1544 for (String pkgName : pkgList) { 1545 for (int i = 0; i < mEntriesMap.size(); i++) { 1546 invalidatePackage(pkgName, mEntriesMap.keyAt(i)); 1547 } 1548 } 1549 } 1550 } else if (Intent.ACTION_USER_ADDED.equals(actionStr)) { 1551 addUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)); 1552 } else if (Intent.ACTION_USER_REMOVED.equals(actionStr)) { 1553 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)); 1554 } 1555 } 1556 } 1557 1558 /** 1559 * Whether the packages for the user have been initialized. 1560 */ isUserAdded(int userId)1561 public boolean isUserAdded(int userId) { 1562 return mEntriesMap.contains(userId); 1563 } 1564 1565 public interface Callbacks { onRunningStateChanged(boolean running)1566 void onRunningStateChanged(boolean running); 1567 onPackageListChanged()1568 void onPackageListChanged(); 1569 onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps)1570 void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps); 1571 onPackageIconChanged()1572 void onPackageIconChanged(); 1573 onPackageSizeChanged(String packageName)1574 void onPackageSizeChanged(String packageName); 1575 onAllSizesComputed()1576 void onAllSizesComputed(); 1577 onLauncherInfoChanged()1578 void onLauncherInfoChanged(); 1579 onLoadEntriesCompleted()1580 void onLoadEntriesCompleted(); 1581 } 1582 1583 public static class SizeInfo { 1584 public long cacheSize; 1585 public long codeSize; 1586 public long dataSize; 1587 public long externalCodeSize; 1588 public long externalDataSize; 1589 1590 // This is the part of externalDataSize that is in the cache 1591 // section of external storage. Note that we don't just combine 1592 // this with cacheSize because currently the platform can't 1593 // automatically trim this data when needed, so it is something 1594 // the user may need to manage. The externalDataSize also includes 1595 // this value, since what this is here is really the part of 1596 // externalDataSize that we can just consider to be "cache" files 1597 // for purposes of cleaning them up in the app details UI. 1598 public long externalCacheSize; 1599 } 1600 1601 public static class AppEntry extends 1602 ApplicationsState.SizeInfo { 1603 public final File apkFile; 1604 public final long id; 1605 public String label; 1606 public long size; 1607 public long internalSize; 1608 public long externalSize; 1609 public String labelDescription; 1610 1611 public boolean mounted; 1612 1613 /** 1614 * Setting this to {@code true} prevents the entry to be filtered by 1615 * {@link #FILTER_DOWNLOADED_AND_LAUNCHER}. 1616 */ 1617 public boolean hasLauncherEntry; 1618 1619 /** 1620 * Whether the component that has a launcher intent filter is enabled. 1621 */ 1622 public boolean launcherEntryEnabled; 1623 1624 /** 1625 * Whether or not it's a Home app. 1626 */ 1627 public boolean isHomeApp; 1628 getNormalizedLabel()1629 public String getNormalizedLabel() { 1630 if (normalizedLabel != null) { 1631 return normalizedLabel; 1632 } 1633 normalizedLabel = normalize(label); 1634 return normalizedLabel; 1635 } 1636 1637 // Need to synchronize on 'this' for the following. 1638 public ApplicationInfo info; 1639 public Drawable icon; 1640 public String sizeStr; 1641 public String internalSizeStr; 1642 public String externalSizeStr; 1643 public boolean sizeStale; 1644 public long sizeLoadStart; 1645 1646 public String normalizedLabel; 1647 1648 // A location where extra info can be placed to be used by custom filters. 1649 public Object extraInfo; 1650 AppEntry(Context context, ApplicationInfo info, long id)1651 public AppEntry(Context context, ApplicationInfo info, long id) { 1652 apkFile = new File(info.sourceDir); 1653 this.id = id; 1654 this.info = info; 1655 this.size = SIZE_UNKNOWN; 1656 this.sizeStale = true; 1657 ensureLabel(context); 1658 // Speed up the cache of the icon and label description if they haven't been created. 1659 ThreadUtils.postOnBackgroundThread(() -> { 1660 if (this.icon == null) { 1661 this.ensureIconLocked(context); 1662 } 1663 if (this.labelDescription == null) { 1664 this.ensureLabelDescriptionLocked(context); 1665 } 1666 }); 1667 } 1668 ensureLabel(Context context)1669 public void ensureLabel(Context context) { 1670 if (this.label == null || !this.mounted) { 1671 if (!this.apkFile.exists()) { 1672 this.mounted = false; 1673 this.label = info.packageName; 1674 } else { 1675 this.mounted = true; 1676 CharSequence label = info.loadLabel(context.getPackageManager()); 1677 this.label = label != null ? label.toString() : info.packageName; 1678 } 1679 } 1680 } 1681 ensureIconLocked(Context context)1682 boolean ensureIconLocked(Context context) { 1683 if (this.icon == null) { 1684 if (this.apkFile.exists()) { 1685 this.icon = LibUtils.getBadgedIcon(context, info); 1686 return true; 1687 } else { 1688 this.mounted = false; 1689 this.icon = context.getDrawable(context.getResources().getIdentifier( 1690 "sym_app_on_sd_unavailable_icon", "drawable", "android")); 1691 } 1692 } else if (!this.mounted) { 1693 // If the app wasn't mounted but is now mounted, reload 1694 // its icon. 1695 if (this.apkFile.exists()) { 1696 this.mounted = true; 1697 this.icon = LibUtils.getBadgedIcon(context, info); 1698 return true; 1699 } 1700 } 1701 return false; 1702 } 1703 getVersion(Context context)1704 public String getVersion(Context context) { 1705 try { 1706 return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName; 1707 } catch (PackageManager.NameNotFoundException e) { 1708 return ""; 1709 } 1710 } 1711 1712 /** 1713 * Get the label description which distinguishes a personal app from a work app for 1714 * accessibility purpose. If the app is in a work profile, then add a "work" prefix to the 1715 * app label. 1716 * 1717 * @param context The application context 1718 */ ensureLabelDescriptionLocked(Context context)1719 public void ensureLabelDescriptionLocked(Context context) { 1720 final int userId = UserHandle.getUserId(this.info.uid); 1721 if (UserManager.get(context).isManagedProfile(userId)) { 1722 this.labelDescription = ""; 1723 } else { 1724 this.labelDescription = this.label; 1725 } 1726 } 1727 } 1728 hasFlag(int flags, int flag)1729 private static boolean hasFlag(int flags, int flag) { 1730 return (flags & flag) != 0; 1731 } 1732 1733 /** 1734 * Compare by label, then package name, then uid. 1735 */ 1736 public static final Comparator<ApplicationsState.AppEntry> ALPHA_COMPARATOR = 1737 new Comparator<ApplicationsState.AppEntry>() { 1738 private final Collator sCollator = Collator.getInstance(); 1739 1740 @Override 1741 public int compare(ApplicationsState.AppEntry object1, 1742 ApplicationsState.AppEntry object2) { 1743 int compareResult = sCollator.compare(object1.label, object2.label); 1744 if (compareResult != 0) { 1745 return compareResult; 1746 } 1747 if (object1.info != null && object2.info != null) { 1748 compareResult = 1749 sCollator.compare(object1.info.packageName, 1750 object2.info.packageName); 1751 if (compareResult != 0) { 1752 return compareResult; 1753 } 1754 } 1755 1756 return object1.info.uid - object2.info.uid; 1757 } 1758 }; 1759 1760 public static final Comparator<ApplicationsState.AppEntry> SIZE_COMPARATOR 1761 = new Comparator<ApplicationsState.AppEntry>() { 1762 @Override 1763 public int compare(ApplicationsState.AppEntry object1, ApplicationsState.AppEntry object2) { 1764 if (object1.size < object2.size) return 1; 1765 if (object1.size > object2.size) return -1; 1766 return ALPHA_COMPARATOR.compare(object1, object2); 1767 } 1768 }; 1769 1770 public static final Comparator<ApplicationsState.AppEntry> INTERNAL_SIZE_COMPARATOR 1771 = new Comparator<ApplicationsState.AppEntry>() { 1772 @Override 1773 public int compare(ApplicationsState.AppEntry object1, ApplicationsState.AppEntry object2) { 1774 if (object1.internalSize < object2.internalSize) return 1; 1775 if (object1.internalSize > object2.internalSize) return -1; 1776 return ALPHA_COMPARATOR.compare(object1, object2); 1777 } 1778 }; 1779 1780 public static final Comparator<ApplicationsState.AppEntry> EXTERNAL_SIZE_COMPARATOR 1781 = new Comparator<ApplicationsState.AppEntry>() { 1782 @Override 1783 public int compare(ApplicationsState.AppEntry object1, ApplicationsState.AppEntry object2) { 1784 if (object1.externalSize < object2.externalSize) return 1; 1785 if (object1.externalSize > object2.externalSize) return -1; 1786 return ALPHA_COMPARATOR.compare(object1, object2); 1787 } 1788 }; 1789 1790 public interface AppFilter { init()1791 void init(); 1792 init(Context context)1793 default void init(Context context) { 1794 init(); 1795 } 1796 filterApp(ApplicationsState.AppEntry info)1797 boolean filterApp(ApplicationsState.AppEntry info); 1798 } 1799 1800 public static final ApplicationsState.AppFilter 1801 FILTER_PERSONAL = new ApplicationsState.AppFilter() { 1802 private int mCurrentUser; 1803 1804 @Override 1805 public void init() { 1806 mCurrentUser = ActivityManager.getCurrentUser(); 1807 } 1808 1809 @Override 1810 public boolean filterApp( 1811 ApplicationsState.AppEntry entry) { 1812 return UserHandle.getUserId(entry.info.uid) == mCurrentUser; 1813 } 1814 }; 1815 1816 public static final ApplicationsState.AppFilter 1817 FILTER_WITHOUT_DISABLED_UNTIL_USED = new ApplicationsState.AppFilter() { 1818 @Override 1819 public void init() { 1820 // do nothing 1821 } 1822 1823 @Override 1824 public boolean filterApp( 1825 ApplicationsState.AppEntry entry) { 1826 return entry.info.enabledSetting 1827 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; 1828 } 1829 }; 1830 1831 1832 /** 1833 * Displays a combined list with "downloaded" and "visible in launcher" apps only. 1834 */ 1835 public static final ApplicationsState.AppFilter 1836 FILTER_DOWNLOADED_AND_LAUNCHER = new ApplicationsState.AppFilter() { 1837 @Override 1838 public void init() { 1839 } 1840 1841 @Override 1842 public boolean filterApp( 1843 ApplicationsState.AppEntry entry) { 1844 if (AppUtils.isInstant(entry.info)) { 1845 return false; 1846 } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) { 1847 return true; 1848 } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) { 1849 return true; 1850 } else if (entry.hasLauncherEntry) { 1851 return true; 1852 } else { 1853 return hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM) && entry.isHomeApp; 1854 } 1855 } 1856 }; 1857 1858 /** 1859 * Displays a combined list with "downloaded" and "visible in launcher" apps only. 1860 */ 1861 public static final ApplicationsState.AppFilter 1862 FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT = new ApplicationsState.AppFilter() { 1863 1864 @Override 1865 public void init() { 1866 } 1867 1868 @Override 1869 public boolean filterApp( 1870 ApplicationsState.AppEntry entry) { 1871 return AppUtils.isInstant(entry.info) 1872 || FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry); 1873 } 1874 1875 }; 1876 1877 public static final ApplicationsState.AppFilter 1878 FILTER_THIRD_PARTY = new ApplicationsState.AppFilter() { 1879 @Override 1880 public void init() { 1881 } 1882 1883 @Override 1884 public boolean filterApp( 1885 ApplicationsState.AppEntry entry) { 1886 if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) { 1887 return true; 1888 } else { 1889 return !hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM); 1890 } 1891 } 1892 }; 1893 1894 public static final ApplicationsState.AppFilter 1895 FILTER_DISABLED = new ApplicationsState.AppFilter() { 1896 @Override 1897 public void init() { 1898 } 1899 1900 @Override 1901 public boolean filterApp( 1902 ApplicationsState.AppEntry entry) { 1903 return !entry.info.enabled && !AppUtils.isInstant(entry.info); 1904 } 1905 }; 1906 1907 public static final ApplicationsState.AppFilter 1908 FILTER_INSTANT = new ApplicationsState.AppFilter() { 1909 @Override 1910 public void init() { 1911 } 1912 1913 @Override 1914 public boolean filterApp( 1915 ApplicationsState.AppEntry entry) { 1916 return AppUtils.isInstant(entry.info); 1917 } 1918 }; 1919 1920 public static final ApplicationsState.AppFilter 1921 FILTER_ALL_ENABLED = new ApplicationsState.AppFilter() { 1922 @Override 1923 public void init() { 1924 } 1925 1926 @Override 1927 public boolean filterApp( 1928 ApplicationsState.AppEntry entry) { 1929 return entry.info.enabled && !AppUtils.isInstant(entry.info); 1930 } 1931 }; 1932 1933 public static final ApplicationsState.AppFilter 1934 FILTER_EVERYTHING = new ApplicationsState.AppFilter() { 1935 @Override 1936 public void init() { 1937 } 1938 1939 @Override 1940 public boolean filterApp( 1941 ApplicationsState.AppEntry entry) { 1942 return true; 1943 } 1944 }; 1945 1946 1947 public static final ApplicationsState.AppFilter 1948 FILTER_NOT_HIDE = new ApplicationsState.AppFilter() { 1949 private String[] mHidePackageNames; 1950 1951 @Override 1952 public void init(Context context) { 1953 mHidePackageNames = context.getResources() 1954 .getStringArray(context.getResources().getIdentifier( 1955 "config_hideWhenDisabled_packageNames", "array", "android")); 1956 } 1957 1958 @Override 1959 public void init() { 1960 } 1961 1962 @Override 1963 public boolean filterApp( 1964 ApplicationsState.AppEntry entry) { 1965 if (ArrayUtils.contains(mHidePackageNames, entry.info.packageName)) { 1966 if (!entry.info.enabled) { 1967 return false; 1968 } else { 1969 return entry.info.enabledSetting 1970 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; 1971 } 1972 } 1973 1974 return true; 1975 } 1976 }; 1977 1978 public static final ApplicationsState.AppFilter 1979 FILTER_GAMES = new ApplicationsState.AppFilter() { 1980 @Override 1981 public void init() { 1982 } 1983 1984 @Override 1985 public boolean filterApp(ApplicationsState.AppEntry info) { 1986 // TODO: Update for the new game category. 1987 boolean isGame; 1988 synchronized (info.info) { 1989 isGame = hasFlag(info.info.flags, ApplicationInfo.FLAG_IS_GAME) 1990 || info.info.category == ApplicationInfo.CATEGORY_GAME; 1991 } 1992 return isGame; 1993 } 1994 }; 1995 1996 public static class VolumeFilter implements 1997 ApplicationsState.AppFilter { 1998 private final String mVolumeUuid; 1999 VolumeFilter(String volumeUuid)2000 public VolumeFilter(String volumeUuid) { 2001 mVolumeUuid = volumeUuid; 2002 } 2003 2004 @Override init()2005 public void init() { 2006 } 2007 2008 @Override filterApp( ApplicationsState.AppEntry info)2009 public boolean filterApp( 2010 ApplicationsState.AppEntry info) { 2011 return Objects.equals(info.info.volumeUuid, mVolumeUuid); 2012 } 2013 } 2014 2015 public static class CompoundFilter implements 2016 ApplicationsState.AppFilter { 2017 private final ApplicationsState.AppFilter mFirstFilter; 2018 private final ApplicationsState.AppFilter mSecondFilter; 2019 CompoundFilter(ApplicationsState.AppFilter first, ApplicationsState.AppFilter second)2020 public CompoundFilter(ApplicationsState.AppFilter first, 2021 ApplicationsState.AppFilter second) { 2022 mFirstFilter = first; 2023 mSecondFilter = second; 2024 } 2025 2026 @Override init(Context context)2027 public void init(Context context) { 2028 mFirstFilter.init(context); 2029 mSecondFilter.init(context); 2030 } 2031 2032 @Override init()2033 public void init() { 2034 mFirstFilter.init(); 2035 mSecondFilter.init(); 2036 } 2037 2038 @Override filterApp( ApplicationsState.AppEntry info)2039 public boolean filterApp( 2040 ApplicationsState.AppEntry info) { 2041 return mFirstFilter.filterApp(info) && mSecondFilter.filterApp(info); 2042 } 2043 } 2044 2045 public static final ApplicationsState.AppFilter 2046 FILTER_AUDIO = new ApplicationsState.AppFilter() { 2047 @Override 2048 public void init() { 2049 } 2050 2051 @Override 2052 public boolean filterApp( 2053 ApplicationsState.AppEntry entry) { 2054 boolean isMusicApp; 2055 synchronized (entry) { 2056 isMusicApp = entry.info.category == ApplicationInfo.CATEGORY_AUDIO; 2057 } 2058 return isMusicApp; 2059 } 2060 }; 2061 2062 public static final ApplicationsState.AppFilter 2063 FILTER_MOVIES = new ApplicationsState.AppFilter() { 2064 @Override 2065 public void init() { 2066 } 2067 2068 @Override 2069 public boolean filterApp( 2070 ApplicationsState.AppEntry entry) { 2071 boolean isMovieApp; 2072 synchronized (entry) { 2073 isMovieApp = entry.info.category == ApplicationInfo.CATEGORY_VIDEO; 2074 } 2075 return isMovieApp; 2076 } 2077 }; 2078 2079 public static final ApplicationsState.AppFilter 2080 FILTER_PHOTOS = 2081 new ApplicationsState.AppFilter() { 2082 @Override 2083 public void init() { 2084 } 2085 2086 @Override 2087 public boolean filterApp( 2088 ApplicationsState.AppEntry entry) { 2089 boolean isPhotosApp; 2090 synchronized (entry) { 2091 isPhotosApp = entry.info.category == ApplicationInfo.CATEGORY_IMAGE; 2092 } 2093 return isPhotosApp; 2094 } 2095 }; 2096 2097 public static final ApplicationsState.AppFilter 2098 FILTER_OTHER_APPS = 2099 new ApplicationsState.AppFilter() { 2100 @Override 2101 public void init() { 2102 } 2103 2104 @Override 2105 public boolean filterApp( 2106 ApplicationsState.AppEntry entry) { 2107 boolean isCategorized; 2108 synchronized (entry) { 2109 isCategorized = 2110 FILTER_AUDIO.filterApp(entry) 2111 || FILTER_GAMES.filterApp(entry) 2112 || FILTER_MOVIES.filterApp(entry) 2113 || FILTER_PHOTOS.filterApp(entry); 2114 } 2115 return !isCategorized; 2116 } 2117 }; 2118 } 2119