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