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