1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.applications; 18 19 import android.app.ActivityManager; 20 import android.content.Context; 21 import android.content.pm.PackageManager; 22 import android.os.ParcelFileDescriptor; 23 import android.os.RemoteException; 24 import android.os.ServiceManager; 25 import android.os.SystemClock; 26 import android.text.format.Formatter; 27 import android.util.ArrayMap; 28 import android.util.Log; 29 import android.util.SparseArray; 30 31 import com.android.internal.app.IProcessStats; 32 import com.android.internal.app.ProcessMap; 33 import com.android.internal.app.ProcessStats; 34 import com.android.internal.app.ProcessStats.ProcessDataCollection; 35 import com.android.internal.app.ProcessStats.TotalMemoryUseCollection; 36 import com.android.internal.util.MemInfoReader; 37 import com.android.settings.R; 38 import com.android.settings.Utils; 39 40 import java.io.IOException; 41 import java.io.InputStream; 42 import java.util.ArrayList; 43 import java.util.Comparator; 44 import java.util.List; 45 46 public class ProcStatsData { 47 48 private static final String TAG = "ProcStatsManager"; 49 50 private static final boolean DEBUG = ProcessStatsUi.DEBUG; 51 52 private static ProcessStats sStatsXfer; 53 54 private PackageManager mPm; 55 private Context mContext; 56 private long memTotalTime; 57 58 private IProcessStats mProcessStats; 59 private ProcessStats mStats; 60 61 private boolean mUseUss; 62 private long mDuration; 63 64 private int[] mMemStates; 65 66 private int[] mStates; 67 68 private MemInfo mMemInfo; 69 70 private ArrayList<ProcStatsPackageEntry> pkgEntries; 71 ProcStatsData(Context context, boolean useXfer)72 public ProcStatsData(Context context, boolean useXfer) { 73 mContext = context; 74 mPm = context.getPackageManager(); 75 mProcessStats = IProcessStats.Stub.asInterface( 76 ServiceManager.getService(ProcessStats.SERVICE_NAME)); 77 mMemStates = ProcessStats.ALL_MEM_ADJ; 78 mStates = ProcessStats.BACKGROUND_PROC_STATES; 79 if (useXfer) { 80 mStats = sStatsXfer; 81 } 82 } 83 setTotalTime(int totalTime)84 public void setTotalTime(int totalTime) { 85 memTotalTime = totalTime; 86 } 87 xferStats()88 public void xferStats() { 89 sStatsXfer = mStats; 90 } 91 setMemStates(int[] memStates)92 public void setMemStates(int[] memStates) { 93 mMemStates = memStates; 94 refreshStats(false); 95 } 96 setStats(int[] stats)97 public void setStats(int[] stats) { 98 this.mStates = stats; 99 refreshStats(false); 100 } 101 getMemState()102 public int getMemState() { 103 int factor = mStats.mMemFactor; 104 if (factor == ProcessStats.ADJ_NOTHING) { 105 return ProcessStats.ADJ_MEM_FACTOR_NORMAL; 106 } 107 if (factor >= ProcessStats.ADJ_SCREEN_ON) { 108 factor -= ProcessStats.ADJ_SCREEN_ON; 109 } 110 return factor; 111 } 112 getMemInfo()113 public MemInfo getMemInfo() { 114 return mMemInfo; 115 } 116 getElapsedTime()117 public long getElapsedTime() { 118 return mStats.mTimePeriodEndRealtime - mStats.mTimePeriodStartRealtime; 119 } 120 setDuration(long duration)121 public void setDuration(long duration) { 122 if (duration != mDuration) { 123 mDuration = duration; 124 refreshStats(true); 125 } 126 } 127 getDuration()128 public long getDuration() { 129 return mDuration; 130 } 131 getEntries()132 public List<ProcStatsPackageEntry> getEntries() { 133 return pkgEntries; 134 } 135 refreshStats(boolean forceLoad)136 public void refreshStats(boolean forceLoad) { 137 if (mStats == null || forceLoad) { 138 load(); 139 } 140 141 pkgEntries = new ArrayList<>(); 142 143 long now = SystemClock.uptimeMillis(); 144 145 memTotalTime = ProcessStats.dumpSingleTime(null, null, mStats.mMemFactorDurations, 146 mStats.mMemFactor, mStats.mStartTime, now); 147 148 ProcessStats.TotalMemoryUseCollection totalMem = new ProcessStats.TotalMemoryUseCollection( 149 ProcessStats.ALL_SCREEN_ADJ, mMemStates); 150 mStats.computeTotalMemoryUse(totalMem, now); 151 152 mMemInfo = new MemInfo(mContext, totalMem, memTotalTime); 153 154 ProcessDataCollection bgTotals = new ProcessDataCollection( 155 ProcessStats.ALL_SCREEN_ADJ, mMemStates, mStates); 156 ProcessDataCollection runTotals = new ProcessDataCollection( 157 ProcessStats.ALL_SCREEN_ADJ, mMemStates, ProcessStats.NON_CACHED_PROC_STATES); 158 159 createPkgMap(getProcs(bgTotals, runTotals), bgTotals, runTotals); 160 if (totalMem.sysMemZRamWeight > 0) { 161 distributeZRam(totalMem.sysMemZRamWeight); 162 } 163 164 ProcStatsPackageEntry osPkg = createOsEntry(bgTotals, runTotals, totalMem, 165 mMemInfo.baseCacheRam); 166 pkgEntries.add(osPkg); 167 } 168 createPkgMap(ArrayList<ProcStatsEntry> procEntries, ProcessDataCollection bgTotals, ProcessDataCollection runTotals)169 private void createPkgMap(ArrayList<ProcStatsEntry> procEntries, ProcessDataCollection bgTotals, 170 ProcessDataCollection runTotals) { 171 // Combine processes into packages. 172 ArrayMap<String, ProcStatsPackageEntry> pkgMap = new ArrayMap<>(); 173 for (int i = procEntries.size() - 1; i >= 0; i--) { 174 ProcStatsEntry proc = procEntries.get(i); 175 proc.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 176 ProcStatsPackageEntry pkg = pkgMap.get(proc.mBestTargetPackage); 177 if (pkg == null) { 178 pkg = new ProcStatsPackageEntry(proc.mBestTargetPackage, memTotalTime); 179 pkgMap.put(proc.mBestTargetPackage, pkg); 180 pkgEntries.add(pkg); 181 } 182 pkg.addEntry(proc); 183 } 184 } 185 distributeZRam(double zramWeight)186 private void distributeZRam(double zramWeight) { 187 // Distribute kernel's Z-Ram across processes, based on how much they have been running. 188 // The idea is that the memory used by the kernel for this is not really the kernel's 189 // responsibility, but that of whoever got swapped in to it... and we will take how 190 // much a process runs for as a sign of the proportion of Z-Ram it is responsible for. 191 192 long zramMem = (long) (zramWeight / memTotalTime); 193 long totalTime = 0; 194 for (int i = pkgEntries.size() - 1; i >= 0; i--) { 195 ProcStatsPackageEntry entry = pkgEntries.get(i); 196 for (int j = entry.mEntries.size() - 1; j >= 0; j--) { 197 ProcStatsEntry proc = entry.mEntries.get(j); 198 totalTime += proc.mRunDuration; 199 } 200 } 201 for (int i = pkgEntries.size() - 1; i >= 0 && totalTime > 0; i--) { 202 ProcStatsPackageEntry entry = pkgEntries.get(i); 203 long pkgRunTime = 0; 204 long maxRunTime = 0; 205 for (int j = entry.mEntries.size() - 1; j >= 0; j--) { 206 ProcStatsEntry proc = entry.mEntries.get(j); 207 pkgRunTime += proc.mRunDuration; 208 if (proc.mRunDuration > maxRunTime) { 209 maxRunTime = proc.mRunDuration; 210 } 211 } 212 long pkgZRam = (zramMem*pkgRunTime)/totalTime; 213 if (pkgZRam > 0) { 214 zramMem -= pkgZRam; 215 totalTime -= pkgRunTime; 216 ProcStatsEntry procEntry = new ProcStatsEntry(entry.mPackage, 0, 217 mContext.getString(R.string.process_stats_os_zram), maxRunTime, 218 pkgZRam, memTotalTime); 219 procEntry.evaluateTargetPackage(mPm, mStats, null, null, sEntryCompare, mUseUss); 220 entry.addEntry(procEntry); 221 } 222 } 223 } 224 createOsEntry(ProcessDataCollection bgTotals, ProcessDataCollection runTotals, TotalMemoryUseCollection totalMem, long baseCacheRam)225 private ProcStatsPackageEntry createOsEntry(ProcessDataCollection bgTotals, 226 ProcessDataCollection runTotals, TotalMemoryUseCollection totalMem, long baseCacheRam) { 227 // Add in fake entry representing the OS itself. 228 ProcStatsPackageEntry osPkg = new ProcStatsPackageEntry("os", memTotalTime); 229 ProcStatsEntry osEntry; 230 if (totalMem.sysMemNativeWeight > 0) { 231 osEntry = new ProcStatsEntry(Utils.OS_PKG, 0, 232 mContext.getString(R.string.process_stats_os_native), memTotalTime, 233 (long) (totalMem.sysMemNativeWeight / memTotalTime), memTotalTime); 234 osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 235 osPkg.addEntry(osEntry); 236 } 237 if (totalMem.sysMemKernelWeight > 0) { 238 osEntry = new ProcStatsEntry(Utils.OS_PKG, 0, 239 mContext.getString(R.string.process_stats_os_kernel), memTotalTime, 240 (long) (totalMem.sysMemKernelWeight / memTotalTime), memTotalTime); 241 osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 242 osPkg.addEntry(osEntry); 243 } 244 /* Turned off now -- zram is being distributed across running apps. 245 if (totalMem.sysMemZRamWeight > 0) { 246 osEntry = new ProcStatsEntry(Utils.OS_PKG, 0, 247 mContext.getString(R.string.process_stats_os_zram), memTotalTime, 248 (long) (totalMem.sysMemZRamWeight / memTotalTime)); 249 osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 250 osPkg.addEntry(osEntry); 251 } 252 */ 253 if (baseCacheRam > 0) { 254 osEntry = new ProcStatsEntry(Utils.OS_PKG, 0, 255 mContext.getString(R.string.process_stats_os_cache), memTotalTime, 256 baseCacheRam / 1024, memTotalTime); 257 osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 258 osPkg.addEntry(osEntry); 259 } 260 return osPkg; 261 } 262 getProcs(ProcessDataCollection bgTotals, ProcessDataCollection runTotals)263 private ArrayList<ProcStatsEntry> getProcs(ProcessDataCollection bgTotals, 264 ProcessDataCollection runTotals) { 265 final ArrayList<ProcStatsEntry> procEntries = new ArrayList<>(); 266 if (DEBUG) Log.d(TAG, "-------------------- PULLING PROCESSES"); 267 268 final ProcessMap<ProcStatsEntry> entriesMap = new ProcessMap<ProcStatsEntry>(); 269 for (int ipkg = 0, N = mStats.mPackages.getMap().size(); ipkg < N; ipkg++) { 270 final SparseArray<SparseArray<ProcessStats.PackageState>> pkgUids = mStats.mPackages 271 .getMap().valueAt(ipkg); 272 for (int iu = 0; iu < pkgUids.size(); iu++) { 273 final SparseArray<ProcessStats.PackageState> vpkgs = pkgUids.valueAt(iu); 274 for (int iv = 0; iv < vpkgs.size(); iv++) { 275 final ProcessStats.PackageState st = vpkgs.valueAt(iv); 276 for (int iproc = 0; iproc < st.mProcesses.size(); iproc++) { 277 final ProcessStats.ProcessState pkgProc = st.mProcesses.valueAt(iproc); 278 final ProcessStats.ProcessState proc = mStats.mProcesses.get(pkgProc.mName, 279 pkgProc.mUid); 280 if (proc == null) { 281 Log.w(TAG, "No process found for pkg " + st.mPackageName 282 + "/" + st.mUid + " proc name " + pkgProc.mName); 283 continue; 284 } 285 ProcStatsEntry ent = entriesMap.get(proc.mName, proc.mUid); 286 if (ent == null) { 287 ent = new ProcStatsEntry(proc, st.mPackageName, bgTotals, runTotals, 288 mUseUss); 289 if (ent.mRunWeight > 0) { 290 if (DEBUG) Log.d(TAG, "Adding proc " + proc.mName + "/" 291 + proc.mUid + ": time=" 292 + ProcessStatsUi.makeDuration(ent.mRunDuration) + " (" 293 + ((((double) ent.mRunDuration) / memTotalTime) * 100) 294 + "%)" 295 + " pss=" + ent.mAvgRunMem); 296 entriesMap.put(proc.mName, proc.mUid, ent); 297 procEntries.add(ent); 298 } 299 } else { 300 ent.addPackage(st.mPackageName); 301 } 302 } 303 } 304 } 305 } 306 307 if (DEBUG) Log.d(TAG, "-------------------- MAPPING SERVICES"); 308 309 // Add in service info. 310 for (int ip = 0, N = mStats.mPackages.getMap().size(); ip < N; ip++) { 311 SparseArray<SparseArray<ProcessStats.PackageState>> uids = mStats.mPackages.getMap() 312 .valueAt(ip); 313 for (int iu = 0; iu < uids.size(); iu++) { 314 SparseArray<ProcessStats.PackageState> vpkgs = uids.valueAt(iu); 315 for (int iv = 0; iv < vpkgs.size(); iv++) { 316 ProcessStats.PackageState ps = vpkgs.valueAt(iv); 317 for (int is = 0, NS = ps.mServices.size(); is < NS; is++) { 318 ProcessStats.ServiceState ss = ps.mServices.valueAt(is); 319 if (ss.mProcessName != null) { 320 ProcStatsEntry ent = entriesMap.get(ss.mProcessName, 321 uids.keyAt(iu)); 322 if (ent != null) { 323 if (DEBUG) Log.d(TAG, "Adding service " + ps.mPackageName 324 + "/" + ss.mName + "/" + uids.keyAt(iu) + " to proc " 325 + ss.mProcessName); 326 ent.addService(ss); 327 } else { 328 Log.w(TAG, "No process " + ss.mProcessName + "/" + uids.keyAt(iu) 329 + " for service " + ss.mName); 330 } 331 } 332 } 333 } 334 } 335 } 336 337 return procEntries; 338 } 339 load()340 private void load() { 341 try { 342 ParcelFileDescriptor pfd = mProcessStats.getStatsOverTime(mDuration); 343 mStats = new ProcessStats(false); 344 InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd); 345 mStats.read(is); 346 try { 347 is.close(); 348 } catch (IOException e) { 349 } 350 if (mStats.mReadError != null) { 351 Log.w(TAG, "Failure reading process stats: " + mStats.mReadError); 352 } 353 } catch (RemoteException e) { 354 Log.e(TAG, "RemoteException:", e); 355 } 356 } 357 358 public static class MemInfo { 359 double realUsedRam; 360 double realFreeRam; 361 double realTotalRam; 362 long baseCacheRam; 363 364 double[] mMemStateWeights = new double[ProcessStats.STATE_COUNT]; 365 double freeWeight; 366 double usedWeight; 367 double weightToRam; 368 double totalRam; 369 double totalScale; 370 long memTotalTime; 371 MemInfo(Context context, ProcessStats.TotalMemoryUseCollection totalMem, long memTotalTime)372 private MemInfo(Context context, ProcessStats.TotalMemoryUseCollection totalMem, 373 long memTotalTime) { 374 this.memTotalTime = memTotalTime; 375 calculateWeightInfo(context, totalMem, memTotalTime); 376 377 double usedRam = (usedWeight * 1024) / memTotalTime; 378 double freeRam = (freeWeight * 1024) / memTotalTime; 379 totalRam = usedRam + freeRam; 380 totalScale = realTotalRam / totalRam; 381 weightToRam = totalScale / memTotalTime * 1024; 382 383 realUsedRam = usedRam * totalScale; 384 realFreeRam = freeRam * totalScale; 385 if (DEBUG) { 386 Log.i(TAG, "Scaled Used RAM: " + Formatter.formatShortFileSize(context, 387 (long) realUsedRam)); 388 Log.i(TAG, "Scaled Free RAM: " + Formatter.formatShortFileSize(context, 389 (long) realFreeRam)); 390 } 391 if (DEBUG) { 392 Log.i(TAG, "Adj Scaled Used RAM: " + Formatter.formatShortFileSize(context, 393 (long) realUsedRam)); 394 Log.i(TAG, "Adj Scaled Free RAM: " + Formatter.formatShortFileSize(context, 395 (long) realFreeRam)); 396 } 397 398 ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo(); 399 ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo( 400 memInfo); 401 if (memInfo.hiddenAppThreshold >= realFreeRam) { 402 realUsedRam = freeRam; 403 realFreeRam = 0; 404 baseCacheRam = (long) realFreeRam; 405 } else { 406 realUsedRam += memInfo.hiddenAppThreshold; 407 realFreeRam -= memInfo.hiddenAppThreshold; 408 baseCacheRam = memInfo.hiddenAppThreshold; 409 } 410 } 411 calculateWeightInfo(Context context, TotalMemoryUseCollection totalMem, long memTotalTime)412 private void calculateWeightInfo(Context context, TotalMemoryUseCollection totalMem, 413 long memTotalTime) { 414 MemInfoReader memReader = new MemInfoReader(); 415 memReader.readMemInfo(); 416 realTotalRam = memReader.getTotalSize(); 417 freeWeight = totalMem.sysMemFreeWeight + totalMem.sysMemCachedWeight; 418 usedWeight = totalMem.sysMemKernelWeight + totalMem.sysMemNativeWeight 419 + totalMem.sysMemZRamWeight; 420 for (int i = 0; i < ProcessStats.STATE_COUNT; i++) { 421 if (i == ProcessStats.STATE_SERVICE_RESTARTING) { 422 // These don't really run. 423 mMemStateWeights[i] = 0; 424 } else { 425 mMemStateWeights[i] = totalMem.processStateWeight[i]; 426 if (i >= ProcessStats.STATE_HOME) { 427 freeWeight += totalMem.processStateWeight[i]; 428 } else { 429 usedWeight += totalMem.processStateWeight[i]; 430 } 431 } 432 } 433 if (DEBUG) { 434 Log.i(TAG, "Used RAM: " + Formatter.formatShortFileSize(context, 435 (long) ((usedWeight * 1024) / memTotalTime))); 436 Log.i(TAG, "Free RAM: " + Formatter.formatShortFileSize(context, 437 (long) ((freeWeight * 1024) / memTotalTime))); 438 Log.i(TAG, "Total RAM: " + Formatter.formatShortFileSize(context, 439 (long) (((freeWeight + usedWeight) * 1024) / memTotalTime))); 440 } 441 } 442 } 443 444 final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() { 445 @Override 446 public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) { 447 if (lhs.mRunWeight < rhs.mRunWeight) { 448 return 1; 449 } else if (lhs.mRunWeight > rhs.mRunWeight) { 450 return -1; 451 } else if (lhs.mRunDuration < rhs.mRunDuration) { 452 return 1; 453 } else if (lhs.mRunDuration > rhs.mRunDuration) { 454 return -1; 455 } 456 return 0; 457 } 458 }; 459 } 460