ApplicationsState.java revision 8089d48968cb2c5a9023842fb6d955a15580b69f
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 17package com.android.settingslib.applications; 18 19import android.app.ActivityManager; 20import android.app.AppGlobals; 21import android.app.Application; 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.content.pm.ApplicationInfo; 27import android.content.pm.IPackageManager; 28import android.content.pm.IPackageStatsObserver; 29import android.content.pm.PackageManager; 30import android.content.pm.PackageStats; 31import android.content.pm.ParceledListSlice; 32import android.content.pm.ResolveInfo; 33import android.content.pm.UserInfo; 34import android.graphics.drawable.Drawable; 35import android.net.Uri; 36import android.os.Handler; 37import android.os.HandlerThread; 38import android.os.Looper; 39import android.os.Message; 40import android.os.Process; 41import android.os.RemoteException; 42import android.os.SystemClock; 43import android.os.UserHandle; 44import android.os.UserManager; 45import android.text.format.Formatter; 46import android.util.Log; 47import android.util.SparseArray; 48 49import com.android.internal.util.ArrayUtils; 50 51import java.io.File; 52import java.text.Collator; 53import java.text.Normalizer; 54import java.text.Normalizer.Form; 55import java.util.ArrayList; 56import java.util.Collections; 57import java.util.Comparator; 58import java.util.HashMap; 59import java.util.List; 60import java.util.Objects; 61import java.util.regex.Pattern; 62 63/** 64 * Keeps track of information about all installed applications, lazy-loading 65 * as needed. 66 */ 67public class ApplicationsState { 68 static final String TAG = "ApplicationsState"; 69 static final boolean DEBUG = false; 70 static final boolean DEBUG_LOCKING = false; 71 72 public static final int SIZE_UNKNOWN = -1; 73 public static final int SIZE_INVALID = -2; 74 75 static final Pattern REMOVE_DIACRITICALS_PATTERN 76 = Pattern.compile("\\p{InCombiningDiacriticalMarks}+"); 77 78 static final Object sLock = new Object(); 79 static ApplicationsState sInstance; 80 81 public static ApplicationsState getInstance(Application app) { 82 synchronized (sLock) { 83 if (sInstance == null) { 84 sInstance = new ApplicationsState(app); 85 } 86 return sInstance; 87 } 88 } 89 90 final Context mContext; 91 final PackageManager mPm; 92 final IPackageManager mIpm; 93 final UserManager mUm; 94 final int mAdminRetrieveFlags; 95 final int mRetrieveFlags; 96 PackageIntentReceiver mPackageIntentReceiver; 97 98 boolean mResumed; 99 boolean mHaveDisabledApps; 100 101 // Information about all applications. Synchronize on mEntriesMap 102 // to protect access to these. 103 final ArrayList<Session> mSessions = new ArrayList<Session>(); 104 final ArrayList<Session> mRebuildingSessions = new ArrayList<Session>(); 105 final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges(); 106 // Map: userid => (Map: package name => AppEntry) 107 final SparseArray<HashMap<String, AppEntry>> mEntriesMap = 108 new SparseArray<HashMap<String, AppEntry>>(); 109 final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>(); 110 List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>(); 111 long mCurId = 1; 112 String mCurComputingSizePkg; 113 int mCurComputingSizeUserId; 114 boolean mSessionsChanged; 115 116 // Temporary for dispatching session callbacks. Only touched by main thread. 117 final ArrayList<Session> mActiveSessions = new ArrayList<Session>(); 118 119 final HandlerThread mThread; 120 final BackgroundHandler mBackgroundHandler; 121 final MainHandler mMainHandler = new MainHandler(Looper.getMainLooper()); 122 123 private ApplicationsState(Application app) { 124 mContext = app; 125 mPm = mContext.getPackageManager(); 126 mIpm = AppGlobals.getPackageManager(); 127 mUm = (UserManager) app.getSystemService(Context.USER_SERVICE); 128 for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) { 129 mEntriesMap.put(userId, new HashMap<String, AppEntry>()); 130 } 131 mThread = new HandlerThread("ApplicationsState.Loader", 132 Process.THREAD_PRIORITY_BACKGROUND); 133 mThread.start(); 134 mBackgroundHandler = new BackgroundHandler(mThread.getLooper()); 135 136 // Only the owner can see all apps. 137 mAdminRetrieveFlags = PackageManager.GET_UNINSTALLED_PACKAGES | 138 PackageManager.GET_DISABLED_COMPONENTS | 139 PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS; 140 mRetrieveFlags = PackageManager.GET_DISABLED_COMPONENTS | 141 PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS; 142 143 /** 144 * This is a trick to prevent the foreground thread from being delayed. 145 * The problem is that Dalvik monitors are initially spin locks, to keep 146 * them lightweight. This leads to unfair contention -- Even though the 147 * background thread only holds the lock for a short amount of time, if 148 * it keeps running and locking again it can prevent the main thread from 149 * acquiring its lock for a long time... sometimes even > 5 seconds 150 * (leading to an ANR). 151 * 152 * Dalvik will promote a monitor to a "real" lock if it detects enough 153 * contention on it. It doesn't figure this out fast enough for us 154 * here, though, so this little trick will force it to turn into a real 155 * lock immediately. 156 */ 157 synchronized (mEntriesMap) { 158 try { 159 mEntriesMap.wait(1); 160 } catch (InterruptedException e) { 161 } 162 } 163 } 164 165 public Looper getBackgroundLooper() { 166 return mThread.getLooper(); 167 } 168 169 public Session newSession(Callbacks callbacks) { 170 Session s = new Session(callbacks); 171 synchronized (mEntriesMap) { 172 mSessions.add(s); 173 } 174 return s; 175 } 176 177 void doResumeIfNeededLocked() { 178 if (mResumed) { 179 return; 180 } 181 mResumed = true; 182 if (mPackageIntentReceiver == null) { 183 mPackageIntentReceiver = new PackageIntentReceiver(); 184 mPackageIntentReceiver.registerReceiver(); 185 } 186 mApplications = new ArrayList<ApplicationInfo>(); 187 for (UserInfo user : mUm.getProfiles(UserHandle.myUserId())) { 188 try { 189 // If this user is new, it needs a map created. 190 if (mEntriesMap.indexOfKey(user.id) < 0) { 191 mEntriesMap.put(user.id, new HashMap<String, AppEntry>()); 192 } 193 @SuppressWarnings("unchecked") 194 ParceledListSlice<ApplicationInfo> list = 195 mIpm.getInstalledApplications( 196 user.isAdmin() ? mAdminRetrieveFlags : mRetrieveFlags, 197 user.id); 198 mApplications.addAll(list.getList()); 199 } catch (RemoteException e) { 200 } 201 } 202 203 if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) { 204 // If an interesting part of the configuration has changed, we 205 // should completely reload the app entries. 206 clearEntries(); 207 } else { 208 for (int i=0; i<mAppEntries.size(); i++) { 209 mAppEntries.get(i).sizeStale = true; 210 } 211 } 212 213 mHaveDisabledApps = false; 214 for (int i=0; i<mApplications.size(); i++) { 215 final ApplicationInfo info = mApplications.get(i); 216 // Need to trim out any applications that are disabled by 217 // something different than the user. 218 if (!info.enabled) { 219 if (info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { 220 mApplications.remove(i); 221 i--; 222 continue; 223 } 224 mHaveDisabledApps = true; 225 } 226 int userId = UserHandle.getUserId(info.uid); 227 final AppEntry entry = mEntriesMap.get(userId).get(info.packageName); 228 if (entry != null) { 229 entry.info = info; 230 } 231 } 232 if (mAppEntries.size() > mApplications.size()) { 233 // There are less apps now, some must have been uninstalled. 234 clearEntries(); 235 } 236 mCurComputingSizePkg = null; 237 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) { 238 mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES); 239 } 240 } 241 242 private void clearEntries() { 243 for (int i = 0; i < mEntriesMap.size(); i++) { 244 mEntriesMap.valueAt(i).clear(); 245 } 246 mAppEntries.clear(); 247 } 248 249 public boolean haveDisabledApps() { 250 return mHaveDisabledApps; 251 } 252 253 void doPauseIfNeededLocked() { 254 if (!mResumed) { 255 return; 256 } 257 for (int i=0; i<mSessions.size(); i++) { 258 if (mSessions.get(i).mResumed) { 259 return; 260 } 261 } 262 doPauseLocked(); 263 } 264 265 void doPauseLocked() { 266 mResumed = false; 267 if (mPackageIntentReceiver != null) { 268 mPackageIntentReceiver.unregisterReceiver(); 269 mPackageIntentReceiver = null; 270 } 271 } 272 273 public AppEntry getEntry(String packageName, int userId) { 274 if (DEBUG_LOCKING) Log.v(TAG, "getEntry about to acquire lock..."); 275 synchronized (mEntriesMap) { 276 AppEntry entry = mEntriesMap.get(userId).get(packageName); 277 if (entry == null) { 278 ApplicationInfo info = getAppInfoLocked(packageName, userId); 279 if (info == null) { 280 try { 281 info = mIpm.getApplicationInfo(packageName, 0, userId); 282 } catch (RemoteException e) { 283 Log.w(TAG, "getEntry couldn't reach PackageManager", e); 284 return null; 285 } 286 } 287 if (info != null) { 288 entry = getEntryLocked(info); 289 } 290 } 291 if (DEBUG_LOCKING) Log.v(TAG, "...getEntry releasing lock"); 292 return entry; 293 } 294 } 295 296 private ApplicationInfo getAppInfoLocked(String pkg, int userId) { 297 for (int i = 0; i < mApplications.size(); i++) { 298 ApplicationInfo info = mApplications.get(i); 299 if (pkg.equals(info.packageName) 300 && userId == UserHandle.getUserId(info.uid)) { 301 return info; 302 } 303 } 304 return null; 305 } 306 307 public void ensureIcon(AppEntry entry) { 308 if (entry.icon != null) { 309 return; 310 } 311 synchronized (entry) { 312 entry.ensureIconLocked(mContext, mPm); 313 } 314 } 315 316 public void requestSize(String packageName, int userId) { 317 if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock..."); 318 synchronized (mEntriesMap) { 319 AppEntry entry = mEntriesMap.get(userId).get(packageName); 320 if (entry != null) { 321 mPm.getPackageSizeInfoAsUser(packageName, userId, mBackgroundHandler.mStatsObserver); 322 } 323 if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock"); 324 } 325 } 326 327 long sumCacheSizes() { 328 long sum = 0; 329 if (DEBUG_LOCKING) Log.v(TAG, "sumCacheSizes about to acquire lock..."); 330 synchronized (mEntriesMap) { 331 if (DEBUG_LOCKING) Log.v(TAG, "-> sumCacheSizes now has lock"); 332 for (int i=mAppEntries.size()-1; i>=0; i--) { 333 sum += mAppEntries.get(i).cacheSize; 334 } 335 if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock"); 336 } 337 return sum; 338 } 339 340 int indexOfApplicationInfoLocked(String pkgName, int userId) { 341 for (int i=mApplications.size()-1; i>=0; i--) { 342 ApplicationInfo appInfo = mApplications.get(i); 343 if (appInfo.packageName.equals(pkgName) 344 && UserHandle.getUserId(appInfo.uid) == userId) { 345 return i; 346 } 347 } 348 return -1; 349 } 350 351 void addPackage(String pkgName, int userId) { 352 try { 353 synchronized (mEntriesMap) { 354 if (DEBUG_LOCKING) Log.v(TAG, "addPackage acquired lock"); 355 if (DEBUG) Log.i(TAG, "Adding package " + pkgName); 356 if (!mResumed) { 357 // If we are not resumed, we will do a full query the 358 // next time we resume, so there is no reason to do work 359 // here. 360 if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: not resumed"); 361 return; 362 } 363 if (indexOfApplicationInfoLocked(pkgName, userId) >= 0) { 364 if (DEBUG) Log.i(TAG, "Package already exists!"); 365 if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: already exists"); 366 return; 367 } 368 ApplicationInfo info = mIpm.getApplicationInfo(pkgName, 369 mUm.isUserAdmin(userId) ? mAdminRetrieveFlags : mRetrieveFlags, 370 userId); 371 if (info == null) { 372 return; 373 } 374 if (!info.enabled) { 375 if (info.enabledSetting 376 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { 377 return; 378 } 379 mHaveDisabledApps = true; 380 } 381 mApplications.add(info); 382 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) { 383 mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES); 384 } 385 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) { 386 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED); 387 } 388 if (DEBUG_LOCKING) Log.v(TAG, "addPackage releasing lock"); 389 } 390 } catch (RemoteException e) { 391 } 392 } 393 394 public void removePackage(String pkgName, int userId) { 395 synchronized (mEntriesMap) { 396 if (DEBUG_LOCKING) Log.v(TAG, "removePackage acquired lock"); 397 int idx = indexOfApplicationInfoLocked(pkgName, userId); 398 if (DEBUG) Log.i(TAG, "removePackage: " + pkgName + " @ " + idx); 399 if (idx >= 0) { 400 AppEntry entry = mEntriesMap.get(userId).get(pkgName); 401 if (DEBUG) Log.i(TAG, "removePackage: " + entry); 402 if (entry != null) { 403 mEntriesMap.get(userId).remove(pkgName); 404 mAppEntries.remove(entry); 405 } 406 ApplicationInfo info = mApplications.get(idx); 407 mApplications.remove(idx); 408 if (!info.enabled) { 409 mHaveDisabledApps = false; 410 for (int i=0; i<mApplications.size(); i++) { 411 if (!mApplications.get(i).enabled) { 412 mHaveDisabledApps = true; 413 break; 414 } 415 } 416 } 417 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) { 418 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED); 419 } 420 } 421 if (DEBUG_LOCKING) Log.v(TAG, "removePackage releasing lock"); 422 } 423 } 424 425 public void invalidatePackage(String pkgName, int userId) { 426 removePackage(pkgName, userId); 427 addPackage(pkgName, userId); 428 } 429 430 private void addUser(int userId) { 431 final int profileIds[] = mUm.getProfileIdsWithDisabled(UserHandle.myUserId()); 432 if (ArrayUtils.contains(profileIds, userId)) { 433 synchronized (mEntriesMap) { 434 mEntriesMap.put(userId, new HashMap<String, AppEntry>()); 435 if (mResumed) { 436 // If resumed, Manually pause, then cause a resume to repopulate the app list. 437 // This is the simplest way to reload the packages so that the new user 438 // is included. Otherwise the list will be repopulated on next resume. 439 doPauseLocked(); 440 doResumeIfNeededLocked(); 441 } 442 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) { 443 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED); 444 } 445 } 446 } 447 } 448 449 private void removeUser(int userId) { 450 synchronized (mEntriesMap) { 451 HashMap<String, AppEntry> userMap = mEntriesMap.get(userId); 452 if (userMap != null) { 453 for (AppEntry appEntry : userMap.values()) { 454 mAppEntries.remove(appEntry); 455 mApplications.remove(appEntry.info); 456 } 457 mEntriesMap.remove(userId); 458 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) { 459 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED); 460 } 461 } 462 } 463 } 464 465 private AppEntry getEntryLocked(ApplicationInfo info) { 466 int userId = UserHandle.getUserId(info.uid); 467 AppEntry entry = mEntriesMap.get(userId).get(info.packageName); 468 if (DEBUG) Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry); 469 if (entry == null) { 470 if (DEBUG) Log.i(TAG, "Creating AppEntry for " + info.packageName); 471 entry = new AppEntry(mContext, info, mCurId++); 472 mEntriesMap.get(userId).put(info.packageName, entry); 473 mAppEntries.add(entry); 474 } else if (entry.info != info) { 475 entry.info = info; 476 } 477 return entry; 478 } 479 480 // -------------------------------------------------------------- 481 482 private long getTotalInternalSize(PackageStats ps) { 483 if (ps != null) { 484 return ps.codeSize + ps.dataSize; 485 } 486 return SIZE_INVALID; 487 } 488 489 private long getTotalExternalSize(PackageStats ps) { 490 if (ps != null) { 491 // We also include the cache size here because for non-emulated 492 // we don't automtically clean cache files. 493 return ps.externalCodeSize + ps.externalDataSize 494 + ps.externalCacheSize 495 + ps.externalMediaSize + ps.externalObbSize; 496 } 497 return SIZE_INVALID; 498 } 499 500 private String getSizeStr(long size) { 501 if (size >= 0) { 502 return Formatter.formatFileSize(mContext, size); 503 } 504 return null; 505 } 506 507 void rebuildActiveSessions() { 508 synchronized (mEntriesMap) { 509 if (!mSessionsChanged) { 510 return; 511 } 512 mActiveSessions.clear(); 513 for (int i=0; i<mSessions.size(); i++) { 514 Session s = mSessions.get(i); 515 if (s.mResumed) { 516 mActiveSessions.add(s); 517 } 518 } 519 } 520 } 521 522 public static String normalize(String str) { 523 String tmp = Normalizer.normalize(str, Form.NFD); 524 return REMOVE_DIACRITICALS_PATTERN.matcher(tmp) 525 .replaceAll("").toLowerCase(); 526 } 527 528 public class Session { 529 final Callbacks mCallbacks; 530 boolean mResumed; 531 532 // Rebuilding of app list. Synchronized on mRebuildSync. 533 final Object mRebuildSync = new Object(); 534 boolean mRebuildRequested; 535 boolean mRebuildAsync; 536 AppFilter mRebuildFilter; 537 Comparator<AppEntry> mRebuildComparator; 538 ArrayList<AppEntry> mRebuildResult; 539 ArrayList<AppEntry> mLastAppList; 540 boolean mRebuildForeground; 541 542 Session(Callbacks callbacks) { 543 mCallbacks = callbacks; 544 } 545 546 public void resume() { 547 if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock..."); 548 synchronized (mEntriesMap) { 549 if (!mResumed) { 550 mResumed = true; 551 mSessionsChanged = true; 552 doResumeIfNeededLocked(); 553 } 554 } 555 if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock"); 556 } 557 558 public void pause() { 559 if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock..."); 560 synchronized (mEntriesMap) { 561 if (mResumed) { 562 mResumed = false; 563 mSessionsChanged = true; 564 mBackgroundHandler.removeMessages(BackgroundHandler.MSG_REBUILD_LIST, this); 565 doPauseIfNeededLocked(); 566 } 567 if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock"); 568 } 569 } 570 571 public ArrayList<AppEntry> getAllApps() { 572 synchronized (mEntriesMap) { 573 return new ArrayList<>(mAppEntries); 574 } 575 } 576 577 // Creates a new list of app entries with the given filter and comparator. 578 public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) { 579 return rebuild(filter, comparator, true); 580 } 581 582 public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator, 583 boolean foreground) { 584 synchronized (mRebuildSync) { 585 synchronized (mRebuildingSessions) { 586 mRebuildingSessions.add(this); 587 mRebuildRequested = true; 588 mRebuildAsync = true; 589 mRebuildFilter = filter; 590 mRebuildComparator = comparator; 591 mRebuildForeground = foreground; 592 mRebuildResult = null; 593 if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) { 594 Message msg = mBackgroundHandler.obtainMessage( 595 BackgroundHandler.MSG_REBUILD_LIST); 596 mBackgroundHandler.sendMessage(msg); 597 } 598 } 599 600 return null; 601 } 602 } 603 604 void handleRebuildList() { 605 AppFilter filter; 606 Comparator<AppEntry> comparator; 607 synchronized (mRebuildSync) { 608 if (!mRebuildRequested) { 609 return; 610 } 611 612 filter = mRebuildFilter; 613 comparator = mRebuildComparator; 614 mRebuildRequested = false; 615 mRebuildFilter = null; 616 mRebuildComparator = null; 617 if (mRebuildForeground) { 618 Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); 619 mRebuildForeground = false; 620 } 621 } 622 623 if (filter != null) { 624 filter.init(); 625 } 626 627 List<AppEntry> apps; 628 synchronized (mEntriesMap) { 629 apps = new ArrayList<>(mAppEntries); 630 } 631 632 ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>(); 633 if (DEBUG) Log.i(TAG, "Rebuilding..."); 634 for (int i=0; i<apps.size(); i++) { 635 AppEntry entry = apps.get(i); 636 if (entry != null && (filter == null || filter.filterApp(entry))) { 637 synchronized (mEntriesMap) { 638 if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock"); 639 if (comparator != null) { 640 // Only need the label if we are going to be sorting. 641 entry.ensureLabel(mContext); 642 } 643 if (DEBUG) Log.i(TAG, "Using " + entry.info.packageName + ": " + entry); 644 filteredApps.add(entry); 645 if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock"); 646 } 647 } 648 } 649 650 if (comparator != null) { 651 Collections.sort(filteredApps, comparator); 652 } 653 654 synchronized (mRebuildSync) { 655 if (!mRebuildRequested) { 656 mLastAppList = filteredApps; 657 if (!mRebuildAsync) { 658 mRebuildResult = filteredApps; 659 mRebuildSync.notifyAll(); 660 } else { 661 if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) { 662 Message msg = mMainHandler.obtainMessage( 663 MainHandler.MSG_REBUILD_COMPLETE, this); 664 mMainHandler.sendMessage(msg); 665 } 666 } 667 } 668 } 669 670 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 671 } 672 673 public void release() { 674 pause(); 675 synchronized (mEntriesMap) { 676 mSessions.remove(this); 677 } 678 } 679 } 680 681 class MainHandler extends Handler { 682 static final int MSG_REBUILD_COMPLETE = 1; 683 static final int MSG_PACKAGE_LIST_CHANGED = 2; 684 static final int MSG_PACKAGE_ICON_CHANGED = 3; 685 static final int MSG_PACKAGE_SIZE_CHANGED = 4; 686 static final int MSG_ALL_SIZES_COMPUTED = 5; 687 static final int MSG_RUNNING_STATE_CHANGED = 6; 688 static final int MSG_LAUNCHER_INFO_CHANGED = 7; 689 static final int MSG_LOAD_ENTRIES_COMPLETE = 8; 690 691 public MainHandler(Looper looper) { 692 super(looper); 693 } 694 695 @Override 696 public void handleMessage(Message msg) { 697 rebuildActiveSessions(); 698 switch (msg.what) { 699 case MSG_REBUILD_COMPLETE: { 700 Session s = (Session)msg.obj; 701 if (mActiveSessions.contains(s)) { 702 s.mCallbacks.onRebuildComplete(s.mLastAppList); 703 } 704 } break; 705 case MSG_PACKAGE_LIST_CHANGED: { 706 for (int i=0; i<mActiveSessions.size(); i++) { 707 mActiveSessions.get(i).mCallbacks.onPackageListChanged(); 708 } 709 } break; 710 case MSG_PACKAGE_ICON_CHANGED: { 711 for (int i=0; i<mActiveSessions.size(); i++) { 712 mActiveSessions.get(i).mCallbacks.onPackageIconChanged(); 713 } 714 } break; 715 case MSG_PACKAGE_SIZE_CHANGED: { 716 for (int i=0; i<mActiveSessions.size(); i++) { 717 mActiveSessions.get(i).mCallbacks.onPackageSizeChanged( 718 (String)msg.obj); 719 } 720 } break; 721 case MSG_ALL_SIZES_COMPUTED: { 722 for (int i=0; i<mActiveSessions.size(); i++) { 723 mActiveSessions.get(i).mCallbacks.onAllSizesComputed(); 724 } 725 } break; 726 case MSG_RUNNING_STATE_CHANGED: { 727 for (int i=0; i<mActiveSessions.size(); i++) { 728 mActiveSessions.get(i).mCallbacks.onRunningStateChanged( 729 msg.arg1 != 0); 730 } 731 } break; 732 case MSG_LAUNCHER_INFO_CHANGED: { 733 for (int i=0; i<mActiveSessions.size(); i++) { 734 mActiveSessions.get(i).mCallbacks.onLauncherInfoChanged(); 735 } 736 } break; 737 case MSG_LOAD_ENTRIES_COMPLETE: { 738 for (int i=0; i<mActiveSessions.size(); i++) { 739 mActiveSessions.get(i).mCallbacks.onLoadEntriesCompleted(); 740 } 741 } break; 742 } 743 } 744 } 745 746 private class BackgroundHandler extends Handler { 747 static final int MSG_REBUILD_LIST = 1; 748 static final int MSG_LOAD_ENTRIES = 2; 749 static final int MSG_LOAD_ICONS = 3; 750 static final int MSG_LOAD_SIZES = 4; 751 static final int MSG_LOAD_LAUNCHER = 5; 752 static final int MSG_LOAD_HOME_APP = 6; 753 754 boolean mRunning; 755 756 BackgroundHandler(Looper looper) { 757 super(looper); 758 } 759 760 @Override 761 public void handleMessage(Message msg) { 762 // Always try rebuilding list first thing, if needed. 763 ArrayList<Session> rebuildingSessions = null; 764 synchronized (mRebuildingSessions) { 765 if (mRebuildingSessions.size() > 0) { 766 rebuildingSessions = new ArrayList<Session>(mRebuildingSessions); 767 mRebuildingSessions.clear(); 768 } 769 } 770 if (rebuildingSessions != null) { 771 for (int i=0; i<rebuildingSessions.size(); i++) { 772 rebuildingSessions.get(i).handleRebuildList(); 773 } 774 } 775 776 switch (msg.what) { 777 case MSG_REBUILD_LIST: { 778 } break; 779 case MSG_LOAD_ENTRIES: { 780 int numDone = 0; 781 synchronized (mEntriesMap) { 782 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES acquired lock"); 783 for (int i = 0; i < mApplications.size() && numDone < 6; i++) { 784 if (!mRunning) { 785 mRunning = true; 786 Message m = mMainHandler.obtainMessage( 787 MainHandler.MSG_RUNNING_STATE_CHANGED, 1); 788 mMainHandler.sendMessage(m); 789 } 790 ApplicationInfo info = mApplications.get(i); 791 int userId = UserHandle.getUserId(info.uid); 792 if (mEntriesMap.get(userId).get(info.packageName) == null) { 793 numDone++; 794 getEntryLocked(info); 795 } 796 if (userId != 0 && mEntriesMap.indexOfKey(0) >= 0) { 797 // If this app is for a profile and we are on the owner, remove 798 // the owner entry if it isn't installed. This will prevent 799 // duplicates of work only apps showing up as 'not installed 800 // for this user'. 801 // Note: This depends on us traversing the users in order, which 802 // happens because of the way we generate the list in 803 // doResumeIfNeededLocked. 804 AppEntry entry = mEntriesMap.get(0).get(info.packageName); 805 if (entry != null && 806 (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { 807 mEntriesMap.get(0).remove(info.packageName); 808 mAppEntries.remove(entry); 809 } 810 } 811 } 812 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock"); 813 } 814 815 if (numDone >= 6) { 816 sendEmptyMessage(MSG_LOAD_ENTRIES); 817 } else { 818 if (!mMainHandler.hasMessages(MainHandler.MSG_LOAD_ENTRIES_COMPLETE)) { 819 mMainHandler.sendEmptyMessage(MainHandler.MSG_LOAD_ENTRIES_COMPLETE); 820 } 821 sendEmptyMessage(MSG_LOAD_HOME_APP); 822 } 823 } break; 824 case MSG_LOAD_HOME_APP: { 825 final List<ResolveInfo> homeActivities = new ArrayList<>(); 826 mPm.getHomeActivities(homeActivities); 827 synchronized (mEntriesMap) { 828 final int entryCount = mEntriesMap.size(); 829 for (int i = 0; i < entryCount; i++) { 830 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP acquired lock"); 831 final HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i); 832 for (ResolveInfo activity : homeActivities) { 833 String packageName = activity.activityInfo.packageName; 834 AppEntry entry = userEntries.get(packageName); 835 if (entry != null) { 836 entry.isHomeApp = true; 837 } 838 } 839 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_HOME_APP releasing lock"); 840 } 841 } 842 sendEmptyMessage(MSG_LOAD_LAUNCHER); 843 } 844 break; 845 case MSG_LOAD_LAUNCHER: { 846 Intent launchIntent = new Intent(Intent.ACTION_MAIN, null) 847 .addCategory(Intent.CATEGORY_LAUNCHER); 848 for (int i = 0; i < mEntriesMap.size(); i++) { 849 int userId = mEntriesMap.keyAt(i); 850 // If we do not specify MATCH_DIRECT_BOOT_AWARE or 851 // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags 852 // according to the user's lock state. When the user is locked, components 853 // with ComponentInfo#directBootAware == false will be filtered. We should 854 // explicitly include both direct boot aware and unaware components here. 855 List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser( 856 launchIntent, 857 PackageManager.GET_DISABLED_COMPONENTS 858 | PackageManager.MATCH_DIRECT_BOOT_AWARE 859 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 860 userId 861 ); 862 synchronized (mEntriesMap) { 863 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock"); 864 HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i); 865 final int N = intents.size(); 866 for (int j = 0; j < N; j++) { 867 String packageName = intents.get(j).activityInfo.packageName; 868 AppEntry entry = userEntries.get(packageName); 869 if (entry != null) { 870 entry.hasLauncherEntry = true; 871 } else { 872 Log.w(TAG, "Cannot find pkg: " + packageName 873 + " on user " + userId); 874 } 875 } 876 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock"); 877 } 878 } 879 880 if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) { 881 mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED); 882 } 883 sendEmptyMessage(MSG_LOAD_ICONS); 884 } break; 885 case MSG_LOAD_ICONS: { 886 int numDone = 0; 887 synchronized (mEntriesMap) { 888 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock"); 889 for (int i=0; i<mAppEntries.size() && numDone<2; i++) { 890 AppEntry entry = mAppEntries.get(i); 891 if (entry.icon == null || !entry.mounted) { 892 synchronized (entry) { 893 if (entry.ensureIconLocked(mContext, mPm)) { 894 if (!mRunning) { 895 mRunning = true; 896 Message m = mMainHandler.obtainMessage( 897 MainHandler.MSG_RUNNING_STATE_CHANGED, 1); 898 mMainHandler.sendMessage(m); 899 } 900 numDone++; 901 } 902 } 903 } 904 } 905 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock"); 906 } 907 if (numDone > 0) { 908 if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) { 909 mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED); 910 } 911 } 912 if (numDone >= 2) { 913 sendEmptyMessage(MSG_LOAD_ICONS); 914 } else { 915 sendEmptyMessage(MSG_LOAD_SIZES); 916 } 917 } break; 918 case MSG_LOAD_SIZES: { 919 synchronized (mEntriesMap) { 920 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock"); 921 if (mCurComputingSizePkg != null) { 922 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing"); 923 return; 924 } 925 926 long now = SystemClock.uptimeMillis(); 927 for (int i=0; i<mAppEntries.size(); i++) { 928 AppEntry entry = mAppEntries.get(i); 929 if (entry.size == SIZE_UNKNOWN || entry.sizeStale) { 930 if (entry.sizeLoadStart == 0 || 931 (entry.sizeLoadStart < (now-20*1000))) { 932 if (!mRunning) { 933 mRunning = true; 934 Message m = mMainHandler.obtainMessage( 935 MainHandler.MSG_RUNNING_STATE_CHANGED, 1); 936 mMainHandler.sendMessage(m); 937 } 938 entry.sizeLoadStart = now; 939 mCurComputingSizePkg = entry.info.packageName; 940 mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid); 941 mPm.getPackageSizeInfoAsUser(mCurComputingSizePkg, 942 mCurComputingSizeUserId, mStatsObserver); 943 } 944 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing"); 945 return; 946 } 947 } 948 if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) { 949 mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED); 950 mRunning = false; 951 Message m = mMainHandler.obtainMessage( 952 MainHandler.MSG_RUNNING_STATE_CHANGED, 0); 953 mMainHandler.sendMessage(m); 954 } 955 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock"); 956 } 957 } break; 958 } 959 } 960 961 final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() { 962 public void onGetStatsCompleted(PackageStats stats, boolean succeeded) { 963 boolean sizeChanged = false; 964 synchronized (mEntriesMap) { 965 if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock"); 966 HashMap<String, AppEntry> userMap = mEntriesMap.get(stats.userHandle); 967 if (userMap == null) { 968 // The user must have been removed. 969 return; 970 } 971 AppEntry entry = userMap.get(stats.packageName); 972 if (entry != null) { 973 synchronized (entry) { 974 entry.sizeStale = false; 975 entry.sizeLoadStart = 0; 976 long externalCodeSize = stats.externalCodeSize 977 + stats.externalObbSize; 978 long externalDataSize = stats.externalDataSize 979 + stats.externalMediaSize; 980 long newSize = externalCodeSize + externalDataSize 981 + getTotalInternalSize(stats); 982 if (entry.size != newSize || 983 entry.cacheSize != stats.cacheSize || 984 entry.codeSize != stats.codeSize || 985 entry.dataSize != stats.dataSize || 986 entry.externalCodeSize != externalCodeSize || 987 entry.externalDataSize != externalDataSize || 988 entry.externalCacheSize != stats.externalCacheSize) { 989 entry.size = newSize; 990 entry.cacheSize = stats.cacheSize; 991 entry.codeSize = stats.codeSize; 992 entry.dataSize = stats.dataSize; 993 entry.externalCodeSize = externalCodeSize; 994 entry.externalDataSize = externalDataSize; 995 entry.externalCacheSize = stats.externalCacheSize; 996 entry.sizeStr = getSizeStr(entry.size); 997 entry.internalSize = getTotalInternalSize(stats); 998 entry.internalSizeStr = getSizeStr(entry.internalSize); 999 entry.externalSize = getTotalExternalSize(stats); 1000 entry.externalSizeStr = getSizeStr(entry.externalSize); 1001 if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry 1002 + ": " + entry.sizeStr); 1003 sizeChanged = true; 1004 } 1005 } 1006 if (sizeChanged) { 1007 Message msg = mMainHandler.obtainMessage( 1008 MainHandler.MSG_PACKAGE_SIZE_CHANGED, stats.packageName); 1009 mMainHandler.sendMessage(msg); 1010 } 1011 } 1012 if (mCurComputingSizePkg != null 1013 && (mCurComputingSizePkg.equals(stats.packageName) 1014 && mCurComputingSizeUserId == stats.userHandle)) { 1015 mCurComputingSizePkg = null; 1016 sendEmptyMessage(MSG_LOAD_SIZES); 1017 } 1018 if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock"); 1019 } 1020 } 1021 }; 1022 } 1023 1024 /** 1025 * Receives notifications when applications are added/removed. 1026 */ 1027 private class PackageIntentReceiver extends BroadcastReceiver { 1028 void registerReceiver() { 1029 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); 1030 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 1031 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 1032 filter.addDataScheme("package"); 1033 mContext.registerReceiver(this, filter); 1034 // Register for events related to sdcard installation. 1035 IntentFilter sdFilter = new IntentFilter(); 1036 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 1037 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 1038 mContext.registerReceiver(this, sdFilter); 1039 // Register for events related to user creation/deletion. 1040 IntentFilter userFilter = new IntentFilter(); 1041 userFilter.addAction(Intent.ACTION_USER_ADDED); 1042 userFilter.addAction(Intent.ACTION_USER_REMOVED); 1043 mContext.registerReceiver(this, userFilter); 1044 } 1045 void unregisterReceiver() { 1046 mContext.unregisterReceiver(this); 1047 } 1048 @Override 1049 public void onReceive(Context context, Intent intent) { 1050 String actionStr = intent.getAction(); 1051 if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) { 1052 Uri data = intent.getData(); 1053 String pkgName = data.getEncodedSchemeSpecificPart(); 1054 for (int i = 0; i < mEntriesMap.size(); i++) { 1055 addPackage(pkgName, mEntriesMap.keyAt(i)); 1056 } 1057 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) { 1058 Uri data = intent.getData(); 1059 String pkgName = data.getEncodedSchemeSpecificPart(); 1060 for (int i = 0; i < mEntriesMap.size(); i++) { 1061 removePackage(pkgName, mEntriesMap.keyAt(i)); 1062 } 1063 } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) { 1064 Uri data = intent.getData(); 1065 String pkgName = data.getEncodedSchemeSpecificPart(); 1066 for (int i = 0; i < mEntriesMap.size(); i++) { 1067 invalidatePackage(pkgName, mEntriesMap.keyAt(i)); 1068 } 1069 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) || 1070 Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) { 1071 // When applications become available or unavailable (perhaps because 1072 // the SD card was inserted or ejected) we need to refresh the 1073 // AppInfo with new label, icon and size information as appropriate 1074 // given the newfound (un)availability of the application. 1075 // A simple way to do that is to treat the refresh as a package 1076 // removal followed by a package addition. 1077 String pkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 1078 if (pkgList == null || pkgList.length == 0) { 1079 // Ignore 1080 return; 1081 } 1082 boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr); 1083 if (avail) { 1084 for (String pkgName : pkgList) { 1085 for (int i = 0; i < mEntriesMap.size(); i++) { 1086 invalidatePackage(pkgName, mEntriesMap.keyAt(i)); 1087 } 1088 } 1089 } 1090 } else if (Intent.ACTION_USER_ADDED.equals(actionStr)) { 1091 addUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)); 1092 } else if (Intent.ACTION_USER_REMOVED.equals(actionStr)) { 1093 removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)); 1094 } 1095 } 1096 } 1097 1098 public interface Callbacks { 1099 void onRunningStateChanged(boolean running); 1100 void onPackageListChanged(); 1101 void onRebuildComplete(ArrayList<AppEntry> apps); 1102 void onPackageIconChanged(); 1103 void onPackageSizeChanged(String packageName); 1104 void onAllSizesComputed(); 1105 void onLauncherInfoChanged(); 1106 void onLoadEntriesCompleted(); 1107 } 1108 1109 public static class SizeInfo { 1110 public long cacheSize; 1111 public long codeSize; 1112 public long dataSize; 1113 public long externalCodeSize; 1114 public long externalDataSize; 1115 1116 // This is the part of externalDataSize that is in the cache 1117 // section of external storage. Note that we don't just combine 1118 // this with cacheSize because currently the platform can't 1119 // automatically trim this data when needed, so it is something 1120 // the user may need to manage. The externalDataSize also includes 1121 // this value, since what this is here is really the part of 1122 // externalDataSize that we can just consider to be "cache" files 1123 // for purposes of cleaning them up in the app details UI. 1124 public long externalCacheSize; 1125 } 1126 1127 public static class AppEntry extends SizeInfo { 1128 public final File apkFile; 1129 public final long id; 1130 public String label; 1131 public long size; 1132 public long internalSize; 1133 public long externalSize; 1134 1135 public boolean mounted; 1136 1137 /** 1138 * Setting this to {@code true} prevents the entry to be filtered by 1139 * {@link #FILTER_DOWNLOADED_AND_LAUNCHER}. 1140 */ 1141 public boolean hasLauncherEntry; 1142 1143 /** 1144 * Whether or not it's a Home app. 1145 */ 1146 public boolean isHomeApp; 1147 1148 public String getNormalizedLabel() { 1149 if (normalizedLabel != null) { 1150 return normalizedLabel; 1151 } 1152 normalizedLabel = normalize(label); 1153 return normalizedLabel; 1154 } 1155 1156 // Need to synchronize on 'this' for the following. 1157 public ApplicationInfo info; 1158 public Drawable icon; 1159 public String sizeStr; 1160 public String internalSizeStr; 1161 public String externalSizeStr; 1162 public boolean sizeStale; 1163 public long sizeLoadStart; 1164 1165 public String normalizedLabel; 1166 1167 // A location where extra info can be placed to be used by custom filters. 1168 public Object extraInfo; 1169 1170 AppEntry(Context context, ApplicationInfo info, long id) { 1171 apkFile = new File(info.sourceDir); 1172 this.id = id; 1173 this.info = info; 1174 this.size = SIZE_UNKNOWN; 1175 this.sizeStale = true; 1176 ensureLabel(context); 1177 } 1178 1179 public void ensureLabel(Context context) { 1180 if (this.label == null || !this.mounted) { 1181 if (!this.apkFile.exists()) { 1182 this.mounted = false; 1183 this.label = info.packageName; 1184 } else { 1185 this.mounted = true; 1186 CharSequence label = info.loadLabel(context.getPackageManager()); 1187 this.label = label != null ? label.toString() : info.packageName; 1188 } 1189 } 1190 } 1191 1192 boolean ensureIconLocked(Context context, PackageManager pm) { 1193 if (this.icon == null) { 1194 if (this.apkFile.exists()) { 1195 this.icon = getBadgedIcon(pm); 1196 return true; 1197 } else { 1198 this.mounted = false; 1199 this.icon = context.getDrawable( 1200 com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon); 1201 } 1202 } else if (!this.mounted) { 1203 // If the app wasn't mounted but is now mounted, reload 1204 // its icon. 1205 if (this.apkFile.exists()) { 1206 this.mounted = true; 1207 this.icon = getBadgedIcon(pm); 1208 return true; 1209 } 1210 } 1211 return false; 1212 } 1213 1214 private Drawable getBadgedIcon(PackageManager pm) { 1215 // Do badging ourself so that it comes from the user of the app not the current user. 1216 return pm.getUserBadgedIcon(pm.loadUnbadgedItemIcon(info, info), 1217 new UserHandle(UserHandle.getUserId(info.uid))); 1218 } 1219 1220 public String getVersion(Context context) { 1221 try { 1222 return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName; 1223 } catch (PackageManager.NameNotFoundException e) { 1224 return ""; 1225 } 1226 } 1227 } 1228 1229 /** 1230 * Compare by label, then package name, then uid. 1231 */ 1232 public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() { 1233 private final Collator sCollator = Collator.getInstance(); 1234 @Override 1235 public int compare(AppEntry object1, AppEntry object2) { 1236 int compareResult = sCollator.compare(object1.label, object2.label); 1237 if (compareResult != 0) { 1238 return compareResult; 1239 } 1240 if (object1.info != null && object2.info != null) { 1241 compareResult = 1242 sCollator.compare(object1.info.packageName, object2.info.packageName); 1243 if (compareResult != 0) { 1244 return compareResult; 1245 } 1246 } 1247 return object1.info.uid - object2.info.uid; 1248 } 1249 }; 1250 1251 public static final Comparator<AppEntry> SIZE_COMPARATOR 1252 = new Comparator<AppEntry>() { 1253 @Override 1254 public int compare(AppEntry object1, AppEntry object2) { 1255 if (object1.size < object2.size) return 1; 1256 if (object1.size > object2.size) return -1; 1257 return ALPHA_COMPARATOR.compare(object1, object2); 1258 } 1259 }; 1260 1261 public static final Comparator<AppEntry> INTERNAL_SIZE_COMPARATOR 1262 = new Comparator<AppEntry>() { 1263 @Override 1264 public int compare(AppEntry object1, AppEntry object2) { 1265 if (object1.internalSize < object2.internalSize) return 1; 1266 if (object1.internalSize > object2.internalSize) return -1; 1267 return ALPHA_COMPARATOR.compare(object1, object2); 1268 } 1269 }; 1270 1271 public static final Comparator<AppEntry> EXTERNAL_SIZE_COMPARATOR 1272 = new Comparator<AppEntry>() { 1273 @Override 1274 public int compare(AppEntry object1, AppEntry object2) { 1275 if (object1.externalSize < object2.externalSize) return 1; 1276 if (object1.externalSize > object2.externalSize) return -1; 1277 return ALPHA_COMPARATOR.compare(object1, object2); 1278 } 1279 }; 1280 1281 public interface AppFilter { 1282 void init(); 1283 boolean filterApp(AppEntry info); 1284 } 1285 1286 public static final AppFilter FILTER_PERSONAL = new AppFilter() { 1287 private int mCurrentUser; 1288 1289 public void init() { 1290 mCurrentUser = ActivityManager.getCurrentUser(); 1291 } 1292 1293 @Override 1294 public boolean filterApp(AppEntry entry) { 1295 return UserHandle.getUserId(entry.info.uid) == mCurrentUser; 1296 } 1297 }; 1298 1299 public static final AppFilter FILTER_WITHOUT_DISABLED_UNTIL_USED = new AppFilter() { 1300 public void init() { 1301 // do nothings 1302 } 1303 1304 @Override 1305 public boolean filterApp(AppEntry entry) { 1306 return entry.info.enabledSetting 1307 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; 1308 } 1309 }; 1310 1311 public static final AppFilter FILTER_WORK = new AppFilter() { 1312 private int mCurrentUser; 1313 1314 public void init() { 1315 mCurrentUser = ActivityManager.getCurrentUser(); 1316 } 1317 1318 @Override 1319 public boolean filterApp(AppEntry entry) { 1320 return UserHandle.getUserId(entry.info.uid) != mCurrentUser; 1321 } 1322 }; 1323 1324 /** 1325 * Displays a combined list with "downloaded" and "visible in launcher" apps only. 1326 */ 1327 public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER = new AppFilter() { 1328 public void init() { 1329 } 1330 1331 @Override 1332 public boolean filterApp(AppEntry entry) { 1333 if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { 1334 return true; 1335 } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { 1336 return true; 1337 } else if (entry.hasLauncherEntry) { 1338 return true; 1339 } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 && entry.isHomeApp) { 1340 return true; 1341 } 1342 return false; 1343 } 1344 }; 1345 1346 public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() { 1347 public void init() { 1348 } 1349 1350 @Override 1351 public boolean filterApp(AppEntry entry) { 1352 if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { 1353 return true; 1354 } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { 1355 return true; 1356 } 1357 return false; 1358 } 1359 }; 1360 1361 public static final AppFilter FILTER_DISABLED = new AppFilter() { 1362 public void init() { 1363 } 1364 1365 @Override 1366 public boolean filterApp(AppEntry entry) { 1367 return !entry.info.enabled; 1368 } 1369 }; 1370 1371 public static final AppFilter FILTER_ALL_ENABLED = new AppFilter() { 1372 public void init() { 1373 } 1374 1375 @Override 1376 public boolean filterApp(AppEntry entry) { 1377 return entry.info.enabled; 1378 } 1379 }; 1380 1381 public static final AppFilter FILTER_EVERYTHING = new AppFilter() { 1382 public void init() { 1383 } 1384 1385 @Override 1386 public boolean filterApp(AppEntry entry) { 1387 return true; 1388 } 1389 }; 1390 1391 public static final AppFilter FILTER_WITH_DOMAIN_URLS = new AppFilter() { 1392 public void init() { 1393 } 1394 1395 @Override 1396 public boolean filterApp(AppEntry entry) { 1397 return (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0; 1398 } 1399 }; 1400 1401 public static class VolumeFilter implements AppFilter { 1402 private final String mVolumeUuid; 1403 1404 public VolumeFilter(String volumeUuid) { 1405 mVolumeUuid = volumeUuid; 1406 } 1407 1408 @Override 1409 public void init() { 1410 } 1411 1412 @Override 1413 public boolean filterApp(AppEntry info) { 1414 return Objects.equals(info.info.volumeUuid, mVolumeUuid); 1415 } 1416 } 1417 1418 public static class CompoundFilter implements AppFilter { 1419 private final AppFilter mFirstFilter; 1420 private final AppFilter mSecondFilter; 1421 1422 public CompoundFilter(AppFilter first, AppFilter second) { 1423 mFirstFilter = first; 1424 mSecondFilter = second; 1425 } 1426 1427 @Override 1428 public void init() { 1429 mFirstFilter.init(); 1430 mSecondFilter.init(); 1431 } 1432 1433 @Override 1434 public boolean filterApp(AppEntry info) { 1435 return mFirstFilter.filterApp(info) && mSecondFilter.filterApp(info); 1436 } 1437 } 1438} 1439