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