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 17 package com.android.settings.applications; 18 19 import com.android.settings.R; 20 import com.android.settings.Utils; 21 22 import android.app.ActivityManager; 23 import android.app.ActivityManagerNative; 24 import android.app.ActivityThread; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageInfo; 29 import android.content.pm.PackageItemInfo; 30 import android.content.pm.PackageManager; 31 import android.content.pm.ServiceInfo; 32 import android.content.pm.UserInfo; 33 import android.content.res.Resources; 34 import android.graphics.drawable.Drawable; 35 import android.graphics.drawable.Drawable.ConstantState; 36 import android.os.Handler; 37 import android.os.HandlerThread; 38 import android.os.Looper; 39 import android.os.Message; 40 import android.os.RemoteException; 41 import android.os.UserHandle; 42 import android.os.UserManager; 43 import android.text.format.Formatter; 44 import android.util.Log; 45 import android.util.SparseArray; 46 47 import java.util.ArrayList; 48 import java.util.Collections; 49 import java.util.Comparator; 50 import java.util.HashMap; 51 import java.util.Iterator; 52 import java.util.List; 53 54 /** 55 * Singleton for retrieving and monitoring the state about all running 56 * applications/processes/services. 57 */ 58 public 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 = Utils.getUserIcon(context, mUm, info); 841 } 842 String name = info != null ? info.name : null; 843 if (name == null && info != null) { 844 name = Integer.toString(info.id); 845 } else if (info == null) { 846 name = context.getString(R.string.unknown); 847 } 848 userItem.mUser.mLabel = context.getResources().getString( 849 R.string.running_process_item_user_label, name); 850 } 851 newMergedItems.add(userItem); 852 } 853 userItem.mChildren.add(newItem); 854 } 855 856 private boolean update(Context context, ActivityManager am) { 857 final PackageManager pm = context.getPackageManager(); 858 859 mSequence++; 860 861 boolean changed = false; 862 863 // Retrieve list of services, filtering out anything that definitely 864 // won't be shown in the UI. 865 List<ActivityManager.RunningServiceInfo> services 866 = am.getRunningServices(MAX_SERVICES); 867 int NS = services != null ? services.size() : 0; 868 for (int i=0; i<NS; i++) { 869 ActivityManager.RunningServiceInfo si = services.get(i); 870 // We are not interested in services that have not been started 871 // and don't have a known client, because 872 // there is nothing the user can do about them. 873 if (!si.started && si.clientLabel == 0) { 874 services.remove(i); 875 i--; 876 NS--; 877 continue; 878 } 879 // We likewise don't care about services running in a 880 // persistent process like the system or phone. 881 if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS) 882 != 0) { 883 services.remove(i); 884 i--; 885 NS--; 886 continue; 887 } 888 } 889 890 // Retrieve list of running processes, organizing them into a sparse 891 // array for easy retrieval. 892 List<ActivityManager.RunningAppProcessInfo> processes 893 = am.getRunningAppProcesses(); 894 final int NP = processes != null ? processes.size() : 0; 895 mTmpAppProcesses.clear(); 896 for (int i=0; i<NP; i++) { 897 ActivityManager.RunningAppProcessInfo pi = processes.get(i); 898 mTmpAppProcesses.put(pi.pid, new AppProcessInfo(pi)); 899 } 900 901 // Initial iteration through running services to collect per-process 902 // info about them. 903 for (int i=0; i<NS; i++) { 904 ActivityManager.RunningServiceInfo si = services.get(i); 905 if (si.restarting == 0 && si.pid > 0) { 906 AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid); 907 if (ainfo != null) { 908 ainfo.hasServices = true; 909 if (si.foreground) { 910 ainfo.hasForegroundServices = true; 911 } 912 } 913 } 914 } 915 916 // Update state we are maintaining about process that are running services. 917 for (int i=0; i<NS; i++) { 918 ActivityManager.RunningServiceInfo si = services.get(i); 919 920 // If this service's process is in use at a higher importance 921 // due to another process bound to one of its services, then we 922 // won't put it in the top-level list of services. Instead we 923 // want it to be included in the set of processes that the other 924 // process needs. 925 if (si.restarting == 0 && si.pid > 0) { 926 AppProcessInfo ainfo = mTmpAppProcesses.get(si.pid); 927 if (ainfo != null && !ainfo.hasForegroundServices) { 928 // This process does not have any foreground services. 929 // If its importance is greater than the service importance 930 // then there is something else more significant that is 931 // keeping it around that it should possibly be included as 932 // a part of instead of being shown by itself. 933 if (ainfo.info.importance 934 < ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE) { 935 // Follow process chain to see if there is something 936 // else that could be shown 937 boolean skip = false; 938 ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid); 939 while (ainfo != null) { 940 if (ainfo.hasServices || isInterestingProcess(ainfo.info)) { 941 skip = true; 942 break; 943 } 944 ainfo = mTmpAppProcesses.get(ainfo.info.importanceReasonPid); 945 } 946 if (skip) { 947 continue; 948 } 949 } 950 } 951 } 952 953 HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid); 954 if (procs == null) { 955 procs = new HashMap<String, ProcessItem>(); 956 mServiceProcessesByName.put(si.uid, procs); 957 } 958 ProcessItem proc = procs.get(si.process); 959 if (proc == null) { 960 changed = true; 961 proc = new ProcessItem(context, si.uid, si.process); 962 procs.put(si.process, proc); 963 } 964 965 if (proc.mCurSeq != mSequence) { 966 int pid = si.restarting == 0 ? si.pid : 0; 967 if (pid != proc.mPid) { 968 changed = true; 969 if (proc.mPid != pid) { 970 if (proc.mPid != 0) { 971 mServiceProcessesByPid.remove(proc.mPid); 972 } 973 if (pid != 0) { 974 mServiceProcessesByPid.put(pid, proc); 975 } 976 proc.mPid = pid; 977 } 978 } 979 proc.mDependentProcesses.clear(); 980 proc.mCurSeq = mSequence; 981 } 982 changed |= proc.updateService(context, si); 983 } 984 985 // Now update the map of other processes that are running (but 986 // don't have services actively running inside them). 987 for (int i=0; i<NP; i++) { 988 ActivityManager.RunningAppProcessInfo pi = processes.get(i); 989 ProcessItem proc = mServiceProcessesByPid.get(pi.pid); 990 if (proc == null) { 991 // This process is not one that is a direct container 992 // of a service, so look for it in the secondary 993 // running list. 994 proc = mRunningProcesses.get(pi.pid); 995 if (proc == null) { 996 changed = true; 997 proc = new ProcessItem(context, pi.uid, pi.processName); 998 proc.mPid = pi.pid; 999 mRunningProcesses.put(pi.pid, proc); 1000 } 1001 proc.mDependentProcesses.clear(); 1002 } 1003 1004 if (isInterestingProcess(pi)) { 1005 if (!mInterestingProcesses.contains(proc)) { 1006 changed = true; 1007 mInterestingProcesses.add(proc); 1008 } 1009 proc.mCurSeq = mSequence; 1010 proc.mInteresting = true; 1011 proc.ensureLabel(pm); 1012 } else { 1013 proc.mInteresting = false; 1014 } 1015 1016 proc.mRunningSeq = mSequence; 1017 proc.mRunningProcessInfo = pi; 1018 } 1019 1020 // Build the chains from client processes to the process they are 1021 // dependent on; also remove any old running processes. 1022 int NRP = mRunningProcesses.size(); 1023 for (int i = 0; i < NRP;) { 1024 ProcessItem proc = mRunningProcesses.valueAt(i); 1025 if (proc.mRunningSeq == mSequence) { 1026 int clientPid = proc.mRunningProcessInfo.importanceReasonPid; 1027 if (clientPid != 0) { 1028 ProcessItem client = mServiceProcessesByPid.get(clientPid); 1029 if (client == null) { 1030 client = mRunningProcesses.get(clientPid); 1031 } 1032 if (client != null) { 1033 client.mDependentProcesses.put(proc.mPid, proc); 1034 } 1035 } else { 1036 // In this pass the process doesn't have a client. 1037 // Clear to make sure that, if it later gets the same one, 1038 // we will detect the change. 1039 proc.mClient = null; 1040 } 1041 i++; 1042 } else { 1043 changed = true; 1044 mRunningProcesses.remove(mRunningProcesses.keyAt(i)); 1045 NRP--; 1046 } 1047 } 1048 1049 // Remove any old interesting processes. 1050 int NHP = mInterestingProcesses.size(); 1051 for (int i=0; i<NHP; i++) { 1052 ProcessItem proc = mInterestingProcesses.get(i); 1053 if (!proc.mInteresting || mRunningProcesses.get(proc.mPid) == null) { 1054 changed = true; 1055 mInterestingProcesses.remove(i); 1056 i--; 1057 NHP--; 1058 } 1059 } 1060 1061 // Follow the tree from all primary service processes to all 1062 // processes they are dependent on, marking these processes as 1063 // still being active and determining if anything has changed. 1064 final int NAP = mServiceProcessesByPid.size(); 1065 for (int i=0; i<NAP; i++) { 1066 ProcessItem proc = mServiceProcessesByPid.valueAt(i); 1067 if (proc.mCurSeq == mSequence) { 1068 changed |= proc.buildDependencyChain(context, pm, mSequence); 1069 } 1070 } 1071 1072 // Look for services and their primary processes that no longer exist... 1073 ArrayList<Integer> uidToDelete = null; 1074 for (int i=0; i<mServiceProcessesByName.size(); i++) { 1075 HashMap<String, ProcessItem> procs = mServiceProcessesByName.valueAt(i); 1076 Iterator<ProcessItem> pit = procs.values().iterator(); 1077 while (pit.hasNext()) { 1078 ProcessItem pi = pit.next(); 1079 if (pi.mCurSeq == mSequence) { 1080 pi.ensureLabel(pm); 1081 if (pi.mPid == 0) { 1082 // Sanity: a non-process can't be dependent on 1083 // anything. 1084 pi.mDependentProcesses.clear(); 1085 } 1086 } else { 1087 changed = true; 1088 pit.remove(); 1089 if (procs.size() == 0) { 1090 if (uidToDelete == null) { 1091 uidToDelete = new ArrayList<Integer>(); 1092 } 1093 uidToDelete.add(mServiceProcessesByName.keyAt(i)); 1094 } 1095 if (pi.mPid != 0) { 1096 mServiceProcessesByPid.remove(pi.mPid); 1097 } 1098 continue; 1099 } 1100 Iterator<ServiceItem> sit = pi.mServices.values().iterator(); 1101 while (sit.hasNext()) { 1102 ServiceItem si = sit.next(); 1103 if (si.mCurSeq != mSequence) { 1104 changed = true; 1105 sit.remove(); 1106 } 1107 } 1108 } 1109 } 1110 1111 if (uidToDelete != null) { 1112 for (int i = 0; i < uidToDelete.size(); i++) { 1113 int uid = uidToDelete.get(i); 1114 mServiceProcessesByName.remove(uid); 1115 } 1116 } 1117 1118 if (changed) { 1119 // First determine an order for the services. 1120 ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>(); 1121 for (int i=0; i<mServiceProcessesByName.size(); i++) { 1122 for (ProcessItem pi : mServiceProcessesByName.valueAt(i).values()) { 1123 pi.mIsSystem = false; 1124 pi.mIsStarted = true; 1125 pi.mActiveSince = Long.MAX_VALUE; 1126 for (ServiceItem si : pi.mServices.values()) { 1127 if (si.mServiceInfo != null 1128 && (si.mServiceInfo.applicationInfo.flags 1129 & ApplicationInfo.FLAG_SYSTEM) != 0) { 1130 pi.mIsSystem = true; 1131 } 1132 if (si.mRunningService != null 1133 && si.mRunningService.clientLabel != 0) { 1134 pi.mIsStarted = false; 1135 if (pi.mActiveSince > si.mRunningService.activeSince) { 1136 pi.mActiveSince = si.mRunningService.activeSince; 1137 } 1138 } 1139 } 1140 sortedProcesses.add(pi); 1141 } 1142 } 1143 1144 Collections.sort(sortedProcesses, mServiceProcessComparator); 1145 1146 ArrayList<BaseItem> newItems = new ArrayList<BaseItem>(); 1147 ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>(); 1148 SparseArray<MergedItem> otherUsers = null; 1149 mProcessItems.clear(); 1150 for (int i=0; i<sortedProcesses.size(); i++) { 1151 ProcessItem pi = sortedProcesses.get(i); 1152 pi.mNeedDivider = false; 1153 1154 int firstProc = mProcessItems.size(); 1155 // First add processes we are dependent on. 1156 pi.addDependentProcesses(newItems, mProcessItems); 1157 // And add the process itself. 1158 newItems.add(pi); 1159 if (pi.mPid > 0) { 1160 mProcessItems.add(pi); 1161 } 1162 1163 // Now add the services running in it. 1164 MergedItem mergedItem = null; 1165 boolean haveAllMerged = false; 1166 boolean needDivider = false; 1167 for (ServiceItem si : pi.mServices.values()) { 1168 si.mNeedDivider = needDivider; 1169 needDivider = true; 1170 newItems.add(si); 1171 if (si.mMergedItem != null) { 1172 if (mergedItem != null && mergedItem != si.mMergedItem) { 1173 haveAllMerged = false; 1174 } 1175 mergedItem = si.mMergedItem; 1176 } else { 1177 haveAllMerged = false; 1178 } 1179 } 1180 1181 if (!haveAllMerged || mergedItem == null 1182 || mergedItem.mServices.size() != pi.mServices.size()) { 1183 // Whoops, we need to build a new MergedItem! 1184 mergedItem = new MergedItem(pi.mUserId); 1185 for (ServiceItem si : pi.mServices.values()) { 1186 mergedItem.mServices.add(si); 1187 si.mMergedItem = mergedItem; 1188 } 1189 mergedItem.mProcess = pi; 1190 mergedItem.mOtherProcesses.clear(); 1191 for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) { 1192 mergedItem.mOtherProcesses.add(mProcessItems.get(mpi)); 1193 } 1194 } 1195 1196 mergedItem.update(context, false); 1197 if (mergedItem.mUserId != mMyUserId) { 1198 addOtherUserItem(context, newMergedItems, mOtherUserMergedItems, mergedItem); 1199 } else { 1200 newMergedItems.add(mergedItem); 1201 } 1202 } 1203 1204 // Finally, interesting processes need to be shown and will 1205 // go at the top. 1206 NHP = mInterestingProcesses.size(); 1207 for (int i=0; i<NHP; i++) { 1208 ProcessItem proc = mInterestingProcesses.get(i); 1209 if (proc.mClient == null && proc.mServices.size() <= 0) { 1210 if (proc.mMergedItem == null) { 1211 proc.mMergedItem = new MergedItem(proc.mUserId); 1212 proc.mMergedItem.mProcess = proc; 1213 } 1214 proc.mMergedItem.update(context, false); 1215 if (proc.mMergedItem.mUserId != mMyUserId) { 1216 addOtherUserItem(context, newMergedItems, mOtherUserMergedItems, 1217 proc.mMergedItem); 1218 } else { 1219 newMergedItems.add(0, proc.mMergedItem); 1220 } 1221 mProcessItems.add(proc); 1222 } 1223 } 1224 1225 // Finally finally, user aggregated merged items need to be 1226 // updated now that they have all of their children. 1227 final int NU = mOtherUserMergedItems.size(); 1228 for (int i=0; i<NU; i++) { 1229 MergedItem user = mOtherUserMergedItems.valueAt(i); 1230 if (user.mCurSeq == mSequence) { 1231 user.update(context, false); 1232 } 1233 } 1234 1235 synchronized (mLock) { 1236 mItems = newItems; 1237 mMergedItems = newMergedItems; 1238 } 1239 } 1240 1241 // Count number of interesting other (non-active) processes, and 1242 // build a list of all processes we will retrieve memory for. 1243 mAllProcessItems.clear(); 1244 mAllProcessItems.addAll(mProcessItems); 1245 int numBackgroundProcesses = 0; 1246 int numForegroundProcesses = 0; 1247 int numServiceProcesses = 0; 1248 NRP = mRunningProcesses.size(); 1249 for (int i=0; i<NRP; i++) { 1250 ProcessItem proc = mRunningProcesses.valueAt(i); 1251 if (proc.mCurSeq != mSequence) { 1252 // We didn't hit this process as a dependency on one 1253 // of our active ones, so add it up if needed. 1254 if (proc.mRunningProcessInfo.importance >= 1255 ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { 1256 numBackgroundProcesses++; 1257 mAllProcessItems.add(proc); 1258 } else if (proc.mRunningProcessInfo.importance <= 1259 ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { 1260 numForegroundProcesses++; 1261 mAllProcessItems.add(proc); 1262 } else { 1263 Log.i("RunningState", "Unknown non-service process: " 1264 + proc.mProcessName + " #" + proc.mPid); 1265 } 1266 } else { 1267 numServiceProcesses++; 1268 } 1269 } 1270 1271 long backgroundProcessMemory = 0; 1272 long foregroundProcessMemory = 0; 1273 long serviceProcessMemory = 0; 1274 ArrayList<MergedItem> newBackgroundItems = null; 1275 ArrayList<MergedItem> newUserBackgroundItems = null; 1276 boolean diffUsers = false; 1277 try { 1278 final int numProc = mAllProcessItems.size(); 1279 int[] pids = new int[numProc]; 1280 for (int i=0; i<numProc; i++) { 1281 pids[i] = mAllProcessItems.get(i).mPid; 1282 } 1283 long[] pss = ActivityManagerNative.getDefault() 1284 .getProcessPss(pids); 1285 int bgIndex = 0; 1286 for (int i=0; i<pids.length; i++) { 1287 ProcessItem proc = mAllProcessItems.get(i); 1288 changed |= proc.updateSize(context, pss[i], mSequence); 1289 if (proc.mCurSeq == mSequence) { 1290 serviceProcessMemory += proc.mSize; 1291 } else if (proc.mRunningProcessInfo.importance >= 1292 ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { 1293 backgroundProcessMemory += proc.mSize; 1294 MergedItem mergedItem; 1295 if (newBackgroundItems != null) { 1296 mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId); 1297 proc.mMergedItem.mProcess = proc; 1298 diffUsers |= mergedItem.mUserId != mMyUserId; 1299 newBackgroundItems.add(mergedItem); 1300 } else { 1301 if (bgIndex >= mBackgroundItems.size() 1302 || mBackgroundItems.get(bgIndex).mProcess != proc) { 1303 newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses); 1304 for (int bgi=0; bgi<bgIndex; bgi++) { 1305 mergedItem = mBackgroundItems.get(bgi); 1306 diffUsers |= mergedItem.mUserId != mMyUserId; 1307 newBackgroundItems.add(mergedItem); 1308 } 1309 mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId); 1310 proc.mMergedItem.mProcess = proc; 1311 diffUsers |= mergedItem.mUserId != mMyUserId; 1312 newBackgroundItems.add(mergedItem); 1313 } else { 1314 mergedItem = mBackgroundItems.get(bgIndex); 1315 } 1316 } 1317 mergedItem.update(context, true); 1318 mergedItem.updateSize(context); 1319 bgIndex++; 1320 } else if (proc.mRunningProcessInfo.importance <= 1321 ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { 1322 foregroundProcessMemory += proc.mSize; 1323 } 1324 } 1325 } catch (RemoteException e) { 1326 } 1327 1328 if (newBackgroundItems == null) { 1329 // One or more at the bottom may no longer exist. 1330 if (mBackgroundItems.size() > numBackgroundProcesses) { 1331 newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses); 1332 for (int bgi=0; bgi<numBackgroundProcesses; bgi++) { 1333 MergedItem mergedItem = mBackgroundItems.get(bgi); 1334 diffUsers |= mergedItem.mUserId != mMyUserId; 1335 newBackgroundItems.add(mergedItem); 1336 } 1337 } 1338 } 1339 1340 if (newBackgroundItems != null) { 1341 // The background items have changed; we need to re-build the 1342 // per-user items. 1343 if (!diffUsers) { 1344 // Easy: there are no other users, we can just use the same array. 1345 newUserBackgroundItems = newBackgroundItems; 1346 } else { 1347 // We now need to re-build the per-user list so that background 1348 // items for users are collapsed together. 1349 newUserBackgroundItems = new ArrayList<MergedItem>(); 1350 final int NB = newBackgroundItems.size(); 1351 for (int i=0; i<NB; i++) { 1352 MergedItem mergedItem = newBackgroundItems.get(i); 1353 if (mergedItem.mUserId != mMyUserId) { 1354 addOtherUserItem(context, newUserBackgroundItems, 1355 mOtherUserBackgroundItems, mergedItem); 1356 } else { 1357 newUserBackgroundItems.add(mergedItem); 1358 } 1359 } 1360 // And user aggregated merged items need to be 1361 // updated now that they have all of their children. 1362 final int NU = mOtherUserBackgroundItems.size(); 1363 for (int i=0; i<NU; i++) { 1364 MergedItem user = mOtherUserBackgroundItems.valueAt(i); 1365 if (user.mCurSeq == mSequence) { 1366 user.update(context, true); 1367 user.updateSize(context); 1368 } 1369 } 1370 } 1371 } 1372 1373 for (int i=0; i<mMergedItems.size(); i++) { 1374 mMergedItems.get(i).updateSize(context); 1375 } 1376 1377 synchronized (mLock) { 1378 mNumBackgroundProcesses = numBackgroundProcesses; 1379 mNumForegroundProcesses = numForegroundProcesses; 1380 mNumServiceProcesses = numServiceProcesses; 1381 mBackgroundProcessMemory = backgroundProcessMemory; 1382 mForegroundProcessMemory = foregroundProcessMemory; 1383 mServiceProcessMemory = serviceProcessMemory; 1384 if (newBackgroundItems != null) { 1385 mBackgroundItems = newBackgroundItems; 1386 mUserBackgroundItems = newUserBackgroundItems; 1387 if (mWatchingBackgroundItems) { 1388 changed = true; 1389 } 1390 } 1391 if (!mHaveData) { 1392 mHaveData = true; 1393 mLock.notifyAll(); 1394 } 1395 } 1396 1397 return changed; 1398 } 1399 1400 ArrayList<BaseItem> getCurrentItems() { 1401 synchronized (mLock) { 1402 return mItems; 1403 } 1404 } 1405 1406 void setWatchingBackgroundItems(boolean watching) { 1407 synchronized (mLock) { 1408 mWatchingBackgroundItems = watching; 1409 } 1410 } 1411 1412 ArrayList<MergedItem> getCurrentMergedItems() { 1413 synchronized (mLock) { 1414 return mMergedItems; 1415 } 1416 } 1417 1418 ArrayList<MergedItem> getCurrentBackgroundItems() { 1419 synchronized (mLock) { 1420 return mUserBackgroundItems; 1421 } 1422 } 1423 } 1424