ProcessStatsUi.java revision b94079a704ef9ca144ca94885d655b2befabcf45
1/* 2 * Copyright (C) 2013 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 17package com.android.settings.applications; 18 19import android.app.ActivityManager; 20import android.content.Context; 21import android.content.pm.PackageManager; 22import android.content.res.Resources; 23import android.os.Bundle; 24import android.os.ParcelFileDescriptor; 25import android.os.RemoteException; 26import android.os.ServiceManager; 27import android.os.SystemClock; 28import android.os.UserManager; 29import android.preference.Preference; 30import android.preference.PreferenceFragment; 31import android.preference.PreferenceGroup; 32import android.preference.PreferenceScreen; 33import android.text.format.Formatter; 34import android.util.Log; 35import android.util.SparseArray; 36import android.util.TimeUtils; 37import android.view.Menu; 38import android.view.MenuInflater; 39import android.view.MenuItem; 40import android.view.SubMenu; 41import com.android.internal.app.IProcessStats; 42import com.android.internal.app.ProcessMap; 43import com.android.internal.app.ProcessStats; 44import com.android.internal.util.MemInfoReader; 45import com.android.settings.R; 46import com.android.settings.SettingsActivity; 47import com.android.settings.Utils; 48 49import java.io.IOException; 50import java.io.InputStream; 51import java.util.ArrayList; 52import java.util.Collections; 53import java.util.Comparator; 54import java.util.HashMap; 55 56public class ProcessStatsUi extends PreferenceFragment 57 implements LinearColorBar.OnRegionTappedListener { 58 static final String TAG = "ProcessStatsUi"; 59 static final boolean DEBUG = false; 60 61 private static final String KEY_APP_LIST = "app_list"; 62 private static final String KEY_MEM_STATUS = "mem_status"; 63 64 private static final int NUM_DURATIONS = 4; 65 66 private static final int MENU_STATS_REFRESH = Menu.FIRST; 67 private static final int MENU_DURATION = Menu.FIRST + 1; 68 private static final int MENU_SHOW_SYSTEM = MENU_DURATION + NUM_DURATIONS; 69 private static final int MENU_USE_USS = MENU_SHOW_SYSTEM + 1; 70 private static final int MENU_TYPE_BACKGROUND = MENU_USE_USS + 1; 71 private static final int MENU_TYPE_FOREGROUND = MENU_TYPE_BACKGROUND + 1; 72 private static final int MENU_TYPE_CACHED = MENU_TYPE_FOREGROUND + 1; 73 private static final int MENU_HELP = MENU_TYPE_CACHED + 1; 74 75 static final int MAX_ITEMS_TO_LIST = 60; 76 77 final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() { 78 @Override 79 public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) { 80 if (lhs.mRunWeight < rhs.mRunWeight) { 81 return 1; 82 } else if (lhs.mRunWeight > rhs.mRunWeight) { 83 return -1; 84 } else if (lhs.mRunDuration < rhs.mRunDuration) { 85 return 1; 86 } else if (lhs.mRunDuration > rhs.mRunDuration) { 87 return -1; 88 } 89 return 0; 90 } 91 }; 92 93 final static Comparator<ProcStatsPackageEntry> sPackageEntryCompare 94 = new Comparator<ProcStatsPackageEntry>() { 95 @Override 96 public int compare(ProcStatsPackageEntry lhs, ProcStatsPackageEntry rhs) { 97 if (lhs.mRunWeight < rhs.mRunWeight) { 98 return 1; 99 } else if (lhs.mRunWeight > rhs.mRunWeight) { 100 return -1; 101 } else if (lhs.mRunDuration < rhs.mRunDuration) { 102 return 1; 103 } else if (lhs.mRunDuration > rhs.mRunDuration) { 104 return -1; 105 } 106 return 0; 107 } 108 }; 109 110 private static ProcessStats sStatsXfer; 111 112 IProcessStats mProcessStats; 113 UserManager mUm; 114 ProcessStats mStats; 115 int mMemState; 116 117 private long mDuration; 118 private long mLastDuration; 119 private boolean mShowSystem; 120 private boolean mUseUss; 121 private int mStatsType; 122 private int mMemRegion; 123 124 private MenuItem[] mDurationMenus = new MenuItem[NUM_DURATIONS]; 125 private MenuItem mShowSystemMenu; 126 private MenuItem mUseUssMenu; 127 private MenuItem mTypeBackgroundMenu; 128 private MenuItem mTypeForegroundMenu; 129 private MenuItem mTypeCachedMenu; 130 131 private PreferenceGroup mAppListGroup; 132 private Preference mMemStatusPref; 133 134 double mMaxWeight; 135 long mTotalTime; 136 137 long[] mMemTimes = new long[ProcessStats.ADJ_MEM_FACTOR_COUNT]; 138 double[] mMemStateWeights = new double[ProcessStats.STATE_COUNT]; 139 double mMemCachedWeight; 140 double mMemFreeWeight; 141 double mMemZRamWeight; 142 double mMemKernelWeight; 143 double mMemNativeWeight; 144 double mMemTotalWeight; 145 double mWeightToRam; 146 147 // The actual duration value to use for each duration option. Note these 148 // are lower than the actual duration, since our durations are computed in 149 // batches of 3 hours so we want to allow the time we use to be slightly 150 // smaller than the actual time selected instead of bumping up to 3 hours 151 // beyond it. 152 private static final long DURATION_QUANTUM = ProcessStats.COMMIT_PERIOD; 153 private static long[] sDurations = new long[] { 154 3*60*60*1000 - DURATION_QUANTUM/2, 6*60*60*1000 - DURATION_QUANTUM/2, 155 12*60*60*1000 - DURATION_QUANTUM/2, 24*60*60*1000 - DURATION_QUANTUM/2 156 }; 157 private static int[] sDurationLabels = new int[] { 158 R.string.menu_duration_3h, R.string.menu_duration_6h, 159 R.string.menu_duration_12h, R.string.menu_duration_1d 160 }; 161 162 @Override 163 public void onCreate(Bundle icicle) { 164 super.onCreate(icicle); 165 166 if (icicle != null) { 167 mStats = sStatsXfer; 168 } 169 170 addPreferencesFromResource(R.xml.process_stats_summary); 171 mProcessStats = IProcessStats.Stub.asInterface( 172 ServiceManager.getService(ProcessStats.SERVICE_NAME)); 173 mUm = (UserManager)getActivity().getSystemService(Context.USER_SERVICE); 174 mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST); 175 mMemStatusPref = mAppListGroup.findPreference(KEY_MEM_STATUS); 176 mDuration = icicle != null ? icicle.getLong("duration", sDurations[0]) : sDurations[0]; 177 mShowSystem = icicle != null ? icicle.getBoolean("show_system") : false; 178 mUseUss = icicle != null ? icicle.getBoolean("use_uss") : false; 179 mStatsType = icicle != null ? icicle.getInt("stats_type", MENU_TYPE_BACKGROUND) 180 : MENU_TYPE_BACKGROUND; 181 mMemRegion = icicle != null ? icicle.getInt("mem_region", LinearColorBar.REGION_GREEN) 182 : LinearColorBar.REGION_GREEN; 183 setHasOptionsMenu(true); 184 } 185 186 @Override 187 public void onResume() { 188 super.onResume(); 189 refreshStats(); 190 } 191 192 @Override 193 public void onPause() { 194 super.onPause(); 195 } 196 197 @Override 198 public void onSaveInstanceState(Bundle outState) { 199 super.onSaveInstanceState(outState); 200 outState.putLong("duration", mDuration); 201 outState.putBoolean("show_system", mShowSystem); 202 outState.putBoolean("use_uss", mUseUss); 203 outState.putInt("stats_type", mStatsType); 204 outState.putInt("mem_region", mMemRegion); 205 } 206 207 @Override 208 public void onDestroy() { 209 super.onDestroy(); 210 if (getActivity().isChangingConfigurations()) { 211 sStatsXfer = mStats; 212 } 213 } 214 215 @Override 216 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 217 if (preference instanceof LinearColorPreference) { 218 Bundle args = new Bundle(); 219 args.putLongArray(ProcessStatsMemDetail.EXTRA_MEM_TIMES, mMemTimes); 220 args.putDoubleArray(ProcessStatsMemDetail.EXTRA_MEM_STATE_WEIGHTS, mMemStateWeights); 221 args.putDouble(ProcessStatsMemDetail.EXTRA_MEM_CACHED_WEIGHT, mMemCachedWeight); 222 args.putDouble(ProcessStatsMemDetail.EXTRA_MEM_FREE_WEIGHT, mMemFreeWeight); 223 args.putDouble(ProcessStatsMemDetail.EXTRA_MEM_ZRAM_WEIGHT, mMemZRamWeight); 224 args.putDouble(ProcessStatsMemDetail.EXTRA_MEM_KERNEL_WEIGHT, mMemKernelWeight); 225 args.putDouble(ProcessStatsMemDetail.EXTRA_MEM_NATIVE_WEIGHT, mMemNativeWeight); 226 args.putDouble(ProcessStatsMemDetail.EXTRA_MEM_TOTAL_WEIGHT, mMemTotalWeight); 227 args.putBoolean(ProcessStatsMemDetail.EXTRA_USE_USS, mUseUss); 228 args.putLong(ProcessStatsMemDetail.EXTRA_TOTAL_TIME, mTotalTime); 229 ((SettingsActivity) getActivity()).startPreferencePanel( 230 ProcessStatsMemDetail.class.getName(), args, R.string.mem_details_title, 231 null, null, 0); 232 return true; 233 } 234 235 if (!(preference instanceof ProcessStatsPreference)) { 236 return false; 237 } 238 239 ProcessStatsPreference pgp = (ProcessStatsPreference) preference; 240 Bundle args = new Bundle(); 241 args.putParcelable(ProcessStatsDetail.EXTRA_PACKAGE_ENTRY, pgp.getEntry()); 242 args.putBoolean(ProcessStatsDetail.EXTRA_USE_USS, mUseUss); 243 args.putDouble(ProcessStatsDetail.EXTRA_MAX_WEIGHT, mMaxWeight); 244 args.putDouble(ProcessStatsDetail.EXTRA_WEIGHT_TO_RAM, mWeightToRam); 245 args.putLong(ProcessStatsDetail.EXTRA_TOTAL_TIME, mTotalTime); 246 ((SettingsActivity) getActivity()).startPreferencePanel( 247 ProcessStatsDetail.class.getName(), args, R.string.details_title, null, null, 0); 248 249 return super.onPreferenceTreeClick(preferenceScreen, preference); 250 } 251 252 @Override 253 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 254 MenuItem refresh = menu.add(0, MENU_STATS_REFRESH, 0, R.string.menu_stats_refresh) 255 .setIcon(R.drawable.ic_menu_refresh_holo_dark) 256 .setAlphabeticShortcut('r'); 257 refresh.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | 258 MenuItem.SHOW_AS_ACTION_WITH_TEXT); 259 SubMenu subMenu = menu.addSubMenu(R.string.menu_proc_stats_duration); 260 for (int i=0; i<NUM_DURATIONS; i++) { 261 mDurationMenus[i] = subMenu.add(0, MENU_DURATION+i, 0, sDurationLabels[i]) 262 .setCheckable(true); 263 } 264 mShowSystemMenu = menu.add(0, MENU_SHOW_SYSTEM, 0, R.string.menu_show_system) 265 .setAlphabeticShortcut('s') 266 .setCheckable(true); 267 mUseUssMenu = menu.add(0, MENU_USE_USS, 0, R.string.menu_use_uss) 268 .setAlphabeticShortcut('u') 269 .setCheckable(true); 270 subMenu = menu.addSubMenu(R.string.menu_proc_stats_type); 271 mTypeBackgroundMenu = subMenu.add(0, MENU_TYPE_BACKGROUND, 0, 272 R.string.menu_proc_stats_type_background) 273 .setAlphabeticShortcut('b') 274 .setCheckable(true); 275 mTypeForegroundMenu = subMenu.add(0, MENU_TYPE_FOREGROUND, 0, 276 R.string.menu_proc_stats_type_foreground) 277 .setAlphabeticShortcut('f') 278 .setCheckable(true); 279 mTypeCachedMenu = subMenu.add(0, MENU_TYPE_CACHED, 0, 280 R.string.menu_proc_stats_type_cached) 281 .setCheckable(true); 282 283 updateMenus(); 284 285 /* 286 String helpUrl; 287 if (!TextUtils.isEmpty(helpUrl = getResources().getString(R.string.help_url_battery))) { 288 final MenuItem help = menu.add(0, MENU_HELP, 0, R.string.help_label); 289 HelpUtils.prepareHelpMenuItem(getActivity(), help, helpUrl); 290 } 291 */ 292 } 293 294 void updateMenus() { 295 int closestIndex = 0; 296 long closestDelta = Math.abs(sDurations[0]-mDuration); 297 for (int i=1; i<NUM_DURATIONS; i++) { 298 long delta = Math.abs(sDurations[i]-mDuration); 299 if (delta < closestDelta) { 300 closestDelta = delta; 301 closestIndex = i; 302 } 303 } 304 for (int i=0; i<NUM_DURATIONS; i++) { 305 if (mDurationMenus[i] != null) { 306 mDurationMenus[i].setChecked(i == closestIndex); 307 } 308 } 309 mDuration = sDurations[closestIndex]; 310 if (mShowSystemMenu != null) { 311 mShowSystemMenu.setChecked(mShowSystem); 312 mShowSystemMenu.setEnabled(mStatsType == MENU_TYPE_BACKGROUND); 313 } 314 if (mUseUssMenu != null) { 315 mUseUssMenu.setChecked(mUseUss); 316 } 317 if (mTypeBackgroundMenu != null) { 318 mTypeBackgroundMenu.setChecked(mStatsType == MENU_TYPE_BACKGROUND); 319 } 320 if (mTypeForegroundMenu != null) { 321 mTypeForegroundMenu.setChecked(mStatsType == MENU_TYPE_FOREGROUND); 322 } 323 if (mTypeCachedMenu != null) { 324 mTypeCachedMenu.setChecked(mStatsType == MENU_TYPE_CACHED); 325 } 326 } 327 328 @Override 329 public boolean onOptionsItemSelected(MenuItem item) { 330 final int id = item.getItemId(); 331 switch (id) { 332 case MENU_STATS_REFRESH: 333 mStats = null; 334 refreshStats(); 335 return true; 336 case MENU_SHOW_SYSTEM: 337 mShowSystem = !mShowSystem; 338 refreshStats(); 339 return true; 340 case MENU_USE_USS: 341 mUseUss = !mUseUss; 342 refreshStats(); 343 return true; 344 case MENU_TYPE_BACKGROUND: 345 case MENU_TYPE_FOREGROUND: 346 case MENU_TYPE_CACHED: 347 mStatsType = item.getItemId(); 348 refreshStats(); 349 return true; 350 default: 351 if (id >= MENU_DURATION && id < (MENU_DURATION+NUM_DURATIONS)) { 352 mDuration = sDurations[id-MENU_DURATION]; 353 refreshStats(); 354 } 355 return false; 356 } 357 } 358 359 @Override 360 public void onRegionTapped(int region) { 361 if (mMemRegion != region) { 362 mMemRegion = region; 363 refreshStats(); 364 } 365 } 366 367 private void addNotAvailableMessage() { 368 Preference notAvailable = new Preference(getActivity()); 369 notAvailable.setTitle(R.string.power_usage_not_available); 370 mAppListGroup.addPreference(notAvailable); 371 } 372 373 public static final int[] BACKGROUND_AND_SYSTEM_PROC_STATES = new int[] { 374 ProcessStats.STATE_PERSISTENT, ProcessStats.STATE_IMPORTANT_FOREGROUND, 375 ProcessStats.STATE_IMPORTANT_BACKGROUND, ProcessStats.STATE_BACKUP, 376 ProcessStats.STATE_HEAVY_WEIGHT, ProcessStats.STATE_SERVICE, 377 ProcessStats.STATE_SERVICE_RESTARTING, ProcessStats.STATE_RECEIVER 378 }; 379 380 public static final int[] FOREGROUND_PROC_STATES = new int[] { 381 ProcessStats.STATE_TOP 382 }; 383 384 public static final int[] CACHED_PROC_STATES = new int[] { 385 ProcessStats.STATE_CACHED_ACTIVITY, ProcessStats.STATE_CACHED_ACTIVITY_CLIENT, 386 ProcessStats.STATE_CACHED_EMPTY 387 }; 388 389 public static final int[] RED_MEM_STATES = new int[] { 390 ProcessStats.ADJ_MEM_FACTOR_CRITICAL 391 }; 392 393 public static final int[] YELLOW_MEM_STATES = new int[] { 394 ProcessStats.ADJ_MEM_FACTOR_CRITICAL, ProcessStats.ADJ_MEM_FACTOR_LOW, 395 ProcessStats.ADJ_MEM_FACTOR_MODERATE 396 }; 397 398 private String makeDuration(long time) { 399 StringBuilder sb = new StringBuilder(32); 400 TimeUtils.formatDuration(time, sb); 401 return sb.toString(); 402 } 403 404 private void refreshStats() { 405 updateMenus(); 406 407 if (mStats == null || mLastDuration != mDuration) { 408 load(); 409 } 410 411 int[] stats; 412 int statsLabel; 413 if (mStatsType == MENU_TYPE_FOREGROUND) { 414 stats = FOREGROUND_PROC_STATES; 415 statsLabel = R.string.process_stats_type_foreground; 416 } else if (mStatsType == MENU_TYPE_CACHED) { 417 stats = CACHED_PROC_STATES; 418 statsLabel = R.string.process_stats_type_cached; 419 } else { 420 stats = mShowSystem ? BACKGROUND_AND_SYSTEM_PROC_STATES 421 : ProcessStats.BACKGROUND_PROC_STATES; 422 statsLabel = R.string.process_stats_type_background; 423 } 424 425 mAppListGroup.removeAll(); 426 mAppListGroup.setOrderingAsAdded(false); 427 428 final long elapsedTime = mStats.mTimePeriodEndRealtime-mStats.mTimePeriodStartRealtime; 429 430 long now = SystemClock.uptimeMillis(); 431 432 final PackageManager pm = getActivity().getPackageManager(); 433 434 mTotalTime = ProcessStats.dumpSingleTime(null, null, mStats.mMemFactorDurations, 435 mStats.mMemFactor, mStats.mStartTime, now); 436 if (DEBUG) Log.d(TAG, "Total time of stats: " + makeDuration(mTotalTime)); 437 438 for (int i=0; i<mMemTimes.length; i++) { 439 mMemTimes[i] = 0; 440 } 441 for (int iscreen=0; iscreen<ProcessStats.ADJ_COUNT; iscreen+=ProcessStats.ADJ_SCREEN_MOD) { 442 for (int imem=0; imem<ProcessStats.ADJ_MEM_FACTOR_COUNT; imem++) { 443 int state = imem+iscreen; 444 mMemTimes[imem] += mStats.mMemFactorDurations[state]; 445 } 446 } 447 448 long memTotalTime; 449 int[] memStates; 450 451 LinearColorPreference colors = new LinearColorPreference(getActivity()); 452 colors.setOrder(-1); 453 switch (mMemRegion) { 454 case LinearColorBar.REGION_RED: 455 memTotalTime = mMemTimes[ProcessStats.ADJ_MEM_FACTOR_CRITICAL]; 456 memStates = RED_MEM_STATES; 457 break; 458 case LinearColorBar.REGION_YELLOW: 459 memTotalTime = mMemTimes[ProcessStats.ADJ_MEM_FACTOR_CRITICAL] 460 + mMemTimes[ProcessStats.ADJ_MEM_FACTOR_LOW] 461 + mMemTimes[ProcessStats.ADJ_MEM_FACTOR_MODERATE]; 462 memStates = YELLOW_MEM_STATES; 463 break; 464 default: 465 memTotalTime = mTotalTime; 466 memStates = ProcessStats.ALL_MEM_ADJ; 467 break; 468 } 469 Resources res = getResources(); 470 colors.setColors(res.getColor(R.color.running_processes_apps_ram), 471 res.getColor(R.color.running_processes_apps_ram), 472 res.getColor(R.color.running_processes_free_ram)); 473 474 // Compute memory badness for chart color. 475 /* 476 int[] badColors = com.android.settings.Utils.BADNESS_COLORS; 477 long timeGood = mMemTimes[ProcessStats.ADJ_MEM_FACTOR_NORMAL]; 478 timeGood += (mMemTimes[ProcessStats.ADJ_MEM_FACTOR_MODERATE]*2)/3; 479 timeGood += mMemTimes[ProcessStats.ADJ_MEM_FACTOR_LOW]/3; 480 float memBadness = ((float)timeGood)/mTotalTime; 481 int badnessColor = badColors[1 + Math.round(memBadness*(badColors.length-2))]; 482 colors.setColors(badnessColor, badnessColor, badnessColor); 483 */ 484 485 // We are now going to scale the mMemTimes to match the total elapsed time. 486 // These are in uptime, so they will often be smaller than the elapsed time, 487 // but if the user taps on the bar we want to show the times to them. It is confusing 488 // to see them be smaller than what we told them the measured duration is, so just 489 // scaling them up with make things look reasonable with them none the wiser. 490 for (int i=0; i<ProcessStats.ADJ_MEM_FACTOR_COUNT; i++) { 491 mMemTimes[i] = (long)((mMemTimes[i]*(double)elapsedTime)/mTotalTime); 492 } 493 494 ProcessStats.TotalMemoryUseCollection totalMem = new ProcessStats.TotalMemoryUseCollection( 495 ProcessStats.ALL_SCREEN_ADJ, memStates); 496 mStats.computeTotalMemoryUse(totalMem, now); 497 double freeWeight = totalMem.sysMemFreeWeight + totalMem.sysMemCachedWeight; 498 double usedWeight = totalMem.sysMemKernelWeight + totalMem.sysMemNativeWeight 499 + totalMem.sysMemZRamWeight; 500 double backgroundWeight = 0, persBackgroundWeight = 0; 501 mMemCachedWeight = totalMem.sysMemCachedWeight; 502 mMemFreeWeight = totalMem.sysMemFreeWeight; 503 mMemZRamWeight = totalMem.sysMemZRamWeight; 504 mMemKernelWeight = totalMem.sysMemKernelWeight; 505 mMemNativeWeight = totalMem.sysMemNativeWeight; 506 for (int i=0; i<ProcessStats.STATE_COUNT; i++) { 507 if (i == ProcessStats.STATE_SERVICE_RESTARTING) { 508 // These don't really run. 509 mMemStateWeights[i] = 0; 510 } else { 511 mMemStateWeights[i] = totalMem.processStateWeight[i]; 512 if (i >= ProcessStats.STATE_HOME) { 513 freeWeight += totalMem.processStateWeight[i]; 514 } else { 515 usedWeight += totalMem.processStateWeight[i]; 516 } 517 if (i >= ProcessStats.STATE_IMPORTANT_FOREGROUND) { 518 backgroundWeight += totalMem.processStateWeight[i]; 519 persBackgroundWeight += totalMem.processStateWeight[i]; 520 } 521 if (i == ProcessStats.STATE_PERSISTENT) { 522 persBackgroundWeight += totalMem.processStateWeight[i]; 523 } 524 } 525 } 526 if (DEBUG) { 527 Log.i(TAG, "Used RAM: " + Formatter.formatShortFileSize(getActivity(), 528 (long)((usedWeight * 1024) / memTotalTime))); 529 Log.i(TAG, "Free RAM: " + Formatter.formatShortFileSize(getActivity(), 530 (long)((freeWeight * 1024) / memTotalTime))); 531 Log.i(TAG, "Total RAM: " + Formatter.formatShortFileSize(getActivity(), 532 (long)(((freeWeight+usedWeight) * 1024) / memTotalTime))); 533 Log.i(TAG, "Background+Cached RAM: " + Formatter.formatShortFileSize(getActivity(), 534 (long)((backgroundWeight * 1024) / memTotalTime))); 535 } 536 mMemTotalWeight = freeWeight + usedWeight; 537 538 // For computing the ratio to show, we want to count the baseline cached RAM we 539 // need (at which point we start killing processes) as used RAM, so that if we 540 // reach the point of thrashing due to no RAM for any background processes we 541 // report that as RAM being full. To do this, we need to first convert the weights 542 // back to actual RAM... and since the RAM values we compute here won't exactly 543 // match the real physical RAM, scale those to the actual physical RAM. No problem! 544 double usedRam = (usedWeight*1024)/memTotalTime; 545 double freeRam = (freeWeight*1024)/memTotalTime; 546 double totalRam = usedRam + freeRam; 547 MemInfoReader memReader = new MemInfoReader(); 548 memReader.readMemInfo(); 549 double realTotalRam = memReader.getTotalSize(); 550 double totalScale = realTotalRam / totalRam; 551 mWeightToRam = totalScale / memTotalTime * 1024; 552 mMaxWeight = totalRam / mWeightToRam; 553 double realUsedRam = usedRam * totalScale; 554 double realFreeRam = freeRam * totalScale; 555 if (DEBUG) { 556 Log.i(TAG, "Scaled Used RAM: " + Formatter.formatShortFileSize(getActivity(), 557 (long)realUsedRam)); 558 Log.i(TAG, "Scaled Free RAM: " + Formatter.formatShortFileSize(getActivity(), 559 (long)realFreeRam)); 560 } 561 ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo(); 562 ((ActivityManager)getActivity().getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo( 563 memInfo); 564 long baseCacheRam; 565 if (memInfo.hiddenAppThreshold >= realFreeRam) { 566 realUsedRam = realFreeRam; 567 realFreeRam = 0; 568 baseCacheRam = (long)realFreeRam; 569 } else { 570 realUsedRam += memInfo.hiddenAppThreshold; 571 realFreeRam -= memInfo.hiddenAppThreshold; 572 baseCacheRam = memInfo.hiddenAppThreshold; 573 } 574 if (DEBUG) { 575 Log.i(TAG, "Adj Scaled Used RAM: " + Formatter.formatShortFileSize(getActivity(), 576 (long)realUsedRam)); 577 Log.i(TAG, "Adj Scaled Free RAM: " + Formatter.formatShortFileSize(getActivity(), 578 (long)realFreeRam)); 579 } 580 581 mMemStatusPref.setOrder(-2); 582 mAppListGroup.addPreference(mMemStatusPref); 583 String durationString = Utils.formatElapsedTime(getActivity(), elapsedTime, false); 584 String usedString = Formatter.formatShortFileSize(getActivity(), (long) realUsedRam); 585 String totalString = Formatter.formatShortFileSize(getActivity(), (long)realTotalRam); 586 CharSequence memString; 587 CharSequence[] memStatesStr = getResources().getTextArray(R.array.ram_states); 588 if (mMemState >= 0 && mMemState < memStatesStr.length) { 589 memString = memStatesStr[mMemState]; 590 } else { 591 memString = "?"; 592 } 593 mMemStatusPref.setTitle(getActivity().getString(R.string.process_stats_total_duration, 594 usedString, totalString, durationString)); 595 mMemStatusPref.setSummary(getActivity().getString(R.string.process_stats_memory_status, 596 memString)); 597 float usedRatio = (float)(realUsedRam/(realFreeRam+realUsedRam)); 598 colors.setRatios(usedRatio, 0, 1-usedRatio); 599 600 if (false) { 601 colors.setOnRegionTappedListener(this); 602 switch (mMemRegion) { 603 case LinearColorBar.REGION_RED: 604 colors.setColoredRegions(LinearColorBar.REGION_RED); 605 memTotalTime = mMemTimes[ProcessStats.ADJ_MEM_FACTOR_CRITICAL]; 606 memStates = RED_MEM_STATES; 607 break; 608 case LinearColorBar.REGION_YELLOW: 609 colors.setColoredRegions(LinearColorBar.REGION_RED 610 | LinearColorBar.REGION_YELLOW); 611 memTotalTime = mMemTimes[ProcessStats.ADJ_MEM_FACTOR_CRITICAL] 612 + mMemTimes[ProcessStats.ADJ_MEM_FACTOR_LOW] 613 + mMemTimes[ProcessStats.ADJ_MEM_FACTOR_MODERATE]; 614 memStates = YELLOW_MEM_STATES; 615 break; 616 default: 617 colors.setColoredRegions(LinearColorBar.REGION_ALL); 618 memTotalTime = mTotalTime; 619 memStates = ProcessStats.ALL_MEM_ADJ; 620 break; 621 } 622 colors.setRatios(mMemTimes[ProcessStats.ADJ_MEM_FACTOR_CRITICAL] / (float)mTotalTime, 623 (mMemTimes[ProcessStats.ADJ_MEM_FACTOR_LOW] 624 + mMemTimes[ProcessStats.ADJ_MEM_FACTOR_MODERATE]) / (float)mTotalTime, 625 mMemTimes[ProcessStats.ADJ_MEM_FACTOR_NORMAL] / (float)mTotalTime); 626 } 627 628 mAppListGroup.addPreference(colors); 629 630 ProcessStats.ProcessDataCollection bgTotals = new ProcessStats.ProcessDataCollection( 631 ProcessStats.ALL_SCREEN_ADJ, memStates, stats); 632 ProcessStats.ProcessDataCollection runTotals = new ProcessStats.ProcessDataCollection( 633 ProcessStats.ALL_SCREEN_ADJ, memStates, ProcessStats.NON_CACHED_PROC_STATES); 634 635 final ArrayList<ProcStatsEntry> procEntries = new ArrayList<>(); 636 final ArrayList<ProcStatsPackageEntry> pkgEntries = new ArrayList<>(); 637 638 /* 639 ArrayList<ProcessStats.ProcessState> rawProcs = mStats.collectProcessesLocked( 640 ProcessStats.ALL_SCREEN_ADJ, ProcessStats.ALL_MEM_ADJ, 641 ProcessStats.BACKGROUND_PROC_STATES, now, null); 642 for (int i=0, N=(rawProcs != null ? rawProcs.size() : 0); i<N; i++) { 643 procs.add(new ProcStatsEntry(rawProcs.get(i), bgTotals)); 644 } 645 */ 646 647 if (DEBUG) Log.d(TAG, "-------------------- PULLING PROCESSES"); 648 649 final ProcessMap<ProcStatsEntry> entriesMap = new ProcessMap<ProcStatsEntry>(); 650 for (int ipkg=0, N=mStats.mPackages.getMap().size(); ipkg<N; ipkg++) { 651 final SparseArray<SparseArray<ProcessStats.PackageState>> pkgUids 652 = mStats.mPackages.getMap().valueAt(ipkg); 653 for (int iu=0; iu<pkgUids.size(); iu++) { 654 final SparseArray<ProcessStats.PackageState> vpkgs = pkgUids.valueAt(iu); 655 for (int iv=0; iv<vpkgs.size(); iv++) { 656 final ProcessStats.PackageState st = vpkgs.valueAt(iv); 657 for (int iproc=0; iproc<st.mProcesses.size(); iproc++) { 658 final ProcessStats.ProcessState pkgProc = st.mProcesses.valueAt(iproc); 659 final ProcessStats.ProcessState proc = mStats.mProcesses.get(pkgProc.mName, 660 pkgProc.mUid); 661 if (proc == null) { 662 Log.w(TAG, "No process found for pkg " + st.mPackageName 663 + "/" + st.mUid + " proc name " + pkgProc.mName); 664 continue; 665 } 666 ProcStatsEntry ent = entriesMap.get(proc.mName, proc.mUid); 667 if (ent == null) { 668 ent = new ProcStatsEntry(proc, st.mPackageName, bgTotals, runTotals, 669 mUseUss); 670 if (ent.mRunWeight > 0) { 671 if (DEBUG) Log.d(TAG, "Adding proc " + proc.mName + "/" 672 + proc.mUid + ": time=" + makeDuration(ent.mRunDuration) + " (" 673 + ((((double)ent.mRunDuration) / memTotalTime) * 100) + "%)" 674 + " pss=" + ent.mAvgRunMem); 675 entriesMap.put(proc.mName, proc.mUid, ent); 676 procEntries.add(ent); 677 } 678 } else { 679 ent.addPackage(st.mPackageName); 680 } 681 } 682 } 683 } 684 } 685 686 if (DEBUG) Log.d(TAG, "-------------------- MAPPING SERVICES"); 687 688 // Add in service info. 689 if (mStatsType == MENU_TYPE_BACKGROUND) { 690 for (int ip=0, N=mStats.mPackages.getMap().size(); ip<N; ip++) { 691 SparseArray<SparseArray<ProcessStats.PackageState>> uids 692 = mStats.mPackages.getMap().valueAt(ip); 693 for (int iu=0; iu<uids.size(); iu++) { 694 SparseArray<ProcessStats.PackageState> vpkgs = uids.valueAt(iu); 695 for (int iv=0; iv<vpkgs.size(); iv++) { 696 ProcessStats.PackageState ps = vpkgs.valueAt(iv); 697 for (int is=0, NS=ps.mServices.size(); is<NS; is++) { 698 ProcessStats.ServiceState ss = ps.mServices.valueAt(is); 699 if (ss.mProcessName != null) { 700 ProcStatsEntry ent = entriesMap.get(ss.mProcessName, 701 uids.keyAt(iu)); 702 if (ent != null) { 703 if (DEBUG) Log.d(TAG, "Adding service " + ps.mPackageName 704 + "/" + ss.mName + "/" + uids.keyAt(iu) + " to proc " 705 + ss.mProcessName); 706 ent.addService(ss); 707 } else { 708 Log.w(TAG, "No process " + ss.mProcessName + "/" + uids.keyAt(iu) 709 + " for service " + ss.mName); 710 } 711 } 712 } 713 } 714 } 715 } 716 } 717 718 // Combine processes into packages. 719 HashMap<String, ProcStatsPackageEntry> pkgMap = new HashMap<>(); 720 for (int i=procEntries.size()-1; i>=0; i--) { 721 ProcStatsEntry proc = procEntries.get(i); 722 proc.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 723 ProcStatsPackageEntry pkg = pkgMap.get(proc.mBestTargetPackage); 724 if (pkg == null) { 725 pkg = new ProcStatsPackageEntry(proc.mBestTargetPackage); 726 pkgMap.put(proc.mBestTargetPackage, pkg); 727 pkgEntries.add(pkg); 728 } 729 pkg.addEntry(proc); 730 } 731 732 // Add in fake entry representing the OS itself. 733 ProcStatsPackageEntry osPkg = new ProcStatsPackageEntry("os"); 734 pkgMap.put("os", osPkg); 735 pkgEntries.add(osPkg); 736 ProcStatsEntry osEntry; 737 if (totalMem.sysMemNativeWeight > 0) { 738 osEntry = new ProcStatsEntry("os", 0, 739 getString(R.string.process_stats_os_native), memTotalTime, 740 (long)(totalMem.sysMemNativeWeight/memTotalTime)); 741 osEntry.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 742 osPkg.addEntry(osEntry); 743 } 744 if (totalMem.sysMemKernelWeight > 0) { 745 osEntry = new ProcStatsEntry("os", 0, 746 getString(R.string.process_stats_os_kernel), memTotalTime, 747 (long)(totalMem.sysMemKernelWeight/memTotalTime)); 748 osEntry.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 749 osPkg.addEntry(osEntry); 750 } 751 if (totalMem.sysMemZRamWeight > 0) { 752 osEntry = new ProcStatsEntry("os", 0, 753 getString(R.string.process_stats_os_zram), memTotalTime, 754 (long)(totalMem.sysMemZRamWeight/memTotalTime)); 755 osEntry.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 756 osPkg.addEntry(osEntry); 757 } 758 if (baseCacheRam > 0) { 759 osEntry = new ProcStatsEntry("os", 0, 760 getString(R.string.process_stats_os_cache), memTotalTime, baseCacheRam/1024); 761 osEntry.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 762 osPkg.addEntry(osEntry); 763 } 764 765 for (int i=0, N=pkgEntries.size(); i<N; i++) { 766 ProcStatsPackageEntry pkg = pkgEntries.get(i); 767 pkg.updateMetrics(); 768 } 769 770 Collections.sort(pkgEntries, sPackageEntryCompare); 771 772 // Now collect the per-process information into applications, so that applications 773 // running as multiple processes will have only one entry representing all of them. 774 775 if (DEBUG) Log.d(TAG, "-------------------- BUILDING UI"); 776 777 // Find where we should stop. Because we have two properties we are looking at, 778 // we need to go from the back looking for the first place either holds. 779 int end = pkgEntries.size()-1; 780 while (end >= 0) { 781 ProcStatsPackageEntry pkg = pkgEntries.get(end); 782 final double percentOfWeight = (pkg.mRunWeight / mMaxWeight) * 100; 783 final double percentOfTime = (((double)pkg.mRunDuration) / memTotalTime) * 100; 784 if (percentOfWeight >= .01 || percentOfTime >= 25) { 785 break; 786 } 787 end--; 788 } 789 for (int i=0; i<=end; i++) { 790 ProcStatsPackageEntry pkg = pkgEntries.get(i); 791 final double percentOfWeight = (pkg.mRunWeight / mMaxWeight) * 100; 792 final double percentOfTime = (((double)pkg.mRunDuration) / memTotalTime) * 100; 793 ProcessStatsPreference pref = new ProcessStatsPreference(getActivity()); 794 pref.init(null, pkg); 795 pkg.retrieveUiData(getActivity(), pm); 796 pref.setTitle(pkg.mUiLabel); 797 if (pkg.mUiTargetApp != null) { 798 pref.setIcon(pkg.mUiTargetApp.loadIcon(pm)); 799 } 800 pref.setOrder(i); 801 pref.setPercent(percentOfWeight, percentOfTime, 802 (long)(pkg.mRunWeight * mWeightToRam)); 803 mAppListGroup.addPreference(pref); 804 if (mStatsType == MENU_TYPE_BACKGROUND) { 805 if (DEBUG) { 806 Log.i(TAG, "App " + pkg.mUiLabel + ": weightedRam=" 807 + Formatter.formatShortFileSize(getActivity(), 808 (long)((pkg.mRunWeight * 1024) / memTotalTime)) 809 + ", avgRam=" + Formatter.formatShortFileSize(getActivity(), 810 (pkg.mAvgRunMem *1024))); 811 } 812 813 } 814 if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST+1)) { 815 if (DEBUG) Log.d(TAG, "Done with UI, hit item limit!"); 816 break; 817 } 818 } 819 } 820 821 private void load() { 822 try { 823 mLastDuration = mDuration; 824 mMemState = mProcessStats.getCurrentMemoryState(); 825 ParcelFileDescriptor pfd = mProcessStats.getStatsOverTime(mDuration); 826 mStats = new ProcessStats(false); 827 InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd); 828 mStats.read(is); 829 try { 830 is.close(); 831 } catch (IOException e) { 832 } 833 if (mStats.mReadError != null) { 834 Log.w(TAG, "Failure reading process stats: " + mStats.mReadError); 835 } 836 } catch (RemoteException e) { 837 Log.e(TAG, "RemoteException:", e); 838 } 839 } 840} 841