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