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