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 com.android.settings.R; 20 21 import android.app.ActivityManager; 22 import android.app.ActivityManagerNative; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.PackageInfo; 27 import android.content.pm.PackageItemInfo; 28 import android.content.pm.PackageManager; 29 import android.content.pm.ServiceInfo; 30 import android.content.res.Resources; 31 import android.os.Debug; 32 import android.os.Handler; 33 import android.os.HandlerThread; 34 import android.os.Looper; 35 import android.os.Message; 36 import android.os.RemoteException; 37 import android.os.SystemClock; 38 import android.text.format.Formatter; 39 import android.util.Log; 40 import android.util.SparseArray; 41 42 import java.util.ArrayList; 43 import java.util.Collections; 44 import java.util.Comparator; 45 import java.util.HashMap; 46 import java.util.Iterator; 47 import java.util.List; 48 49 /** 50 * Singleton for retrieving and monitoring the state about all running 51 * applications/processes/services. 52 */ 53 public class RunningState { 54 static Object sGlobalLock = new Object(); 55 static RunningState sInstance; 56 57 static final int MSG_UPDATE_CONTENTS = 1; 58 static final int MSG_REFRESH_UI = 2; 59 static final int MSG_UPDATE_TIME = 3; 60 61 static final long TIME_UPDATE_DELAY = 1000; 62 static final long CONTENTS_UPDATE_DELAY = 2000; 63 64 static final int MAX_SERVICES = 100; 65 66 final Context mApplicationContext; 67 final ActivityManager mAm; 68 final PackageManager mPm; 69 70 OnRefreshUiListener mRefreshUiListener; 71 72 // Processes that are hosting a service we are interested in, organized 73 // by uid and name. Note that this mapping does not change even across 74 // service restarts, and during a restart there will still be a process 75 // entry. 76 final SparseArray<HashMap<String, ProcessItem>> mServiceProcessesByName 77 = new SparseArray<HashMap<String, ProcessItem>>(); 78 79 // Processes that are hosting a service we are interested in, organized 80 // by their pid. These disappear and re-appear as services are restarted. 81 final SparseArray<ProcessItem> mServiceProcessesByPid 82 = new SparseArray<ProcessItem>(); 83 84 // Used to sort the interesting processes. 85 final ServiceProcessComparator mServiceProcessComparator 86 = new ServiceProcessComparator(); 87 88 // Additional interesting processes to be shown to the user, even if 89 // there is no service running in them. 90 final ArrayList<ProcessItem> mInterestingProcesses = new ArrayList<ProcessItem>(); 91 92 // All currently running processes, for finding dependencies etc. 93 final SparseArray<ProcessItem> mRunningProcesses 94 = new SparseArray<ProcessItem>(); 95 96 // The processes associated with services, in sorted order. 97 final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>(); 98 99 // All processes, used for retrieving memory information. 100 final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>(); 101 102 int mSequence = 0; 103 104 // ----- following protected by mLock ----- 105 106 // Lock for protecting the state that will be shared between the 107 // background update thread and the UI thread. 108 final Object mLock = new Object(); 109 110 boolean mResumed; 111 boolean mHaveData; 112 boolean mWatchingBackgroundItems; 113 114 ArrayList<BaseItem> mItems = new ArrayList<BaseItem>(); 115 ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>(); 116 ArrayList<MergedItem> mBackgroundItems = new ArrayList<MergedItem>(); 117 118 int mNumBackgroundProcesses; 119 long mBackgroundProcessMemory; 120 int mNumForegroundProcesses; 121 long mForegroundProcessMemory; 122 int mNumServiceProcesses; 123 long mServiceProcessMemory; 124 125 // ----- BACKGROUND MONITORING THREAD ----- 126 127 final HandlerThread mBackgroundThread; 128 final class BackgroundHandler extends Handler { 129 public BackgroundHandler(Looper looper) { 130 super(looper); 131 } 132 133 @Override 134 public void handleMessage(Message msg) { 135 switch (msg.what) { 136 case MSG_UPDATE_CONTENTS: 137 synchronized (mLock) { 138 if (!mResumed) { 139 return; 140 } 141 } 142 Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI); 143 cmd.arg1 = update(mApplicationContext, mAm) ? 1 : 0; 144 mHandler.sendMessage(cmd); 145 removeMessages(MSG_UPDATE_CONTENTS); 146 msg = obtainMessage(MSG_UPDATE_CONTENTS); 147 sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY); 148 break; 149 } 150 } 151 }; 152 153 final BackgroundHandler mBackgroundHandler; 154 155 final Handler mHandler = new Handler() { 156 int mNextUpdate = OnRefreshUiListener.REFRESH_TIME; 157 158 @Override 159 public void handleMessage(Message msg) { 160 switch (msg.what) { 161 case MSG_REFRESH_UI: 162 mNextUpdate = msg.arg1 != 0 163 ? OnRefreshUiListener.REFRESH_STRUCTURE 164 : OnRefreshUiListener.REFRESH_DATA; 165 break; 166 case MSG_UPDATE_TIME: 167 synchronized (mLock) { 168 if (!mResumed) { 169 return; 170 } 171 } 172 removeMessages(MSG_UPDATE_TIME); 173 Message m = obtainMessage(MSG_UPDATE_TIME); 174 sendMessageDelayed(m, TIME_UPDATE_DELAY); 175 176 if (mRefreshUiListener != null) { 177 //Log.i("foo", "Refresh UI: " + mNextUpdate 178 // + " @ " + SystemClock.uptimeMillis()); 179 mRefreshUiListener.onRefreshUi(mNextUpdate); 180 mNextUpdate = OnRefreshUiListener.REFRESH_TIME; 181 } 182 break; 183 } 184 } 185 }; 186 187 // ----- DATA STRUCTURES ----- 188 189 static interface OnRefreshUiListener { 190 public static final int REFRESH_TIME = 0; 191 public static final int REFRESH_DATA = 1; 192 public static final int REFRESH_STRUCTURE = 2; 193 194 public void onRefreshUi(int what); 195 } 196 197 static class BaseItem { 198 final boolean mIsProcess; 199 200 PackageItemInfo mPackageInfo; 201 CharSequence mDisplayLabel; 202 String mLabel; 203 String mDescription; 204 205 int mCurSeq; 206 207 long mActiveSince; 208 long mSize; 209 String mSizeStr; 210 String mCurSizeStr; 211 boolean mNeedDivider; 212 boolean mBackground; 213 214 public BaseItem(boolean isProcess) { 215 mIsProcess = isProcess; 216 } 217 } 218 219 static class ServiceItem extends BaseItem { 220 ActivityManager.RunningServiceInfo mRunningService; 221 ServiceInfo mServiceInfo; 222 boolean mShownAsStarted; 223 224 MergedItem mMergedItem; 225 226 public ServiceItem() { 227 super(false); 228 } 229 } 230 231 static class ProcessItem extends BaseItem { 232 final HashMap<ComponentName, ServiceItem> mServices 233 = new HashMap<ComponentName, ServiceItem>(); 234 final SparseArray<ProcessItem> mDependentProcesses 235 = new SparseArray<ProcessItem>(); 236 237 final int mUid; 238 final String mProcessName; 239 int mPid; 240 241 ProcessItem mClient; 242 int mLastNumDependentProcesses; 243 244 int mRunningSeq; 245 ActivityManager.RunningAppProcessInfo mRunningProcessInfo; 246 247 MergedItem mMergedItem; 248 249 // Purely for sorting. 250 boolean mIsSystem; 251 boolean mIsStarted; 252 long mActiveSince; 253 254 public ProcessItem(Context context, int uid, String processName) { 255 super(true); 256 mDescription = context.getResources().getString( 257 R.string.service_process_name, processName); 258 mUid = uid; 259 mProcessName = processName; 260 } 261 262 void ensureLabel(PackageManager pm) { 263 if (mLabel != null) { 264 return; 265 } 266 267 try { 268 ApplicationInfo ai = pm.getApplicationInfo(mProcessName, 0); 269 if (ai.uid == mUid) { 270 mDisplayLabel = ai.loadLabel(pm); 271 mLabel = mDisplayLabel.toString(); 272 mPackageInfo = ai; 273 return; 274 } 275 } catch (PackageManager.NameNotFoundException e) { 276 } 277 278 // If we couldn't get information about the overall 279 // process, try to find something about the uid. 280 String[] pkgs = pm.getPackagesForUid(mUid); 281 282 // If there is one package with this uid, that is what we want. 283 if (pkgs.length == 1) { 284 try { 285 ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0); 286 mDisplayLabel = ai.loadLabel(pm); 287 mLabel = mDisplayLabel.toString(); 288 mPackageInfo = ai; 289 return; 290 } catch (PackageManager.NameNotFoundException e) { 291 } 292 } 293 294 // If there are multiple, see if one gives us the official name 295 // for this uid. 296 for (String name : pkgs) { 297 try { 298 PackageInfo pi = pm.getPackageInfo(name, 0); 299 if (pi.sharedUserLabel != 0) { 300 CharSequence nm = pm.getText(name, 301 pi.sharedUserLabel, pi.applicationInfo); 302 if (nm != null) { 303 mDisplayLabel = nm; 304 mLabel = nm.toString(); 305 mPackageInfo = pi.applicationInfo; 306 return; 307 } 308 } 309 } catch (PackageManager.NameNotFoundException e) { 310 } 311 } 312 313 // If still don't have anything to display, just use the 314 // service info. 315 if (mServices.size() > 0) { 316 mPackageInfo = mServices.values().iterator().next() 317 .mServiceInfo.applicationInfo; 318 mDisplayLabel = mPackageInfo.loadLabel(pm); 319 mLabel = mDisplayLabel.toString(); 320 return; 321 } 322 323 // Finally... whatever, just pick the first package's name. 324 try { 325 ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0); 326 mDisplayLabel = ai.loadLabel(pm); 327 mLabel = mDisplayLabel.toString(); 328 mPackageInfo = ai; 329 return; 330 } catch (PackageManager.NameNotFoundException e) { 331 } 332 } 333 334 boolean updateService(Context context, 335 ActivityManager.RunningServiceInfo service) { 336 final PackageManager pm = context.getPackageManager(); 337 338 boolean changed = false; 339 ServiceItem si = mServices.get(service.service); 340 if (si == null) { 341 changed = true; 342 si = new ServiceItem(); 343 si.mRunningService = service; 344 try { 345 si.mServiceInfo = pm.getServiceInfo(service.service, 0); 346 } catch (PackageManager.NameNotFoundException e) { 347 } 348 si.mDisplayLabel = makeLabel(pm, 349 si.mRunningService.service.getClassName(), si.mServiceInfo); 350 mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null; 351 si.mPackageInfo = si.mServiceInfo.applicationInfo; 352 mServices.put(service.service, si); 353 } 354 si.mCurSeq = mCurSeq; 355 si.mRunningService = service; 356 long activeSince = service.restarting == 0 ? service.activeSince : -1; 357 if (si.mActiveSince != activeSince) { 358 si.mActiveSince = activeSince; 359 changed = true; 360 } 361 if (service.clientPackage != null && service.clientLabel != 0) { 362 if (si.mShownAsStarted) { 363 si.mShownAsStarted = false; 364 changed = true; 365 } 366 try { 367 Resources clientr = pm.getResourcesForApplication(service.clientPackage); 368 String label = clientr.getString(service.clientLabel); 369 si.mDescription = context.getResources().getString( 370 R.string.service_client_name, label); 371 } catch (PackageManager.NameNotFoundException e) { 372 si.mDescription = null; 373 } 374 } else { 375 if (!si.mShownAsStarted) { 376 si.mShownAsStarted = true; 377 changed = true; 378 } 379 si.mDescription = context.getResources().getString( 380 R.string.service_started_by_app); 381 } 382 383 return changed; 384 } 385 386 boolean updateSize(Context context, Debug.MemoryInfo mem, int curSeq) { 387 mSize = ((long)mem.getTotalPss()) * 1024; 388 if (mCurSeq == curSeq) { 389 String sizeStr = Formatter.formatShortFileSize( 390 context, mSize); 391 if (!sizeStr.equals(mSizeStr)){ 392 mSizeStr = sizeStr; 393 // We update this on the second tick where we update just 394 // the text in the current items, so no need to say we 395 // changed here. 396 return false; 397 } 398 } 399 return false; 400 } 401 402 boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) { 403 final int NP = mDependentProcesses.size(); 404 boolean changed = false; 405 for (int i=0; i<NP; i++) { 406 ProcessItem proc = mDependentProcesses.valueAt(i); 407 if (proc.mClient != this) { 408 changed = true; 409 proc.mClient = this; 410 } 411 proc.mCurSeq = curSeq; 412 proc.ensureLabel(pm); 413 changed |= proc.buildDependencyChain(context, pm, curSeq); 414 } 415 416 if (mLastNumDependentProcesses != mDependentProcesses.size()) { 417 changed = true; 418 mLastNumDependentProcesses = mDependentProcesses.size(); 419 } 420 421 return changed; 422 } 423 424 void addDependentProcesses(ArrayList<BaseItem> dest, 425 ArrayList<ProcessItem> destProc) { 426 final int NP = mDependentProcesses.size(); 427 for (int i=0; i<NP; i++) { 428 ProcessItem proc = mDependentProcesses.valueAt(i); 429 proc.addDependentProcesses(dest, destProc); 430 dest.add(proc); 431 if (proc.mPid > 0) { 432 destProc.add(proc); 433 } 434 } 435 } 436 } 437 438 static class MergedItem extends BaseItem { 439 ProcessItem mProcess; 440 final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>(); 441 final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>(); 442 443 private int mLastNumProcesses = -1, mLastNumServices = -1; 444 445 MergedItem() { 446 super(false); 447 } 448 449 boolean update(Context context, boolean background) { 450 mPackageInfo = mProcess.mPackageInfo; 451 mDisplayLabel = mProcess.mDisplayLabel; 452 mLabel = mProcess.mLabel; 453 mBackground = background; 454 455 if (!mBackground) { 456 int numProcesses = (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size(); 457 int numServices = mServices.size(); 458 if (mLastNumProcesses != numProcesses || mLastNumServices != numServices) { 459 mLastNumProcesses = numProcesses; 460 mLastNumServices = numServices; 461 int resid = R.string.running_processes_item_description_s_s; 462 if (numProcesses != 1) { 463 resid = numServices != 1 464 ? R.string.running_processes_item_description_p_p 465 : R.string.running_processes_item_description_p_s; 466 } else if (numServices != 1) { 467 resid = R.string.running_processes_item_description_s_p; 468 } 469 mDescription = context.getResources().getString(resid, numProcesses, 470 numServices); 471 } 472 } 473 474 mActiveSince = -1; 475 for (int i=0; i<mServices.size(); i++) { 476 ServiceItem si = mServices.get(i); 477 if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) { 478 mActiveSince = si.mActiveSince; 479 } 480 } 481 482 return false; 483 } 484 485 boolean updateSize(Context context) { 486 mSize = mProcess.mSize; 487 for (int i=0; i<mOtherProcesses.size(); i++) { 488 mSize += mOtherProcesses.get(i).mSize; 489 } 490 491 String sizeStr = Formatter.formatShortFileSize( 492 context, mSize); 493 if (!sizeStr.equals(mSizeStr)){ 494 mSizeStr = sizeStr; 495 // We update this on the second tick where we update just 496 // the text in the current items, so no need to say we 497 // changed here. 498 return false; 499 } 500 return false; 501 } 502 } 503 504 static class ServiceProcessComparator implements Comparator<ProcessItem> { 505 public int compare(ProcessItem object1, ProcessItem object2) { 506 if (object1.mIsStarted != object2.mIsStarted) { 507 // Non-started processes go last. 508 return object1.mIsStarted ? -1 : 1; 509 } 510 if (object1.mIsSystem != object2.mIsSystem) { 511 // System processes go below non-system. 512 return object1.mIsSystem ? 1 : -1; 513 } 514 if (object1.mActiveSince != object2.mActiveSince) { 515 // Remaining ones are sorted with the longest running 516 // services last. 517 return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1; 518 } 519 return 0; 520 } 521 } 522 523 static CharSequence makeLabel(PackageManager pm, 524 String className, PackageItemInfo item) { 525 if (item != null && (item.labelRes != 0 526 || item.nonLocalizedLabel != null)) { 527 CharSequence label = item.loadLabel(pm); 528 if (label != null) { 529 return label; 530 } 531 } 532 533 String label = className; 534 int tail = label.lastIndexOf('.'); 535 if (tail >= 0) { 536 label = label.substring(tail+1, label.length()); 537 } 538 return label; 539 } 540 541 static RunningState getInstance(Context context) { 542 synchronized (sGlobalLock) { 543 if (sInstance == null) { 544 sInstance = new RunningState(context); 545 } 546 return sInstance; 547 } 548 } 549 550 private RunningState(Context context) { 551 mApplicationContext = context.getApplicationContext(); 552 mAm = (ActivityManager)mApplicationContext.getSystemService(Context.ACTIVITY_SERVICE); 553 mPm = mApplicationContext.getPackageManager(); 554 mResumed = false; 555 mBackgroundThread = new HandlerThread("RunningState:Background"); 556 mBackgroundThread.start(); 557 mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper()); 558 } 559 560 void resume(OnRefreshUiListener listener) { 561 synchronized (mLock) { 562 mResumed = true; 563 mRefreshUiListener = listener; 564 if (!mBackgroundHandler.hasMessages(MSG_UPDATE_CONTENTS)) { 565 mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS); 566 } 567 mHandler.sendEmptyMessage(MSG_UPDATE_TIME); 568 } 569 } 570 571 void updateNow() { 572 synchronized (mLock) { 573 mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS); 574 mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS); 575 } 576 } 577 578 boolean hasData() { 579 synchronized (mLock) { 580 return mHaveData; 581 } 582 } 583 584 void waitForData() { 585 synchronized (mLock) { 586 while (!mHaveData) { 587 try { 588 mLock.wait(0); 589 } catch (InterruptedException e) { 590 } 591 } 592 } 593 } 594 595 void pause() { 596 synchronized (mLock) { 597 mResumed = false; 598 mRefreshUiListener = null; 599 mHandler.removeMessages(MSG_UPDATE_TIME); 600 } 601 } 602 603 private boolean isInterestingProcess(ActivityManager.RunningAppProcessInfo pi) { 604 if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE) != 0) { 605 return true; 606 } 607 if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT) == 0 608 && pi.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND 609 && pi.importanceReasonCode 610 == ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) { 611 return true; 612 } 613 return false; 614 } 615 616 private boolean update(Context context, ActivityManager am) { 617 final PackageManager pm = context.getPackageManager(); 618 619 mSequence++; 620 621 boolean changed = false; 622 623 List<ActivityManager.RunningServiceInfo> services 624 = am.getRunningServices(MAX_SERVICES); 625 final int NS = services != null ? services.size() : 0; 626 for (int i=0; i<NS; i++) { 627 ActivityManager.RunningServiceInfo si = services.get(i); 628 // We are not interested in services that have not been started 629 // and don't have a known client, because 630 // there is nothing the user can do about them. 631 if (!si.started && si.clientLabel == 0) { 632 continue; 633 } 634 // We likewise don't care about services running in a 635 // persistent process like the system or phone. 636 if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS) 637 != 0) { 638 continue; 639 } 640 641 HashMap<String, ProcessItem> procs = mServiceProcessesByName.get(si.uid); 642 if (procs == null) { 643 procs = new HashMap<String, ProcessItem>(); 644 mServiceProcessesByName.put(si.uid, procs); 645 } 646 ProcessItem proc = procs.get(si.process); 647 if (proc == null) { 648 changed = true; 649 proc = new ProcessItem(context, si.uid, si.process); 650 procs.put(si.process, proc); 651 } 652 653 if (proc.mCurSeq != mSequence) { 654 int pid = si.restarting == 0 ? si.pid : 0; 655 if (pid != proc.mPid) { 656 changed = true; 657 if (proc.mPid != pid) { 658 if (proc.mPid != 0) { 659 mServiceProcessesByPid.remove(proc.mPid); 660 } 661 if (pid != 0) { 662 mServiceProcessesByPid.put(pid, proc); 663 } 664 proc.mPid = pid; 665 } 666 } 667 proc.mDependentProcesses.clear(); 668 proc.mCurSeq = mSequence; 669 } 670 changed |= proc.updateService(context, si); 671 } 672 673 // Now update the map of other processes that are running (but 674 // don't have services actively running inside them). 675 List<ActivityManager.RunningAppProcessInfo> processes 676 = am.getRunningAppProcesses(); 677 final int NP = processes != null ? processes.size() : 0; 678 for (int i=0; i<NP; i++) { 679 ActivityManager.RunningAppProcessInfo pi = processes.get(i); 680 ProcessItem proc = mServiceProcessesByPid.get(pi.pid); 681 if (proc == null) { 682 // This process is not one that is a direct container 683 // of a service, so look for it in the secondary 684 // running list. 685 proc = mRunningProcesses.get(pi.pid); 686 if (proc == null) { 687 changed = true; 688 proc = new ProcessItem(context, pi.uid, pi.processName); 689 proc.mPid = pi.pid; 690 mRunningProcesses.put(pi.pid, proc); 691 } 692 proc.mDependentProcesses.clear(); 693 } 694 695 if (isInterestingProcess(pi)) { 696 if (!mInterestingProcesses.contains(proc)) { 697 changed = true; 698 mInterestingProcesses.add(proc); 699 } 700 proc.mCurSeq = mSequence; 701 proc.ensureLabel(pm); 702 } 703 704 proc.mRunningSeq = mSequence; 705 proc.mRunningProcessInfo = pi; 706 } 707 708 // Build the chains from client processes to the process they are 709 // dependent on; also remove any old running processes. 710 int NRP = mRunningProcesses.size(); 711 for (int i=0; i<NRP; i++) { 712 ProcessItem proc = mRunningProcesses.valueAt(i); 713 if (proc.mRunningSeq == mSequence) { 714 int clientPid = proc.mRunningProcessInfo.importanceReasonPid; 715 if (clientPid != 0) { 716 ProcessItem client = mServiceProcessesByPid.get(clientPid); 717 if (client == null) { 718 client = mRunningProcesses.get(clientPid); 719 } 720 if (client != null) { 721 client.mDependentProcesses.put(proc.mPid, proc); 722 } 723 } else { 724 // In this pass the process doesn't have a client. 725 // Clear to make sure that, if it later gets the same one, 726 // we will detect the change. 727 proc.mClient = null; 728 } 729 } else { 730 changed = true; 731 mRunningProcesses.remove(mRunningProcesses.keyAt(i)); 732 } 733 } 734 735 // Remove any old interesting processes. 736 int NHP = mInterestingProcesses.size(); 737 for (int i=0; i<NHP; i++) { 738 ProcessItem proc = mInterestingProcesses.get(i); 739 if (mRunningProcesses.get(proc.mPid) == null) { 740 changed = true; 741 mInterestingProcesses.remove(i); 742 i--; 743 NHP--; 744 } 745 } 746 747 // Follow the tree from all primary service processes to all 748 // processes they are dependent on, marking these processes as 749 // still being active and determining if anything has changed. 750 final int NAP = mServiceProcessesByPid.size(); 751 for (int i=0; i<NAP; i++) { 752 ProcessItem proc = mServiceProcessesByPid.valueAt(i); 753 if (proc.mCurSeq == mSequence) { 754 changed |= proc.buildDependencyChain(context, pm, mSequence); 755 } 756 } 757 758 // Look for services and their primary processes that no longer exist... 759 for (int i=0; i<mServiceProcessesByName.size(); i++) { 760 HashMap<String, ProcessItem> procs = mServiceProcessesByName.valueAt(i); 761 Iterator<ProcessItem> pit = procs.values().iterator(); 762 while (pit.hasNext()) { 763 ProcessItem pi = pit.next(); 764 if (pi.mCurSeq == mSequence) { 765 pi.ensureLabel(pm); 766 if (pi.mPid == 0) { 767 // Sanity: a non-process can't be dependent on 768 // anything. 769 pi.mDependentProcesses.clear(); 770 } 771 } else { 772 changed = true; 773 pit.remove(); 774 if (procs.size() == 0) { 775 mServiceProcessesByName.remove(mServiceProcessesByName.keyAt(i)); 776 } 777 if (pi.mPid != 0) { 778 mServiceProcessesByPid.remove(pi.mPid); 779 } 780 continue; 781 } 782 Iterator<ServiceItem> sit = pi.mServices.values().iterator(); 783 while (sit.hasNext()) { 784 ServiceItem si = sit.next(); 785 if (si.mCurSeq != mSequence) { 786 changed = true; 787 sit.remove(); 788 } 789 } 790 } 791 } 792 793 if (changed) { 794 // First determine an order for the services. 795 ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>(); 796 for (int i=0; i<mServiceProcessesByName.size(); i++) { 797 for (ProcessItem pi : mServiceProcessesByName.valueAt(i).values()) { 798 pi.mIsSystem = false; 799 pi.mIsStarted = true; 800 pi.mActiveSince = Long.MAX_VALUE; 801 for (ServiceItem si : pi.mServices.values()) { 802 if (si.mServiceInfo != null 803 && (si.mServiceInfo.applicationInfo.flags 804 & ApplicationInfo.FLAG_SYSTEM) != 0) { 805 pi.mIsSystem = true; 806 } 807 if (si.mRunningService != null 808 && si.mRunningService.clientLabel != 0) { 809 pi.mIsStarted = false; 810 if (pi.mActiveSince > si.mRunningService.activeSince) { 811 pi.mActiveSince = si.mRunningService.activeSince; 812 } 813 } 814 } 815 sortedProcesses.add(pi); 816 } 817 } 818 819 Collections.sort(sortedProcesses, mServiceProcessComparator); 820 821 ArrayList<BaseItem> newItems = new ArrayList<BaseItem>(); 822 ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>(); 823 mProcessItems.clear(); 824 for (int i=0; i<sortedProcesses.size(); i++) { 825 ProcessItem pi = sortedProcesses.get(i); 826 pi.mNeedDivider = false; 827 828 int firstProc = mProcessItems.size(); 829 // First add processes we are dependent on. 830 pi.addDependentProcesses(newItems, mProcessItems); 831 // And add the process itself. 832 newItems.add(pi); 833 if (pi.mPid > 0) { 834 mProcessItems.add(pi); 835 } 836 837 // Now add the services running in it. 838 MergedItem mergedItem = null; 839 boolean haveAllMerged = false; 840 boolean needDivider = false; 841 for (ServiceItem si : pi.mServices.values()) { 842 si.mNeedDivider = needDivider; 843 needDivider = true; 844 newItems.add(si); 845 if (si.mMergedItem != null) { 846 if (mergedItem != null && mergedItem != si.mMergedItem) { 847 haveAllMerged = false; 848 } 849 mergedItem = si.mMergedItem; 850 } else { 851 haveAllMerged = false; 852 } 853 } 854 855 if (!haveAllMerged || mergedItem == null 856 || mergedItem.mServices.size() != pi.mServices.size()) { 857 // Whoops, we need to build a new MergedItem! 858 mergedItem = new MergedItem(); 859 for (ServiceItem si : pi.mServices.values()) { 860 mergedItem.mServices.add(si); 861 si.mMergedItem = mergedItem; 862 } 863 mergedItem.mProcess = pi; 864 mergedItem.mOtherProcesses.clear(); 865 for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) { 866 mergedItem.mOtherProcesses.add(mProcessItems.get(mpi)); 867 } 868 } 869 870 mergedItem.update(context, false); 871 newMergedItems.add(mergedItem); 872 } 873 874 // Finally, interesting processes need to be shown and will 875 // go at the top. 876 NHP = mInterestingProcesses.size(); 877 for (int i=0; i<NHP; i++) { 878 ProcessItem proc = mInterestingProcesses.get(i); 879 if (proc.mClient == null && proc.mServices.size() <= 0) { 880 if (proc.mMergedItem == null) { 881 proc.mMergedItem = new MergedItem(); 882 proc.mMergedItem.mProcess = proc; 883 } 884 proc.mMergedItem.update(context, false); 885 newMergedItems.add(0, proc.mMergedItem); 886 mProcessItems.add(proc); 887 } 888 } 889 890 synchronized (mLock) { 891 mItems = newItems; 892 mMergedItems = newMergedItems; 893 } 894 } 895 896 // Count number of interesting other (non-active) processes, and 897 // build a list of all processes we will retrieve memory for. 898 mAllProcessItems.clear(); 899 mAllProcessItems.addAll(mProcessItems); 900 int numBackgroundProcesses = 0; 901 int numForegroundProcesses = 0; 902 int numServiceProcesses = 0; 903 NRP = mRunningProcesses.size(); 904 for (int i=0; i<NRP; i++) { 905 ProcessItem proc = mRunningProcesses.valueAt(i); 906 if (proc.mCurSeq != mSequence) { 907 // We didn't hit this process as a dependency on one 908 // of our active ones, so add it up if needed. 909 if (proc.mRunningProcessInfo.importance >= 910 ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { 911 numBackgroundProcesses++; 912 mAllProcessItems.add(proc); 913 } else if (proc.mRunningProcessInfo.importance <= 914 ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { 915 numForegroundProcesses++; 916 mAllProcessItems.add(proc); 917 } else { 918 Log.i("RunningState", "Unknown non-service process: " 919 + proc.mProcessName + " #" + proc.mPid); 920 } 921 } else { 922 numServiceProcesses++; 923 } 924 } 925 926 long backgroundProcessMemory = 0; 927 long foregroundProcessMemory = 0; 928 long serviceProcessMemory = 0; 929 ArrayList<MergedItem> newBackgroundItems = null; 930 try { 931 final int numProc = mAllProcessItems.size(); 932 int[] pids = new int[numProc]; 933 for (int i=0; i<numProc; i++) { 934 pids[i] = mAllProcessItems.get(i).mPid; 935 } 936 Debug.MemoryInfo[] mem = ActivityManagerNative.getDefault() 937 .getProcessMemoryInfo(pids); 938 int bgIndex = 0; 939 for (int i=0; i<pids.length; i++) { 940 ProcessItem proc = mAllProcessItems.get(i); 941 changed |= proc.updateSize(context, mem[i], mSequence); 942 if (proc.mCurSeq == mSequence) { 943 serviceProcessMemory += proc.mSize; 944 } else if (proc.mRunningProcessInfo.importance >= 945 ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { 946 backgroundProcessMemory += proc.mSize; 947 MergedItem mergedItem; 948 if (newBackgroundItems != null) { 949 mergedItem = proc.mMergedItem = new MergedItem(); 950 proc.mMergedItem.mProcess = proc; 951 newBackgroundItems.add(mergedItem); 952 } else { 953 if (bgIndex >= mBackgroundItems.size() 954 || mBackgroundItems.get(bgIndex).mProcess != proc) { 955 newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses); 956 for (int bgi=0; bgi<bgIndex; bgi++) { 957 newBackgroundItems.add(mBackgroundItems.get(bgi)); 958 } 959 mergedItem = proc.mMergedItem = new MergedItem(); 960 proc.mMergedItem.mProcess = proc; 961 newBackgroundItems.add(mergedItem); 962 } else { 963 mergedItem = mBackgroundItems.get(bgIndex); 964 } 965 } 966 mergedItem.update(context, true); 967 mergedItem.updateSize(context); 968 bgIndex++; 969 } else if (proc.mRunningProcessInfo.importance <= 970 ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { 971 foregroundProcessMemory += proc.mSize; 972 } 973 } 974 } catch (RemoteException e) { 975 } 976 977 if (newBackgroundItems == null) { 978 // One or more at the bottom may no longer exit. 979 if (mBackgroundItems.size() > numBackgroundProcesses) { 980 newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses); 981 for (int bgi=0; bgi<numBackgroundProcesses; bgi++) { 982 newBackgroundItems.add(mBackgroundItems.get(bgi)); 983 } 984 } 985 } 986 987 for (int i=0; i<mMergedItems.size(); i++) { 988 mMergedItems.get(i).updateSize(context); 989 } 990 991 synchronized (mLock) { 992 mNumBackgroundProcesses = numBackgroundProcesses; 993 mNumForegroundProcesses = numForegroundProcesses; 994 mNumServiceProcesses = numServiceProcesses; 995 mBackgroundProcessMemory = backgroundProcessMemory; 996 mForegroundProcessMemory = foregroundProcessMemory; 997 mServiceProcessMemory = serviceProcessMemory; 998 if (newBackgroundItems != null) { 999 mBackgroundItems = newBackgroundItems; 1000 if (mWatchingBackgroundItems) { 1001 changed = true; 1002 } 1003 } 1004 if (!mHaveData) { 1005 mHaveData = true; 1006 mLock.notifyAll(); 1007 } 1008 } 1009 1010 return changed; 1011 } 1012 1013 ArrayList<BaseItem> getCurrentItems() { 1014 synchronized (mLock) { 1015 return mItems; 1016 } 1017 } 1018 1019 void setWatchingBackgroundItems(boolean watching) { 1020 synchronized (mLock) { 1021 mWatchingBackgroundItems = watching; 1022 } 1023 } 1024 1025 ArrayList<MergedItem> getCurrentMergedItems() { 1026 synchronized (mLock) { 1027 return mMergedItems; 1028 } 1029 } 1030 1031 ArrayList<MergedItem> getCurrentBackgroundItems() { 1032 synchronized (mLock) { 1033 return mBackgroundItems; 1034 } 1035 } 1036 } 1037