1/* 2 * Copyright (C) 2010 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.settings.applications; 18 19import com.android.settings.R; 20import com.android.settings.users.UserUtils; 21 22import android.app.ActivityManager; 23import android.app.ActivityManagerNative; 24import android.app.ActivityThread; 25import android.content.ComponentName; 26import android.content.Context; 27import android.content.pm.ApplicationInfo; 28import android.content.pm.PackageInfo; 29import android.content.pm.PackageItemInfo; 30import android.content.pm.PackageManager; 31import android.content.pm.ServiceInfo; 32import android.content.pm.UserInfo; 33import android.content.res.Resources; 34import android.graphics.drawable.Drawable; 35import android.graphics.drawable.Drawable.ConstantState; 36import android.os.Handler; 37import android.os.HandlerThread; 38import android.os.Looper; 39import android.os.Message; 40import android.os.RemoteException; 41import android.os.UserHandle; 42import android.os.UserManager; 43import android.text.format.Formatter; 44import android.util.Log; 45import android.util.SparseArray; 46 47import java.util.ArrayList; 48import java.util.Collections; 49import java.util.Comparator; 50import java.util.HashMap; 51import java.util.Iterator; 52import java.util.List; 53 54/** 55 * Singleton for retrieving and monitoring the state about all running 56 * applications/processes/services. 57 */ 58public class RunningState { 59 static final String TAG = "RunningState"; 60 static final boolean DEBUG_COMPARE = false; 61 62 static Object sGlobalLock = new Object(); 63 static RunningState sInstance; 64 65 static final int MSG_RESET_CONTENTS = 1; 66 static final int MSG_UPDATE_CONTENTS = 2; 67 static final int MSG_REFRESH_UI = 3; 68 static final int MSG_UPDATE_TIME = 4; 69 70 static final long TIME_UPDATE_DELAY = 1000; 71 static final long CONTENTS_UPDATE_DELAY = 2000; 72 73 static final int MAX_SERVICES = 100; 74 75 final Context mApplicationContext; 76 final ActivityManager mAm; 77 final PackageManager mPm; 78 final UserManager mUm; 79 final int mMyUserId; 80 81 OnRefreshUiListener mRefreshUiListener; 82 83 final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges(); 84 85 // Processes that are hosting a service we are interested in, organized 86 // by uid and name. Note that this mapping does not change even across 87 // service restarts, and during a restart there will still be a process 88 // entry. 89 final SparseArray<HashMap<String, ProcessItem>> mServiceProcessesByName 90 = new SparseArray<HashMap<String, ProcessItem>>(); 91 92 // Processes that are hosting a service we are interested in, organized 93 // by their pid. These disappear and re-appear as services are restarted. 94 final SparseArray<ProcessItem> mServiceProcessesByPid 95 = new SparseArray<ProcessItem>(); 96 97 // Used to sort the interesting processes. 98 final ServiceProcessComparator mServiceProcessComparator 99 = new ServiceProcessComparator(); 100 101 // Additional interesting processes to be shown to the user, even if 102 // there is no service running in them. 103 final ArrayList<ProcessItem> mInterestingProcesses = new ArrayList<ProcessItem>(); 104 105 // All currently running processes, for finding dependencies etc. 106 final SparseArray<ProcessItem> mRunningProcesses 107 = new SparseArray<ProcessItem>(); 108 109 // The processes associated with services, in sorted order. 110 final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>(); 111 112 // All processes, used for retrieving memory information. 113 final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>(); 114 115 // If there are other users on the device, these are the merged items 116 // representing all items that would be put in mMergedItems for that user. 117 final SparseArray<MergedItem> mOtherUserMergedItems = new SparseArray<MergedItem>(); 118 119 // If there are other users on the device, these are the merged items 120 // representing all items that would be put in mUserBackgroundItems for that user. 121 final SparseArray<MergedItem> mOtherUserBackgroundItems = new SparseArray<MergedItem>(); 122 123 // Tracking of information about users. 124 final SparseArray<UserState> mUsers = new SparseArray<UserState>(); 125 126 static class AppProcessInfo { 127 final ActivityManager.RunningAppProcessInfo info; 128 boolean hasServices; 129 boolean hasForegroundServices; 130 131 AppProcessInfo(ActivityManager.RunningAppProcessInfo _info) { 132 info = _info; 133 } 134 } 135 136 // Temporary structure used when updating above information. 137 final SparseArray<AppProcessInfo> mTmpAppProcesses = new SparseArray<AppProcessInfo>(); 138 139 int mSequence = 0; 140 141 final Comparator<RunningState.MergedItem> mBackgroundComparator 142 = new Comparator<RunningState.MergedItem>() { 143 @Override 144 public int compare(MergedItem lhs, MergedItem rhs) { 145 if (DEBUG_COMPARE) { 146 Log.i(TAG, "Comparing " + lhs + " with " + rhs); 147 Log.i(TAG, " Proc " + lhs.mProcess + " with " + rhs.mProcess); 148 Log.i(TAG, " UserId " + lhs.mUserId + " with " + rhs.mUserId); 149 } 150 if (lhs.mUserId != rhs.mUserId) { 151 if (lhs.mUserId == mMyUserId) return -1; 152 if (rhs.mUserId == mMyUserId) return 1; 153 return lhs.mUserId < rhs.mUserId ? -1 : 1; 154 } 155 if (lhs.mProcess == rhs.mProcess) { 156 if (lhs.mLabel == rhs.mLabel) { 157 return 0; 158 } 159 return lhs.mLabel != null ? lhs.mLabel.compareTo(rhs.mLabel) : -1; 160 } 161 if (lhs.mProcess == null) return -1; 162 if (rhs.mProcess == null) return 1; 163 if (DEBUG_COMPARE) Log.i(TAG, " Label " + lhs.mProcess.mLabel 164 + " with " + rhs.mProcess.mLabel); 165 final ActivityManager.RunningAppProcessInfo lhsInfo 166 = lhs.mProcess.mRunningProcessInfo; 167 final ActivityManager.RunningAppProcessInfo rhsInfo 168 = rhs.mProcess.mRunningProcessInfo; 169 final boolean lhsBg = lhsInfo.importance 170 >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; 171 final boolean rhsBg = rhsInfo.importance 172 >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; 173 if (DEBUG_COMPARE) Log.i(TAG, " Bg " + lhsBg + " with " + rhsBg); 174 if (lhsBg != rhsBg) { 175 return lhsBg ? 1 : -1; 176 } 177 final boolean lhsA = (lhsInfo.flags 178 & ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES) != 0; 179 final boolean rhsA = (rhsInfo.flags 180 & ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES) != 0; 181 if (DEBUG_COMPARE) Log.i(TAG, " Act " + lhsA + " with " + rhsA); 182 if (lhsA != rhsA) { 183 return lhsA ? -1 : 1; 184 } 185 if (DEBUG_COMPARE) Log.i(TAG, " Lru " + lhsInfo.lru + " with " + rhsInfo.lru); 186 if (lhsInfo.lru != rhsInfo.lru) { 187 return lhsInfo.lru < rhsInfo.lru ? -1 : 1; 188 } 189 if (lhs.mProcess.mLabel == rhs.mProcess.mLabel) { 190 return 0; 191 } 192 if (lhs.mProcess.mLabel == null) return 1; 193 if (rhs.mProcess.mLabel == null) return -1; 194 return lhs.mProcess.mLabel.compareTo(rhs.mProcess.mLabel); 195 } 196 }; 197 198 // ----- following protected by mLock ----- 199 200 // Lock for protecting the state that will be shared between the 201 // background update thread and the UI thread. 202 final Object mLock = new Object(); 203 204 boolean mResumed; 205 boolean mHaveData; 206 boolean mWatchingBackgroundItems; 207 208 ArrayList<BaseItem> mItems = new ArrayList<BaseItem>(); 209 ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>(); 210 ArrayList<MergedItem> mBackgroundItems = new ArrayList<MergedItem>(); 211 ArrayList<MergedItem> mUserBackgroundItems = new ArrayList<MergedItem>(); 212 213 int mNumBackgroundProcesses; 214 long mBackgroundProcessMemory; 215 int mNumForegroundProcesses; 216 long mForegroundProcessMemory; 217 int mNumServiceProcesses; 218 long mServiceProcessMemory; 219 220 // ----- BACKGROUND MONITORING THREAD ----- 221 222 final HandlerThread mBackgroundThread; 223 final class BackgroundHandler extends Handler { 224 public BackgroundHandler(Looper looper) { 225 super(looper); 226 } 227 228 @Override 229 public void handleMessage(Message msg) { 230 switch (msg.what) { 231 case MSG_RESET_CONTENTS: 232 reset(); 233 break; 234 case MSG_UPDATE_CONTENTS: 235 synchronized (mLock) { 236 if (!mResumed) { 237 return; 238 } 239 } 240 Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI); 241 cmd.arg1 = update(mApplicationContext, mAm) ? 1 : 0; 242 mHandler.sendMessage(cmd); 243 removeMessages(MSG_UPDATE_CONTENTS); 244 msg = obtainMessage(MSG_UPDATE_CONTENTS); 245 sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY); 246 break; 247 } 248 } 249 }; 250 251 final BackgroundHandler mBackgroundHandler; 252 253 final Handler mHandler = new Handler() { 254 int mNextUpdate = OnRefreshUiListener.REFRESH_TIME; 255 256 @Override 257 public void handleMessage(Message msg) { 258 switch (msg.what) { 259 case MSG_REFRESH_UI: 260 mNextUpdate = msg.arg1 != 0 261 ? OnRefreshUiListener.REFRESH_STRUCTURE 262 : OnRefreshUiListener.REFRESH_DATA; 263 break; 264 case MSG_UPDATE_TIME: 265 synchronized (mLock) { 266 if (!mResumed) { 267 return; 268 } 269 } 270 removeMessages(MSG_UPDATE_TIME); 271 Message m = obtainMessage(MSG_UPDATE_TIME); 272 sendMessageDelayed(m, TIME_UPDATE_DELAY); 273 274 if (mRefreshUiListener != null) { 275 //Log.i("foo", "Refresh UI: " + mNextUpdate 276 // + " @ " + SystemClock.uptimeMillis()); 277 mRefreshUiListener.onRefreshUi(mNextUpdate); 278 mNextUpdate = OnRefreshUiListener.REFRESH_TIME; 279 } 280 break; 281 } 282 } 283 }; 284 285 // ----- DATA STRUCTURES ----- 286 287 static interface OnRefreshUiListener { 288 public static final int REFRESH_TIME = 0; 289 public static final int REFRESH_DATA = 1; 290 public static final int REFRESH_STRUCTURE = 2; 291 292 public void onRefreshUi(int what); 293 } 294 295 static class UserState { 296 UserInfo mInfo; 297 String mLabel; 298 Drawable mIcon; 299 } 300 301 static class BaseItem { 302 final boolean mIsProcess; 303 final int mUserId; 304 305 PackageItemInfo mPackageInfo; 306 CharSequence mDisplayLabel; 307 String mLabel; 308 String mDescription; 309 310 int mCurSeq; 311 312 long mActiveSince; 313 long mSize; 314 String mSizeStr; 315 String mCurSizeStr; 316 boolean mNeedDivider; 317 boolean mBackground; 318 319 public BaseItem(boolean isProcess, int userId) { 320 mIsProcess = isProcess; 321 mUserId = userId; 322 } 323 324 public Drawable loadIcon(Context context, RunningState state) { 325 if (mPackageInfo != null) { 326 return mPackageInfo.loadIcon(state.mPm); 327 } 328 return null; 329 } 330 } 331 332 static class ServiceItem extends BaseItem { 333 ActivityManager.RunningServiceInfo mRunningService; 334 ServiceInfo mServiceInfo; 335 boolean mShownAsStarted; 336 337 MergedItem mMergedItem; 338 339 public ServiceItem(int userId) { 340 super(false, userId); 341 } 342 } 343 344 static class ProcessItem extends BaseItem { 345 final HashMap<ComponentName, ServiceItem> mServices 346 = new HashMap<ComponentName, ServiceItem>(); 347 final SparseArray<ProcessItem> mDependentProcesses 348 = new SparseArray<ProcessItem>(); 349 350 final int mUid; 351 final String mProcessName; 352 int mPid; 353 354 ProcessItem mClient; 355 int mLastNumDependentProcesses; 356 357 int mRunningSeq; 358 ActivityManager.RunningAppProcessInfo mRunningProcessInfo; 359 360 MergedItem mMergedItem; 361 362 boolean mInteresting; 363 364 // Purely for sorting. 365 boolean mIsSystem; 366 boolean mIsStarted; 367 long mActiveSince; 368 369 public ProcessItem(Context context, int uid, String processName) { 370 super(true, UserHandle.getUserId(uid)); 371 mDescription = context.getResources().getString( 372 R.string.service_process_name, processName); 373 mUid = uid; 374 mProcessName = processName; 375 } 376 377 void ensureLabel(PackageManager pm) { 378 if (mLabel != null) { 379 return; 380 } 381 382 try { 383 ApplicationInfo ai = pm.getApplicationInfo(mProcessName, 384 PackageManager.GET_UNINSTALLED_PACKAGES); 385 if (ai.uid == mUid) { 386 mDisplayLabel = ai.loadLabel(pm); 387 mLabel = mDisplayLabel.toString(); 388 mPackageInfo = ai; 389 return; 390 } 391 } catch (PackageManager.NameNotFoundException e) { 392 } 393 394 // If we couldn't get information about the overall 395 // process, try to find something about the uid. 396 String[] pkgs = pm.getPackagesForUid(mUid); 397 398 // If there is one package with this uid, that is what we want. 399 if (pkgs.length == 1) { 400 try { 401 ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 402 PackageManager.GET_UNINSTALLED_PACKAGES); 403 mDisplayLabel = ai.loadLabel(pm); 404 mLabel = mDisplayLabel.toString(); 405 mPackageInfo = ai; 406 return; 407 } catch (PackageManager.NameNotFoundException e) { 408 } 409 } 410 411 // If there are multiple, see if one gives us the official name 412 // for this uid. 413 for (String name : pkgs) { 414 try { 415 PackageInfo pi = pm.getPackageInfo(name, 0); 416 if (pi.sharedUserLabel != 0) { 417 CharSequence nm = pm.getText(name, 418 pi.sharedUserLabel, pi.applicationInfo); 419 if (nm != null) { 420 mDisplayLabel = nm; 421 mLabel = nm.toString(); 422 mPackageInfo = pi.applicationInfo; 423 return; 424 } 425 } 426 } catch (PackageManager.NameNotFoundException e) { 427 } 428 } 429 430 // If still don't have anything to display, just use the 431 // service info. 432 if (mServices.size() > 0) { 433 ApplicationInfo ai = mServices.values().iterator().next() 434 .mServiceInfo.applicationInfo; 435 mPackageInfo = ai; 436 mDisplayLabel = mPackageInfo.loadLabel(pm); 437 mLabel = mDisplayLabel.toString(); 438 return; 439 } 440 441 // Finally... whatever, just pick the first package's name. 442 try { 443 ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 444 PackageManager.GET_UNINSTALLED_PACKAGES); 445 mDisplayLabel = ai.loadLabel(pm); 446 mLabel = mDisplayLabel.toString(); 447 mPackageInfo = ai; 448 return; 449 } catch (PackageManager.NameNotFoundException e) { 450 } 451 } 452 453 boolean updateService(Context context, ActivityManager.RunningServiceInfo service) { 454 final PackageManager pm = context.getPackageManager(); 455 456 boolean changed = false; 457 ServiceItem si = mServices.get(service.service); 458 if (si == null) { 459 changed = true; 460 si = new ServiceItem(mUserId); 461 si.mRunningService = service; 462 try { 463 si.mServiceInfo = ActivityThread.getPackageManager().getServiceInfo( 464 service.service, PackageManager.GET_UNINSTALLED_PACKAGES, 465 UserHandle.getUserId(service.uid)); 466 467 if (si.mServiceInfo == null) { 468 Log.d("RunningService", "getServiceInfo returned null for: " 469 + service.service); 470 return false; 471 } 472 } catch (RemoteException e) { 473 } 474 si.mDisplayLabel = makeLabel(pm, 475 si.mRunningService.service.getClassName(), si.mServiceInfo); 476 mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null; 477 si.mPackageInfo = si.mServiceInfo.applicationInfo; 478 mServices.put(service.service, si); 479 } 480 si.mCurSeq = mCurSeq; 481 si.mRunningService = service; 482 long activeSince = service.restarting == 0 ? service.activeSince : -1; 483 if (si.mActiveSince != activeSince) { 484 si.mActiveSince = activeSince; 485 changed = true; 486 } 487 if (service.clientPackage != null && service.clientLabel != 0) { 488 if (si.mShownAsStarted) { 489 si.mShownAsStarted = false; 490 changed = true; 491 } 492 try { 493 Resources clientr = pm.getResourcesForApplication(service.clientPackage); 494 String label = clientr.getString(service.clientLabel); 495 si.mDescription = context.getResources().getString( 496 R.string.service_client_name, label); 497 } catch (PackageManager.NameNotFoundException e) { 498 si.mDescription = null; 499 } 500 } else { 501 if (!si.mShownAsStarted) { 502 si.mShownAsStarted = true; 503 changed = true; 504 } 505 si.mDescription = context.getResources().getString( 506 R.string.service_started_by_app); 507 } 508 509 return changed; 510 } 511 512 boolean updateSize(Context context, long pss, int curSeq) { 513 mSize = pss * 1024; 514 if (mCurSeq == curSeq) { 515 String sizeStr = Formatter.formatShortFileSize( 516 context, mSize); 517 if (!sizeStr.equals(mSizeStr)){ 518 mSizeStr = sizeStr; 519 // We update this on the second tick where we update just 520 // the text in the current items, so no need to say we 521 // changed here. 522 return false; 523 } 524 } 525 return false; 526 } 527 528 boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) { 529 final int NP = mDependentProcesses.size(); 530 boolean changed = false; 531 for (int i=0; i<NP; i++) { 532 ProcessItem proc = mDependentProcesses.valueAt(i); 533 if (proc.mClient != this) { 534 changed = true; 535 proc.mClient = this; 536 } 537 proc.mCurSeq = curSeq; 538 proc.ensureLabel(pm); 539 changed |= proc.buildDependencyChain(context, pm, curSeq); 540 } 541 542 if (mLastNumDependentProcesses != mDependentProcesses.size()) { 543 changed = true; 544 mLastNumDependentProcesses = mDependentProcesses.size(); 545 } 546 547 return changed; 548 } 549 550 void addDependentProcesses(ArrayList<BaseItem> dest, 551 ArrayList<ProcessItem> destProc) { 552 final int NP = mDependentProcesses.size(); 553 for (int i=0; i<NP; i++) { 554 ProcessItem proc = mDependentProcesses.valueAt(i); 555 proc.addDependentProcesses(dest, destProc); 556 dest.add(proc); 557 if (proc.mPid > 0) { 558 destProc.add(proc); 559 } 560 } 561 } 562 } 563 564 static class MergedItem extends BaseItem { 565 ProcessItem mProcess; 566 UserState mUser; 567 final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>(); 568 final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>(); 569 final ArrayList<MergedItem> mChildren = new ArrayList<MergedItem>(); 570 571 private int mLastNumProcesses = -1, mLastNumServices = -1; 572 573 MergedItem(int userId) { 574 super(false, userId); 575 } 576 577 private void setDescription(Context context, int numProcesses, int numServices) { 578 if (mLastNumProcesses != numProcesses || mLastNumServices != numServices) { 579 mLastNumProcesses = numProcesses; 580 mLastNumServices = numServices; 581 int resid = R.string.running_processes_item_description_s_s; 582 if (numProcesses != 1) { 583 resid = numServices != 1 584 ? R.string.running_processes_item_description_p_p 585 : R.string.running_processes_item_description_p_s; 586 } else if (numServices != 1) { 587 resid = R.string.running_processes_item_description_s_p; 588 } 589 mDescription = context.getResources().getString(resid, numProcesses, 590 numServices); 591 } 592 } 593 594 boolean update(Context context, boolean background) { 595 mBackground = background; 596 597 if (mUser != null) { 598 // This is a merged item that contains a child collection 599 // of items... that is, it is an entire user, containing 600 // everything associated with that user. So set it up as such. 601 // For concrete stuff we need about the process of this item, 602 // we will just use the info from the first child. 603 MergedItem child0 = mChildren.get(0); 604 mPackageInfo = child0.mProcess.mPackageInfo; 605 mLabel = mUser != null ? mUser.mLabel : null; 606 mDisplayLabel = mLabel; 607 int numProcesses = 0; 608 int numServices = 0; 609 mActiveSince = -1; 610 for (int i=0; i<mChildren.size(); i++) { 611 MergedItem child = mChildren.get(i); 612 numProcesses += child.mLastNumProcesses; 613 numServices += child.mLastNumServices; 614 if (child.mActiveSince >= 0 && mActiveSince < child.mActiveSince) { 615 mActiveSince = child.mActiveSince; 616 } 617 } 618 if (!mBackground) { 619 setDescription(context, numProcesses, numServices); 620 } 621 } else { 622 mPackageInfo = mProcess.mPackageInfo; 623 mDisplayLabel = mProcess.mDisplayLabel; 624 mLabel = mProcess.mLabel; 625 626 if (!mBackground) { 627 setDescription(context, (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size(), 628 mServices.size()); 629 } 630 631 mActiveSince = -1; 632 for (int i=0; i<mServices.size(); i++) { 633 ServiceItem si = mServices.get(i); 634 if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) { 635 mActiveSince = si.mActiveSince; 636 } 637 } 638 } 639 640 return false; 641 } 642 643 boolean updateSize(Context context) { 644 if (mUser != null) { 645 mSize = 0; 646 for (int i=0; i<mChildren.size(); i++) { 647 MergedItem child = mChildren.get(i); 648 child.updateSize(context); 649 mSize += child.mSize; 650 } 651 } else { 652 mSize = mProcess.mSize; 653 for (int i=0; i<mOtherProcesses.size(); i++) { 654 mSize += mOtherProcesses.get(i).mSize; 655 } 656 } 657 658 String sizeStr = Formatter.formatShortFileSize( 659 context, mSize); 660 if (!sizeStr.equals(mSizeStr)){ 661 mSizeStr = sizeStr; 662 // We update this on the second tick where we update just 663 // the text in the current items, so no need to say we 664 // changed here. 665 return false; 666 } 667 return false; 668 } 669 670 public Drawable loadIcon(Context context, RunningState state) { 671 if (mUser == null) { 672 return super.loadIcon(context, state); 673 } 674 if (mUser.mIcon != null) { 675 ConstantState constState = mUser.mIcon.getConstantState(); 676 if (constState == null) { 677 return mUser.mIcon; 678 } else { 679 return constState.newDrawable(); 680 } 681 } 682 return context.getResources().getDrawable( 683 com.android.internal.R.drawable.ic_menu_cc); 684 } 685 } 686 687 class ServiceProcessComparator implements Comparator<ProcessItem> { 688 public int compare(ProcessItem object1, ProcessItem object2) { 689 if (object1.mUserId != object2.mUserId) { 690 if (object1.mUserId == mMyUserId) return -1; 691 if (object2.mUserId == mMyUserId) return 1; 692 return object1.mUserId < object2.mUserId ? -1 : 1; 693 } 694 if (object1.mIsStarted != object2.mIsStarted) { 695 // Non-started processes go last. 696 return object1.mIsStarted ? -1 : 1; 697 } 698 if (object1.mIsSystem != object2.mIsSystem) { 699 // System processes go below non-system. 700 return object1.mIsSystem ? 1 : -1; 701 } 702 if (object1.mActiveSince != object2.mActiveSince) { 703 // Remaining ones are sorted with the longest running 704 // services last. 705 return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1; 706 } 707 return 0; 708 } 709 } 710 711 static CharSequence makeLabel(PackageManager pm, 712 String className, PackageItemInfo item) { 713 if (item != null && (item.labelRes != 0 714 || item.nonLocalizedLabel != null)) { 715 CharSequence label = item.loadLabel(pm); 716 if (label != null) { 717 return label; 718 } 719 } 720 721 String label = className; 722 int tail = label.lastIndexOf('.'); 723 if (tail >= 0) { 724 label = label.substring(tail+1, label.length()); 725 } 726 return label; 727 } 728 729 static RunningState getInstance(Context context) { 730 synchronized (sGlobalLock) { 731 if (sInstance == null) { 732 sInstance = new RunningState(context); 733 } 734 return sInstance; 735 } 736 } 737 738 private RunningState(Context context) { 739 mApplicationContext = context.getApplicationContext(); 740 mAm = (ActivityManager)mApplicationContext.getSystemService(Context.ACTIVITY_SERVICE); 741 mPm = mApplicationContext.getPackageManager(); 742 mUm = (UserManager)mApplicationContext.getSystemService(Context.USER_SERVICE); 743 mMyUserId = UserHandle.myUserId(); 744 mResumed = false; 745 mBackgroundThread = new HandlerThread("RunningState:Background"); 746 mBackgroundThread.start(); 747 mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper()); 748 } 749 750 void resume(OnRefreshUiListener listener) { 751 synchronized (mLock) { 752 mResumed = true; 753 mRefreshUiListener = listener; 754 if (mInterestingConfigChanges.applyNewConfig(mApplicationContext.getResources())) { 755 mHaveData = false; 756 mBackgroundHandler.removeMessages(MSG_RESET_CONTENTS); 757 mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS); 758 mBackgroundHandler.sendEmptyMessage(MSG_RESET_CONTENTS); 759 } 760 if (!mBackgroundHandler.hasMessages(MSG_UPDATE_CONTENTS)) { 761 mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS); 762 } 763 mHandler.sendEmptyMessage(MSG_UPDATE_TIME); 764 } 765 } 766 767 void updateNow() { 768 synchronized (mLock) { 769 mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS); 770 mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS); 771 } 772 } 773 774 boolean hasData() { 775 synchronized (mLock) { 776 return mHaveData; 777 } 778 } 779 780 void waitForData() { 781 synchronized (mLock) { 782 while (!mHaveData) { 783 try { 784 mLock.wait(0); 785 } catch (InterruptedException e) { 786 } 787 } 788 } 789 } 790 791 void pause() { 792 synchronized (mLock) { 793 mResumed = false; 794 mRefreshUiListener = null; 795 mHandler.removeMessages(MSG_UPDATE_TIME); 796 } 797 } 798 799 private boolean isInterestingProcess(ActivityManager.RunningAppProcessInfo pi) { 800 if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE) != 0) { 801 return true; 802 } 803 if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT) == 0 804 && pi.importance >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND 805 && pi.importance < ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE 806 && pi.importanceReasonCode 807 == ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) { 808 return true; 809 } 810 return false; 811 } 812 813 private void reset() { 814 mServiceProcessesByName.clear(); 815 mServiceProcessesByPid.clear(); 816 mInterestingProcesses.clear(); 817 mRunningProcesses.clear(); 818 mProcessItems.clear(); 819 mAllProcessItems.clear(); 820 mUsers.clear(); 821 } 822 823 private void addOtherUserItem(Context context, ArrayList<MergedItem> newMergedItems, 824 SparseArray<MergedItem> userItems, MergedItem newItem) { 825 MergedItem userItem = userItems.get(newItem.mUserId); 826 boolean first = userItem == null || userItem.mCurSeq != mSequence; 827 if (first) { 828 if (userItem == null) { 829 userItem = new MergedItem(newItem.mUserId); 830 userItems.put(newItem.mUserId, userItem); 831 } else { 832 userItem.mChildren.clear(); 833 } 834 userItem.mCurSeq = mSequence; 835 if ((userItem.mUser=mUsers.get(newItem.mUserId)) == null) { 836 userItem.mUser = new UserState(); 837 UserInfo info = mUm.getUserInfo(newItem.mUserId); 838 userItem.mUser.mInfo = info; 839 if (info != null) { 840 userItem.mUser.mIcon = UserUtils.getUserIcon(context, mUm, 841 info, context.getResources()); 842 } 843 String name = info != null ? info.name : null; 844 if (name == null) { 845 name = Integer.toString(info.id); 846 } 847 userItem.mUser.mLabel = context.getResources().getString( 848 R.string.running_process_item_user_label, name); 849 } 850 newMergedItems.add(userItem); 851 } 852 userItem.mChildren.add(newItem); 853 } 854 855 private boolean update(Context context, ActivityManager am) { 856 final PackageManager pm = context.getPackageManager(); 857 858 mSequence++; 859 860 boolean changed = false; 861 862 // Retrieve list of services, filtering out anything that definitely 863 // won't be shown in the UI. 864 List<ActivityManager.RunningServiceInfo> services 865 = am.getRunningServices(MAX_SERVICES); 866 int NS = services != null ? services.size() : 0; 867 for (int i=0; i<NS; i++) { 868 ActivityManager.RunningServiceInfo si = services.get(i); 869 // We are not interested in services that have not been started 870 // and don't have a known client, because 871 // there is nothing the user can do about them. 872 if (!si.started && si.clientLabel == 0) { 873 services.remove(i); 874 i--; 875 NS--; 876 continue; 877 } 878 // We likewise don't care about services running in a 879 // persistent process like the system or phone. 880 if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS) 881 != 0) { 882 services.remove(i); 883 i--; 884 NS--; 885 continue; 886 } 887 } 888 889 // Retrieve list of running processes, organizing them into a sparse 890 // array for easy retrieval. 891 List<ActivityManager.RunningAppProcessInfo> processes 892 = am.getRunningAppProcesses(); 893 final int NP = processes != null ? processes.size() : 0; 894 mTmpAppProcesses.clear(); 895 for (int i=0; i<NP; i++) { 896 ActivityManager.RunningAppProcessInfo pi = processes.get(i); 897 mTmpAppProcesses.put(pi.pid, new AppProcessInfo(pi)); 898 } 899 900 // Initial iteration through running services to collect per-process 901 // info about them. 902 for (int i=0; i<NS; i++) { 903 ActivityManager.RunningServiceInfo si = services.get(i); 904 if (si.restarting == 0 && si.pid > 0) { 905 AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid); 906 if (ainfo != null) { 907 ainfo.hasServices = true; 908 if (si.foreground) { 909 ainfo.hasForegroundServices = true; 910 } 911 } 912 } 913 } 914 915 // Update state we are maintaining about process that are running services. 916 for (int i=0; i<NS; i++) { 917 ActivityManager.RunningServiceInfo si = services.get(i); 918 919 // If this service's process is in use at a higher importance 920 // due to another process bound to one of its services, then we 921 // won't put it in the top-level list of services. Instead we 922 // want it to be included in the set of processes that the other 923 // process needs. 924 if (si.restarting == 0 && si.pid > 0) { 925 AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid); 926 if (ainfo != null && !ainfo.hasForegroundServices) { 927 // This process does not have any foreground services. 928 // If its importance is greater than the service importance 929 // then there is something else more significant that is 930 // keeping it around that it should possibly be included as 931 // a part of instead of being shown by itself. 932 if (ainfo.info.importance 933 < ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE) { 934 // Follow process chain to see if there is something 935 // else that could be shown 936 boolean skip = false; 937 ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid); 938 while (ainfo != null) { 939 if (ainfo.hasServices || isInterestingProcess(ainfo.info)) { 940 skip = true; 941 break; 942 } 943 ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid); 944 } 945 if (skip) { 946 continue; 947 } 948 } 949 } 950 } 951 952 HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid); 953 if (procs == null) { 954 procs = new HashMap<String, ProcessItem>(); 955 mServiceProcessesByName.put(si.uid, procs); 956 } 957 ProcessItem proc = procs.get(si.process); 958 if (proc == null) { 959 changed = true; 960 proc = new ProcessItem(context, si.uid, si.process); 961 procs.put(si.process, proc); 962 } 963 964 if (proc.mCurSeq != mSequence) { 965 int pid = si.restarting == 0 ? si.pid : 0; 966 if (pid != proc.mPid) { 967 changed = true; 968 if (proc.mPid != pid) { 969 if (proc.mPid != 0) { 970 mServiceProcessesByPid.remove(proc.mPid); 971 } 972 if (pid != 0) { 973 mServiceProcessesByPid.put(pid, proc); 974 } 975 proc.mPid = pid; 976 } 977 } 978 proc.mDependentProcesses.clear(); 979 proc.mCurSeq = mSequence; 980 } 981 changed |= proc.updateService(context, si); 982 } 983 984 // Now update the map of other processes that are running (but 985 // don't have services actively running inside them). 986 for (int i=0; i<NP; i++) { 987 ActivityManager.RunningAppProcessInfo pi = processes.get(i); 988 ProcessItem proc = mServiceProcessesByPid.get(pi.pid); 989 if (proc == null) { 990 // This process is not one that is a direct container 991 // of a service, so look for it in the secondary 992 // running list. 993 proc = mRunningProcesses.get(pi.pid); 994 if (proc == null) { 995 changed = true; 996 proc = new ProcessItem(context, pi.uid, pi.processName); 997 proc.mPid = pi.pid; 998 mRunningProcesses.put(pi.pid, proc); 999 } 1000 proc.mDependentProcesses.clear(); 1001 } 1002 1003 if (isInterestingProcess(pi)) { 1004 if (!mInterestingProcesses.contains(proc)) { 1005 changed = true; 1006 mInterestingProcesses.add(proc); 1007 } 1008 proc.mCurSeq = mSequence; 1009 proc.mInteresting = true; 1010 proc.ensureLabel(pm); 1011 } else { 1012 proc.mInteresting = false; 1013 } 1014 1015 proc.mRunningSeq = mSequence; 1016 proc.mRunningProcessInfo = pi; 1017 } 1018 1019 // Build the chains from client processes to the process they are 1020 // dependent on; also remove any old running processes. 1021 int NRP = mRunningProcesses.size(); 1022 for (int i = 0; i < NRP;) { 1023 ProcessItem proc = mRunningProcesses.valueAt(i); 1024 if (proc.mRunningSeq == mSequence) { 1025 int clientPid = proc.mRunningProcessInfo.importanceReasonPid; 1026 if (clientPid != 0) { 1027 ProcessItem client = mServiceProcessesByPid.get(clientPid); 1028 if (client == null) { 1029 client = mRunningProcesses.get(clientPid); 1030 } 1031 if (client != null) { 1032 client.mDependentProcesses.put(proc.mPid, proc); 1033 } 1034 } else { 1035 // In this pass the process doesn't have a client. 1036 // Clear to make sure that, if it later gets the same one, 1037 // we will detect the change. 1038 proc.mClient = null; 1039 } 1040 i++; 1041 } else { 1042 changed = true; 1043 mRunningProcesses.remove(mRunningProcesses.keyAt(i)); 1044 NRP--; 1045 } 1046 } 1047 1048 // Remove any old interesting processes. 1049 int NHP = mInterestingProcesses.size(); 1050 for (int i=0; i<NHP; i++) { 1051 ProcessItem proc = mInterestingProcesses.get(i); 1052 if (!proc.mInteresting || mRunningProcesses.get(proc.mPid) == null) { 1053 changed = true; 1054 mInterestingProcesses.remove(i); 1055 i--; 1056 NHP--; 1057 } 1058 } 1059 1060 // Follow the tree from all primary service processes to all 1061 // processes they are dependent on, marking these processes as 1062 // still being active and determining if anything has changed. 1063 final int NAP = mServiceProcessesByPid.size(); 1064 for (int i=0; i<NAP; i++) { 1065 ProcessItem proc = mServiceProcessesByPid.valueAt(i); 1066 if (proc.mCurSeq == mSequence) { 1067 changed |= proc.buildDependencyChain(context, pm, mSequence); 1068 } 1069 } 1070 1071 // Look for services and their primary processes that no longer exist... 1072 ArrayList<Integer> uidToDelete = null; 1073 for (int i=0; i<mServiceProcessesByName.size(); i++) { 1074 HashMap<String, ProcessItem> procs = mServiceProcessesByName.valueAt(i); 1075 Iterator<ProcessItem> pit = procs.values().iterator(); 1076 while (pit.hasNext()) { 1077 ProcessItem pi = pit.next(); 1078 if (pi.mCurSeq == mSequence) { 1079 pi.ensureLabel(pm); 1080 if (pi.mPid == 0) { 1081 // Sanity: a non-process can't be dependent on 1082 // anything. 1083 pi.mDependentProcesses.clear(); 1084 } 1085 } else { 1086 changed = true; 1087 pit.remove(); 1088 if (procs.size() == 0) { 1089 if (uidToDelete == null) { 1090 uidToDelete = new ArrayList<Integer>(); 1091 } 1092 uidToDelete.add(mServiceProcessesByName.keyAt(i)); 1093 } 1094 if (pi.mPid != 0) { 1095 mServiceProcessesByPid.remove(pi.mPid); 1096 } 1097 continue; 1098 } 1099 Iterator<ServiceItem> sit = pi.mServices.values().iterator(); 1100 while (sit.hasNext()) { 1101 ServiceItem si = sit.next(); 1102 if (si.mCurSeq != mSequence) { 1103 changed = true; 1104 sit.remove(); 1105 } 1106 } 1107 } 1108 } 1109 1110 if (uidToDelete != null) { 1111 for (int i = 0; i < uidToDelete.size(); i++) { 1112 int uid = uidToDelete.get(i); 1113 mServiceProcessesByName.remove(uid); 1114 } 1115 } 1116 1117 if (changed) { 1118 // First determine an order for the services. 1119 ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>(); 1120 for (int i=0; i<mServiceProcessesByName.size(); i++) { 1121 for (ProcessItem pi : mServiceProcessesByName.valueAt(i).values()) { 1122 pi.mIsSystem = false; 1123 pi.mIsStarted = true; 1124 pi.mActiveSince = Long.MAX_VALUE; 1125 for (ServiceItem si : pi.mServices.values()) { 1126 if (si.mServiceInfo != null 1127 && (si.mServiceInfo.applicationInfo.flags 1128 & ApplicationInfo.FLAG_SYSTEM) != 0) { 1129 pi.mIsSystem = true; 1130 } 1131 if (si.mRunningService != null 1132 && si.mRunningService.clientLabel != 0) { 1133 pi.mIsStarted = false; 1134 if (pi.mActiveSince > si.mRunningService.activeSince) { 1135 pi.mActiveSince = si.mRunningService.activeSince; 1136 } 1137 } 1138 } 1139 sortedProcesses.add(pi); 1140 } 1141 } 1142 1143 Collections.sort(sortedProcesses, mServiceProcessComparator); 1144 1145 ArrayList<BaseItem> newItems = new ArrayList<BaseItem>(); 1146 ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>(); 1147 SparseArray<MergedItem> otherUsers = null; 1148 mProcessItems.clear(); 1149 for (int i=0; i<sortedProcesses.size(); i++) { 1150 ProcessItem pi = sortedProcesses.get(i); 1151 pi.mNeedDivider = false; 1152 1153 int firstProc = mProcessItems.size(); 1154 // First add processes we are dependent on. 1155 pi.addDependentProcesses(newItems, mProcessItems); 1156 // And add the process itself. 1157 newItems.add(pi); 1158 if (pi.mPid > 0) { 1159 mProcessItems.add(pi); 1160 } 1161 1162 // Now add the services running in it. 1163 MergedItem mergedItem = null; 1164 boolean haveAllMerged = false; 1165 boolean needDivider = false; 1166 for (ServiceItem si : pi.mServices.values()) { 1167 si.mNeedDivider = needDivider; 1168 needDivider = true; 1169 newItems.add(si); 1170 if (si.mMergedItem != null) { 1171 if (mergedItem != null && mergedItem != si.mMergedItem) { 1172 haveAllMerged = false; 1173 } 1174 mergedItem = si.mMergedItem; 1175 } else { 1176 haveAllMerged = false; 1177 } 1178 } 1179 1180 if (!haveAllMerged || mergedItem == null 1181 || mergedItem.mServices.size() != pi.mServices.size()) { 1182 // Whoops, we need to build a new MergedItem! 1183 mergedItem = new MergedItem(pi.mUserId); 1184 for (ServiceItem si : pi.mServices.values()) { 1185 mergedItem.mServices.add(si); 1186 si.mMergedItem = mergedItem; 1187 } 1188 mergedItem.mProcess = pi; 1189 mergedItem.mOtherProcesses.clear(); 1190 for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) { 1191 mergedItem.mOtherProcesses.add(mProcessItems.get(mpi)); 1192 } 1193 } 1194 1195 mergedItem.update(context, false); 1196 if (mergedItem.mUserId != mMyUserId) { 1197 addOtherUserItem(context, newMergedItems, mOtherUserMergedItems, mergedItem); 1198 } else { 1199 newMergedItems.add(mergedItem); 1200 } 1201 } 1202 1203 // Finally, interesting processes need to be shown and will 1204 // go at the top. 1205 NHP = mInterestingProcesses.size(); 1206 for (int i=0; i<NHP; i++) { 1207 ProcessItem proc = mInterestingProcesses.get(i); 1208 if (proc.mClient == null && proc.mServices.size() <= 0) { 1209 if (proc.mMergedItem == null) { 1210 proc.mMergedItem = new MergedItem(proc.mUserId); 1211 proc.mMergedItem.mProcess = proc; 1212 } 1213 proc.mMergedItem.update(context, false); 1214 if (proc.mMergedItem.mUserId != mMyUserId) { 1215 addOtherUserItem(context, newMergedItems, mOtherUserMergedItems, 1216 proc.mMergedItem); 1217 } else { 1218 newMergedItems.add(0, proc.mMergedItem); 1219 } 1220 mProcessItems.add(proc); 1221 } 1222 } 1223 1224 // Finally finally, user aggregated merged items need to be 1225 // updated now that they have all of their children. 1226 final int NU = mOtherUserMergedItems.size(); 1227 for (int i=0; i<NU; i++) { 1228 MergedItem user = mOtherUserMergedItems.valueAt(i); 1229 if (user.mCurSeq == mSequence) { 1230 user.update(context, false); 1231 } 1232 } 1233 1234 synchronized (mLock) { 1235 mItems = newItems; 1236 mMergedItems = newMergedItems; 1237 } 1238 } 1239 1240 // Count number of interesting other (non-active) processes, and 1241 // build a list of all processes we will retrieve memory for. 1242 mAllProcessItems.clear(); 1243 mAllProcessItems.addAll(mProcessItems); 1244 int numBackgroundProcesses = 0; 1245 int numForegroundProcesses = 0; 1246 int numServiceProcesses = 0; 1247 NRP = mRunningProcesses.size(); 1248 for (int i=0; i<NRP; i++) { 1249 ProcessItem proc = mRunningProcesses.valueAt(i); 1250 if (proc.mCurSeq != mSequence) { 1251 // We didn't hit this process as a dependency on one 1252 // of our active ones, so add it up if needed. 1253 if (proc.mRunningProcessInfo.importance >= 1254 ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { 1255 numBackgroundProcesses++; 1256 mAllProcessItems.add(proc); 1257 } else if (proc.mRunningProcessInfo.importance <= 1258 ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { 1259 numForegroundProcesses++; 1260 mAllProcessItems.add(proc); 1261 } else { 1262 Log.i("RunningState", "Unknown non-service process: " 1263 + proc.mProcessName + " #" + proc.mPid); 1264 } 1265 } else { 1266 numServiceProcesses++; 1267 } 1268 } 1269 1270 long backgroundProcessMemory = 0; 1271 long foregroundProcessMemory = 0; 1272 long serviceProcessMemory = 0; 1273 ArrayList<MergedItem> newBackgroundItems = null; 1274 ArrayList<MergedItem> newUserBackgroundItems = null; 1275 boolean diffUsers = false; 1276 try { 1277 final int numProc = mAllProcessItems.size(); 1278 int[] pids = new int[numProc]; 1279 for (int i=0; i<numProc; i++) { 1280 pids[i] = mAllProcessItems.get(i).mPid; 1281 } 1282 long[] pss = ActivityManagerNative.getDefault() 1283 .getProcessPss(pids); 1284 int bgIndex = 0; 1285 for (int i=0; i<pids.length; i++) { 1286 ProcessItem proc = mAllProcessItems.get(i); 1287 changed |= proc.updateSize(context, pss[i], mSequence); 1288 if (proc.mCurSeq == mSequence) { 1289 serviceProcessMemory += proc.mSize; 1290 } else if (proc.mRunningProcessInfo.importance >= 1291 ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { 1292 backgroundProcessMemory += proc.mSize; 1293 MergedItem mergedItem; 1294 if (newBackgroundItems != null) { 1295 mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId); 1296 proc.mMergedItem.mProcess = proc; 1297 diffUsers |= mergedItem.mUserId != mMyUserId; 1298 newBackgroundItems.add(mergedItem); 1299 } else { 1300 if (bgIndex >= mBackgroundItems.size() 1301 || mBackgroundItems.get(bgIndex).mProcess != proc) { 1302 newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses); 1303 for (int bgi=0; bgi<bgIndex; bgi++) { 1304 mergedItem = mBackgroundItems.get(bgi); 1305 diffUsers |= mergedItem.mUserId != mMyUserId; 1306 newBackgroundItems.add(mergedItem); 1307 } 1308 mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId); 1309 proc.mMergedItem.mProcess = proc; 1310 diffUsers |= mergedItem.mUserId != mMyUserId; 1311 newBackgroundItems.add(mergedItem); 1312 } else { 1313 mergedItem = mBackgroundItems.get(bgIndex); 1314 } 1315 } 1316 mergedItem.update(context, true); 1317 mergedItem.updateSize(context); 1318 bgIndex++; 1319 } else if (proc.mRunningProcessInfo.importance <= 1320 ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { 1321 foregroundProcessMemory += proc.mSize; 1322 } 1323 } 1324 } catch (RemoteException e) { 1325 } 1326 1327 if (newBackgroundItems == null) { 1328 // One or more at the bottom may no longer exist. 1329 if (mBackgroundItems.size() > numBackgroundProcesses) { 1330 newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses); 1331 for (int bgi=0; bgi<numBackgroundProcesses; bgi++) { 1332 MergedItem mergedItem = mBackgroundItems.get(bgi); 1333 diffUsers |= mergedItem.mUserId != mMyUserId; 1334 newBackgroundItems.add(mergedItem); 1335 } 1336 } 1337 } 1338 1339 if (newBackgroundItems != null) { 1340 // The background items have changed; we need to re-build the 1341 // per-user items. 1342 if (!diffUsers) { 1343 // Easy: there are no other users, we can just use the same array. 1344 newUserBackgroundItems = newBackgroundItems; 1345 } else { 1346 // We now need to re-build the per-user list so that background 1347 // items for users are collapsed together. 1348 newUserBackgroundItems = new ArrayList<MergedItem>(); 1349 final int NB = newBackgroundItems.size(); 1350 for (int i=0; i<NB; i++) { 1351 MergedItem mergedItem = newBackgroundItems.get(i); 1352 if (mergedItem.mUserId != mMyUserId) { 1353 addOtherUserItem(context, newUserBackgroundItems, 1354 mOtherUserBackgroundItems, mergedItem); 1355 } else { 1356 newUserBackgroundItems.add(mergedItem); 1357 } 1358 } 1359 // And user aggregated merged items need to be 1360 // updated now that they have all of their children. 1361 final int NU = mOtherUserBackgroundItems.size(); 1362 for (int i=0; i<NU; i++) { 1363 MergedItem user = mOtherUserBackgroundItems.valueAt(i); 1364 if (user.mCurSeq == mSequence) { 1365 user.update(context, true); 1366 user.updateSize(context); 1367 } 1368 } 1369 } 1370 } 1371 1372 for (int i=0; i<mMergedItems.size(); i++) { 1373 mMergedItems.get(i).updateSize(context); 1374 } 1375 1376 synchronized (mLock) { 1377 mNumBackgroundProcesses = numBackgroundProcesses; 1378 mNumForegroundProcesses = numForegroundProcesses; 1379 mNumServiceProcesses = numServiceProcesses; 1380 mBackgroundProcessMemory = backgroundProcessMemory; 1381 mForegroundProcessMemory = foregroundProcessMemory; 1382 mServiceProcessMemory = serviceProcessMemory; 1383 if (newBackgroundItems != null) { 1384 mBackgroundItems = newBackgroundItems; 1385 mUserBackgroundItems = newUserBackgroundItems; 1386 if (mWatchingBackgroundItems) { 1387 changed = true; 1388 } 1389 } 1390 if (!mHaveData) { 1391 mHaveData = true; 1392 mLock.notifyAll(); 1393 } 1394 } 1395 1396 return changed; 1397 } 1398 1399 ArrayList<BaseItem> getCurrentItems() { 1400 synchronized (mLock) { 1401 return mItems; 1402 } 1403 } 1404 1405 void setWatchingBackgroundItems(boolean watching) { 1406 synchronized (mLock) { 1407 mWatchingBackgroundItems = watching; 1408 } 1409 } 1410 1411 ArrayList<MergedItem> getCurrentMergedItems() { 1412 synchronized (mLock) { 1413 return mMergedItems; 1414 } 1415 } 1416 1417 ArrayList<MergedItem> getCurrentBackgroundItems() { 1418 synchronized (mLock) { 1419 return mUserBackgroundItems; 1420 } 1421 } 1422} 1423