blob: 583f6b340d47e169472693c0b0ecf3575f052247 [file] [log] [blame]
Adrian Roos91250682017-02-06 14:48:15 -08001/*
2 * Copyright (C) 2017 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.systemui.util.leak;
18
Dan Sandler646d66b2019-06-07 15:51:27 -040019import static android.service.quicksettings.Tile.STATE_ACTIVE;
20import static android.telephony.ims.feature.ImsFeature.STATE_UNAVAILABLE;
21
Dan Sandler4d90d1e2018-03-23 16:29:06 -040022import static com.android.internal.logging.MetricsLogger.VIEW_UNKNOWN;
Jason Monk196d6392018-12-20 13:25:34 -050023import static com.android.systemui.Dependency.BG_LOOPER_NAME;
Adrian Roos91250682017-02-06 14:48:15 -080024
Dan Sandler646d66b2019-06-07 15:51:27 -040025import android.annotation.Nullable;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040026import android.app.ActivityManager;
27import android.content.Context;
28import android.content.Intent;
29import android.content.res.ColorStateList;
30import android.graphics.Canvas;
31import android.graphics.ColorFilter;
32import android.graphics.Paint;
33import android.graphics.PixelFormat;
34import android.graphics.PorterDuff;
35import android.graphics.Rect;
36import android.graphics.drawable.Drawable;
Adrian Roos91250682017-02-06 14:48:15 -080037import android.os.Build;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040038import android.os.Debug;
Adrian Roos91250682017-02-06 14:48:15 -080039import android.os.Handler;
40import android.os.Looper;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040041import android.os.Message;
42import android.os.Process;
Adrian Roosfb2bb3f2017-02-14 17:19:16 +010043import android.os.SystemProperties;
Jason Monk3b9357f2017-07-14 09:40:54 -040044import android.provider.Settings;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040045import android.text.format.DateUtils;
46import android.util.Log;
47import android.util.LongSparseArray;
Adrian Roos91250682017-02-06 14:48:15 -080048
Dan Sandler646d66b2019-06-07 15:51:27 -040049import com.android.systemui.Dumpable;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040050import com.android.systemui.R;
Adrian Roos91250682017-02-06 14:48:15 -080051import com.android.systemui.SystemUI;
Jason Monk00413ce2018-12-21 11:22:30 -050052import com.android.systemui.SystemUIFactory;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040053import com.android.systemui.plugins.qs.QSTile;
54import com.android.systemui.qs.QSHost;
55import com.android.systemui.qs.tileimpl.QSTileImpl;
56
Dan Sandler646d66b2019-06-07 15:51:27 -040057import java.io.FileDescriptor;
58import java.io.PrintWriter;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040059import java.util.ArrayList;
Adrian Roos91250682017-02-06 14:48:15 -080060
Jason Monk196d6392018-12-20 13:25:34 -050061import javax.inject.Inject;
62import javax.inject.Named;
63import javax.inject.Singleton;
64
65/**
66 */
67@Singleton
Dan Sandler646d66b2019-06-07 15:51:27 -040068public class GarbageMonitor implements Dumpable {
Dan Sandler4d90d1e2018-03-23 16:29:06 -040069 private static final boolean LEAK_REPORTING_ENABLED =
70 Build.IS_DEBUGGABLE
71 && SystemProperties.getBoolean("debug.enable_leak_reporting", false);
72 private static final String FORCE_ENABLE_LEAK_REPORTING = "sysui_force_enable_leak_reporting";
73
74 private static final boolean HEAP_TRACKING_ENABLED = Build.IS_DEBUGGABLE;
Dan Sandler1e5d0122019-06-06 23:35:22 -040075
76 // whether to use ActivityManager.setHeapLimit
77 private static final boolean ENABLE_AM_HEAP_LIMIT = Build.IS_DEBUGGABLE;
78 // heap limit value, in KB (overrides R.integer.watch_heap_limit)
79 private static final String SETTINGS_KEY_AM_HEAP_LIMIT = "systemui_am_heap_limit";
Adrian Roos91250682017-02-06 14:48:15 -080080
81 private static final String TAG = "GarbageMonitor";
82
Dan Sandler4d90d1e2018-03-23 16:29:06 -040083 private static final long GARBAGE_INSPECTION_INTERVAL =
84 15 * DateUtils.MINUTE_IN_MILLIS; // 15 min
85 private static final long HEAP_TRACK_INTERVAL = 1 * DateUtils.MINUTE_IN_MILLIS; // 1 min
Dan Sandler646d66b2019-06-07 15:51:27 -040086 private static final int HEAP_TRACK_HISTORY_LEN = 720; // 12 hours
Dan Sandler4d90d1e2018-03-23 16:29:06 -040087
88 private static final int DO_GARBAGE_INSPECTION = 1000;
89 private static final int DO_HEAP_TRACK = 3000;
90
Adrian Roos91250682017-02-06 14:48:15 -080091 private static final int GARBAGE_ALLOWANCE = 5;
92
Dan Sandler646d66b2019-06-07 15:51:27 -040093 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
94
Adrian Roos91250682017-02-06 14:48:15 -080095 private final Handler mHandler;
96 private final TrackedGarbage mTrackedGarbage;
97 private final LeakReporter mLeakReporter;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040098 private final Context mContext;
99 private final ActivityManager mAm;
100 private MemoryTile mQSTile;
101 private DumpTruck mDumpTruck;
Adrian Roos91250682017-02-06 14:48:15 -0800102
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400103 private final LongSparseArray<ProcessMemInfo> mData = new LongSparseArray<>();
104 private final ArrayList<Long> mPids = new ArrayList<>();
105 private int[] mPidsArray = new int[1];
106
107 private long mHeapLimit;
108
Jason Monk196d6392018-12-20 13:25:34 -0500109 /**
110 */
111 @Inject
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400112 public GarbageMonitor(
113 Context context,
Jason Monk196d6392018-12-20 13:25:34 -0500114 @Named(BG_LOOPER_NAME) Looper bgLooper,
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400115 LeakDetector leakDetector,
Adrian Roos91250682017-02-06 14:48:15 -0800116 LeakReporter leakReporter) {
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400117 mContext = context.getApplicationContext();
118 mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
119
120 mHandler = new BackgroundHeapCheckHandler(bgLooper);
121
Adrian Roos91250682017-02-06 14:48:15 -0800122 mTrackedGarbage = leakDetector.getTrackedGarbage();
123 mLeakReporter = leakReporter;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400124
125 mDumpTruck = new DumpTruck(mContext);
126
127 if (ENABLE_AM_HEAP_LIMIT) {
Dan Sandler1e5d0122019-06-06 23:35:22 -0400128 mHeapLimit = Settings.Global.getInt(context.getContentResolver(),
129 SETTINGS_KEY_AM_HEAP_LIMIT,
130 mContext.getResources().getInteger(R.integer.watch_heap_limit));
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400131 }
Adrian Roos91250682017-02-06 14:48:15 -0800132 }
133
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400134 public void startLeakMonitor() {
Adrian Roos91250682017-02-06 14:48:15 -0800135 if (mTrackedGarbage == null) {
136 return;
137 }
138
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400139 mHandler.sendEmptyMessage(DO_GARBAGE_INSPECTION);
Adrian Roos91250682017-02-06 14:48:15 -0800140 }
141
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400142 public void startHeapTracking() {
143 startTrackingProcess(
144 android.os.Process.myPid(), mContext.getPackageName(), System.currentTimeMillis());
145 mHandler.sendEmptyMessage(DO_HEAP_TRACK);
Adrian Roos91250682017-02-06 14:48:15 -0800146 }
147
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400148 private boolean gcAndCheckGarbage() {
Adrian Roos91250682017-02-06 14:48:15 -0800149 if (mTrackedGarbage.countOldGarbage() > GARBAGE_ALLOWANCE) {
150 Runtime.getRuntime().gc();
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400151 return true;
Adrian Roos91250682017-02-06 14:48:15 -0800152 }
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400153 return false;
Adrian Roos91250682017-02-06 14:48:15 -0800154 }
155
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400156 void reinspectGarbageAfterGc() {
Adrian Roos91250682017-02-06 14:48:15 -0800157 int count = mTrackedGarbage.countOldGarbage();
158 if (count > GARBAGE_ALLOWANCE) {
159 mLeakReporter.dumpLeak(count);
160 }
161 }
162
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400163 public ProcessMemInfo getMemInfo(int pid) {
164 return mData.get(pid);
165 }
166
167 public int[] getTrackedProcesses() {
168 return mPidsArray;
169 }
170
171 public void startTrackingProcess(long pid, String name, long start) {
172 synchronized (mPids) {
173 if (mPids.contains(pid)) return;
174
175 mPids.add(pid);
176 updatePidsArrayL();
177
178 mData.put(pid, new ProcessMemInfo(pid, name, start));
179 }
180 }
181
182 private void updatePidsArrayL() {
183 final int N = mPids.size();
184 mPidsArray = new int[N];
185 StringBuffer sb = new StringBuffer("Now tracking processes: ");
186 for (int i = 0; i < N; i++) {
187 final int p = mPids.get(i).intValue();
188 mPidsArray[i] = p;
189 sb.append(p);
190 sb.append(" ");
191 }
Dan Sandler646d66b2019-06-07 15:51:27 -0400192 if (DEBUG) Log.v(TAG, sb.toString());
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400193 }
194
195 private void update() {
196 synchronized (mPids) {
197 Debug.MemoryInfo[] dinfos = mAm.getProcessMemoryInfo(mPidsArray);
198 for (int i = 0; i < dinfos.length; i++) {
199 Debug.MemoryInfo dinfo = dinfos[i];
200 if (i > mPids.size()) {
Dan Sandler646d66b2019-06-07 15:51:27 -0400201 if (DEBUG) Log.e(TAG, "update: unknown process info received: " + dinfo);
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400202 break;
203 }
204 final long pid = mPids.get(i).intValue();
205 final ProcessMemInfo info = mData.get(pid);
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400206 info.pss[info.head] = info.currentPss = dinfo.getTotalPss();
207 info.uss[info.head] = info.currentUss = dinfo.getTotalPrivateDirty();
Dan Sandler646d66b2019-06-07 15:51:27 -0400208 info.head = (info.head + 1) % info.pss.length;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400209 if (info.currentPss > info.max) info.max = info.currentPss;
210 if (info.currentUss > info.max) info.max = info.currentUss;
211 if (info.currentPss == 0) {
Dan Sandler646d66b2019-06-07 15:51:27 -0400212 if (DEBUG) Log.v(TAG, "update: pid " + pid + " has pss=0, it probably died");
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400213 mData.remove(pid);
214 }
215 }
216 for (int i = mPids.size() - 1; i >= 0; i--) {
217 final long pid = mPids.get(i).intValue();
218 if (mData.get(pid) == null) {
219 mPids.remove(i);
220 updatePidsArrayL();
221 }
222 }
223 }
224 if (mQSTile != null) mQSTile.update();
225 }
226
227 private void setTile(MemoryTile tile) {
228 mQSTile = tile;
229 if (tile != null) tile.update();
230 }
231
232 private static String formatBytes(long b) {
233 String[] SUFFIXES = {"B", "K", "M", "G", "T"};
234 int i;
235 for (i = 0; i < SUFFIXES.length; i++) {
236 if (b < 1024) break;
237 b /= 1024;
238 }
239 return b + SUFFIXES[i];
240 }
241
Dan Sandler646d66b2019-06-07 15:51:27 -0400242 private Intent dumpHprofAndGetShareIntent() {
243 return mDumpTruck.captureHeaps(getTrackedProcesses()).createShareIntent();
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400244 }
245
Dan Sandler646d66b2019-06-07 15:51:27 -0400246 @Override
247 public void dump(@Nullable FileDescriptor fd, PrintWriter pw, @Nullable String[] args) {
248 pw.println("GarbageMonitor params:");
249 pw.println(String.format(" mHeapLimit=%d KB", mHeapLimit));
250 pw.println(String.format(" GARBAGE_INSPECTION_INTERVAL=%d (%.1f mins)",
251 GARBAGE_INSPECTION_INTERVAL,
252 (float) GARBAGE_INSPECTION_INTERVAL / DateUtils.MINUTE_IN_MILLIS));
253 final float htiMins = HEAP_TRACK_INTERVAL / DateUtils.MINUTE_IN_MILLIS;
254 pw.println(String.format(" HEAP_TRACK_INTERVAL=%d (%.1f mins)",
255 HEAP_TRACK_INTERVAL,
256 htiMins));
257 pw.println(String.format(" HEAP_TRACK_HISTORY_LEN=%d (%.1f hr total)",
258 HEAP_TRACK_HISTORY_LEN,
259 (float) HEAP_TRACK_HISTORY_LEN * htiMins / 60f));
260
261 pw.println("GarbageMonitor tracked processes:");
262
263 for (long pid : mPids) {
264 final ProcessMemInfo pmi = mData.get(pid);
265 if (pmi != null) {
266 pmi.dump(fd, pw, args);
267 }
268 }
269 }
270
271
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400272 private static class MemoryIconDrawable extends Drawable {
273 long pss, limit;
274 final Drawable baseIcon;
275 final Paint paint = new Paint();
276 final float dp;
277
278 MemoryIconDrawable(Context context) {
279 baseIcon = context.getDrawable(R.drawable.ic_memory).mutate();
280 dp = context.getResources().getDisplayMetrics().density;
Dan Sandler646d66b2019-06-07 15:51:27 -0400281 paint.setColor(QSTileImpl.getColorForState(context, STATE_ACTIVE));
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400282 }
283
284 public void setPss(long pss) {
285 if (pss != this.pss) {
286 this.pss = pss;
287 invalidateSelf();
288 }
289 }
290
291 public void setLimit(long limit) {
292 if (limit != this.limit) {
293 this.limit = limit;
294 invalidateSelf();
295 }
296 }
297
298 @Override
299 public void draw(Canvas canvas) {
300 baseIcon.draw(canvas);
301
302 if (limit > 0 && pss > 0) {
303 float frac = Math.min(1f, (float) pss / limit);
304
305 final Rect bounds = getBounds();
306 canvas.translate(bounds.left + 8 * dp, bounds.top + 5 * dp);
307 //android:pathData="M16.0,5.0l-8.0,0.0l0.0,14.0l8.0,0.0z"
308 canvas.drawRect(0, 14 * dp * (1 - frac), 8 * dp + 1, 14 * dp + 1, paint);
309 }
310 }
311
312 @Override
313 public void setBounds(int left, int top, int right, int bottom) {
314 super.setBounds(left, top, right, bottom);
315 baseIcon.setBounds(left, top, right, bottom);
316 }
317
318 @Override
319 public int getIntrinsicHeight() {
320 return baseIcon.getIntrinsicHeight();
321 }
322
323 @Override
324 public int getIntrinsicWidth() {
325 return baseIcon.getIntrinsicWidth();
326 }
327
328 @Override
329 public void setAlpha(int i) {
330 baseIcon.setAlpha(i);
331 }
332
333 @Override
334 public void setColorFilter(ColorFilter colorFilter) {
335 baseIcon.setColorFilter(colorFilter);
336 paint.setColorFilter(colorFilter);
337 }
338
339 @Override
340 public void setTint(int tint) {
341 super.setTint(tint);
342 baseIcon.setTint(tint);
343 }
344
345 @Override
346 public void setTintList(ColorStateList tint) {
347 super.setTintList(tint);
348 baseIcon.setTintList(tint);
349 }
350
351 @Override
352 public void setTintMode(PorterDuff.Mode tintMode) {
353 super.setTintMode(tintMode);
354 baseIcon.setTintMode(tintMode);
355 }
356
357 @Override
358 public int getOpacity() {
359 return PixelFormat.TRANSLUCENT;
360 }
361 }
362
363 private static class MemoryGraphIcon extends QSTile.Icon {
364 long pss, limit;
365
366 public void setPss(long pss) {
367 this.pss = pss;
368 }
369
370 public void setHeapLimit(long limit) {
371 this.limit = limit;
372 }
373
374 @Override
375 public Drawable getDrawable(Context context) {
376 final MemoryIconDrawable drawable = new MemoryIconDrawable(context);
377 drawable.setPss(pss);
378 drawable.setLimit(limit);
379 return drawable;
380 }
381 }
382
383 public static class MemoryTile extends QSTileImpl<QSTile.State> {
384 public static final String TILE_SPEC = "dbg:mem";
385
Dan Sandler1e5d0122019-06-06 23:35:22 -0400386 // Tell QSTileHost.java to toss this into the default tileset?
387 public static final boolean ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS = true;
388
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400389 private final GarbageMonitor gm;
390 private ProcessMemInfo pmi;
Dan Sandler646d66b2019-06-07 15:51:27 -0400391 private boolean dumpInProgress;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400392
Jason Monk5d577202018-12-26 15:43:06 -0500393 @Inject
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400394 public MemoryTile(QSHost host) {
395 super(host);
Jason Monk00413ce2018-12-21 11:22:30 -0500396 gm = SystemUIFactory.getInstance().getRootComponent().createGarbageMonitor();
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400397 }
398
399 @Override
400 public State newTileState() {
401 return new QSTile.State();
402 }
403
404 @Override
405 public Intent getLongClickIntent() {
406 return new Intent();
407 }
408
409 @Override
410 protected void handleClick() {
Dan Sandler646d66b2019-06-07 15:51:27 -0400411 if (dumpInProgress) return;
412
413 dumpInProgress = true;
414 refreshState();
415 new Thread("HeapDumpThread") {
416 @Override
417 public void run() {
418 try {
419 // wait for animations & state changes
420 Thread.sleep(500);
421 } catch (InterruptedException ignored) { }
422 final Intent shareIntent = gm.dumpHprofAndGetShareIntent();
423 mHandler.post(() -> {
424 dumpInProgress = false;
425 refreshState();
426 getHost().collapsePanels();
427 mContext.startActivity(shareIntent);
428 });
429 }
430 }.start();
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400431 }
432
433 @Override
434 public int getMetricsCategory() {
435 return VIEW_UNKNOWN;
436 }
437
438 @Override
439 public void handleSetListening(boolean listening) {
440 if (gm != null) gm.setTile(listening ? this : null);
441
442 final ActivityManager am = mContext.getSystemService(ActivityManager.class);
443 if (listening && gm.mHeapLimit > 0) {
444 am.setWatchHeapLimit(1024 * gm.mHeapLimit); // why is this in bytes?
445 } else {
446 am.clearWatchHeapLimit();
447 }
448 }
449
450 @Override
451 public CharSequence getTileLabel() {
452 return getState().label;
453 }
454
455 @Override
456 protected void handleUpdateState(State state, Object arg) {
457 pmi = gm.getMemInfo(Process.myPid());
458 final MemoryGraphIcon icon = new MemoryGraphIcon();
459 icon.setHeapLimit(gm.mHeapLimit);
Dan Sandler646d66b2019-06-07 15:51:27 -0400460 state.state = dumpInProgress ? STATE_UNAVAILABLE : STATE_ACTIVE;
461 state.label = dumpInProgress
462 ? "Dumping..."
463 : mContext.getString(R.string.heap_dump_tile_name);
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400464 if (pmi != null) {
465 icon.setPss(pmi.currentPss);
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400466 state.secondaryLabel =
467 String.format(
468 "pss: %s / %s",
469 formatBytes(pmi.currentPss * 1024),
470 formatBytes(gm.mHeapLimit * 1024));
471 } else {
472 icon.setPss(0);
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400473 state.secondaryLabel = null;
474 }
475 state.icon = icon;
476 }
477
478 public void update() {
479 refreshState();
480 }
481
482 public long getPss() {
483 return pmi != null ? pmi.currentPss : 0;
484 }
485
486 public long getHeapLimit() {
487 return gm != null ? gm.mHeapLimit : 0;
488 }
489 }
490
Dan Sandler646d66b2019-06-07 15:51:27 -0400491 /** */
492 public static class ProcessMemInfo implements Dumpable {
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400493 public long pid;
494 public String name;
495 public long startTime;
496 public long currentPss, currentUss;
Dan Sandler646d66b2019-06-07 15:51:27 -0400497 public long[] pss = new long[HEAP_TRACK_HISTORY_LEN];
498 public long[] uss = new long[HEAP_TRACK_HISTORY_LEN];
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400499 public long max = 1;
500 public int head = 0;
501
502 public ProcessMemInfo(long pid, String name, long start) {
503 this.pid = pid;
504 this.name = name;
505 this.startTime = start;
506 }
507
508 public long getUptime() {
509 return System.currentTimeMillis() - startTime;
510 }
Dan Sandler646d66b2019-06-07 15:51:27 -0400511
512 @Override
513 public void dump(@Nullable FileDescriptor fd, PrintWriter pw, @Nullable String[] args) {
514 pw.print("{ \"pid\": ");
515 pw.print(pid);
516 pw.print(", \"name\": \"");
517 pw.print(name.replace('"', '-'));
518 pw.print("\", \"start\": ");
519 pw.print(startTime);
520 pw.print(", \"pss\": [");
521 // write pss values starting from the oldest, which is pss[head], wrapping around to
522 // pss[(head-1) % pss.length]
523 for (int i = 0; i < pss.length; i++) {
524 if (i > 0) pw.print(",");
525 pw.print(pss[(head + i) % pss.length]);
526 }
527 pw.print("], \"uss\": [");
528 for (int i = 0; i < uss.length; i++) {
529 if (i > 0) pw.print(",");
530 pw.print(uss[(head + i) % uss.length]);
531 }
532 pw.println("] }");
533 }
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400534 }
535
Dan Sandler646d66b2019-06-07 15:51:27 -0400536 /** */
537 public static class Service extends SystemUI implements Dumpable {
Adrian Roos91250682017-02-06 14:48:15 -0800538 private GarbageMonitor mGarbageMonitor;
539
540 @Override
541 public void start() {
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400542 boolean forceEnable =
543 Settings.Secure.getInt(
544 mContext.getContentResolver(), FORCE_ENABLE_LEAK_REPORTING, 0)
545 != 0;
Jason Monk00413ce2018-12-21 11:22:30 -0500546 mGarbageMonitor = SystemUIFactory.getInstance().getRootComponent()
547 .createGarbageMonitor();
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400548 if (LEAK_REPORTING_ENABLED || forceEnable) {
549 mGarbageMonitor.startLeakMonitor();
550 }
551 if (HEAP_TRACKING_ENABLED || forceEnable) {
552 mGarbageMonitor.startHeapTracking();
553 }
554 }
Dan Sandler646d66b2019-06-07 15:51:27 -0400555
556 @Override
557 public void dump(@Nullable FileDescriptor fd, PrintWriter pw, @Nullable String[] args) {
558 if (mGarbageMonitor != null) mGarbageMonitor.dump(fd, pw, args);
559 }
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400560 }
561
562 private class BackgroundHeapCheckHandler extends Handler {
563 BackgroundHeapCheckHandler(Looper onLooper) {
564 super(onLooper);
565 if (Looper.getMainLooper().equals(onLooper)) {
566 throw new RuntimeException(
567 "BackgroundHeapCheckHandler may not run on the ui thread");
568 }
569 }
570
571 @Override
572 public void handleMessage(Message m) {
573 switch (m.what) {
574 case DO_GARBAGE_INSPECTION:
575 if (gcAndCheckGarbage()) {
576 postDelayed(GarbageMonitor.this::reinspectGarbageAfterGc, 100);
577 }
578
579 removeMessages(DO_GARBAGE_INSPECTION);
580 sendEmptyMessageDelayed(DO_GARBAGE_INSPECTION, GARBAGE_INSPECTION_INTERVAL);
581 break;
582
583 case DO_HEAP_TRACK:
584 update();
585 removeMessages(DO_HEAP_TRACK);
586 sendEmptyMessageDelayed(DO_HEAP_TRACK, HEAP_TRACK_INTERVAL);
587 break;
588 }
Adrian Roos91250682017-02-06 14:48:15 -0800589 }
590 }
591}