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