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