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