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