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