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