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