189 lines
7.4 KiB
Java
189 lines
7.4 KiB
Java
/*
|
|
* Copyright (C) 2019 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.shell;
|
|
|
|
import static com.android.shell.BugreportProgressService.isTv;
|
|
|
|
import android.annotation.Nullable;
|
|
import android.app.Notification;
|
|
import android.app.NotificationChannel;
|
|
import android.app.NotificationManager;
|
|
import android.app.PendingIntent;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.net.Uri;
|
|
import android.os.AsyncTask;
|
|
import android.os.FileUtils;
|
|
import android.os.Process;
|
|
import android.text.format.DateUtils;
|
|
import android.util.Log;
|
|
|
|
import java.io.File;
|
|
|
|
/**
|
|
* Receiver that handles finished heap dumps.
|
|
*/
|
|
public class HeapDumpReceiver extends BroadcastReceiver {
|
|
private static final String TAG = "HeapDumpReceiver";
|
|
|
|
/**
|
|
* Broadcast action to determine when to delete a specific dump heap. Must include a {@link
|
|
* HeapDumpActivity#KEY_URI} String extra.
|
|
*/
|
|
static final String ACTION_DELETE_HEAP_DUMP = "com.android.shell.action.DELETE_HEAP_DUMP";
|
|
|
|
/** Broadcast sent when heap dump collection has been completed. */
|
|
private static final String ACTION_HEAP_DUMP_FINISHED =
|
|
"com.android.internal.intent.action.HEAP_DUMP_FINISHED";
|
|
|
|
/** The process we are reporting */
|
|
static final String EXTRA_PROCESS_NAME = "com.android.internal.extra.heap_dump.PROCESS_NAME";
|
|
|
|
/** The size limit the process reached. */
|
|
static final String EXTRA_SIZE_BYTES = "com.android.internal.extra.heap_dump.SIZE_BYTES";
|
|
|
|
/** Whether the user initiated the dump or not. */
|
|
static final String EXTRA_IS_USER_INITIATED =
|
|
"com.android.internal.extra.heap_dump.IS_USER_INITIATED";
|
|
|
|
/** Optional name of package to directly launch. */
|
|
static final String EXTRA_REPORT_PACKAGE =
|
|
"com.android.internal.extra.heap_dump.REPORT_PACKAGE";
|
|
|
|
private static final String NOTIFICATION_CHANNEL_ID = "heapdumps";
|
|
private static final int NOTIFICATION_ID = 2019;
|
|
|
|
/**
|
|
* Always keep heap dumps taken in the last week.
|
|
*/
|
|
private static final long MIN_KEEP_AGE_MS = DateUtils.WEEK_IN_MILLIS;
|
|
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
Log.d(TAG, "onReceive(): " + intent);
|
|
final String action = intent.getAction();
|
|
if (action == null) {
|
|
Log.e(TAG, "null action received");
|
|
return;
|
|
}
|
|
switch (action) {
|
|
case Intent.ACTION_BOOT_COMPLETED:
|
|
cleanupOldFiles(context);
|
|
break;
|
|
case ACTION_DELETE_HEAP_DUMP:
|
|
deleteHeapDump(context, intent.getStringExtra(HeapDumpActivity.KEY_URI));
|
|
break;
|
|
case ACTION_HEAP_DUMP_FINISHED:
|
|
showDumpNotification(context, intent);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void cleanupOldFiles(Context context) {
|
|
final PendingResult result = goAsync();
|
|
new AsyncTask<Void, Void, Void>() {
|
|
@Override
|
|
protected Void doInBackground(Void... params) {
|
|
try {
|
|
Log.d(TAG, "Deleting from " + new File(context.getFilesDir(), "heapdumps"));
|
|
FileUtils.deleteOlderFiles(new File(context.getFilesDir(), "heapdumps"), 0,
|
|
MIN_KEEP_AGE_MS);
|
|
} catch (RuntimeException e) {
|
|
Log.e(TAG, "Couldn't delete old files", e);
|
|
}
|
|
result.finish();
|
|
return null;
|
|
}
|
|
}.execute();
|
|
}
|
|
|
|
private void deleteHeapDump(Context context, @Nullable final String uri) {
|
|
if (uri == null) {
|
|
Log.e(TAG, "null URI for delete heap dump intent");
|
|
return;
|
|
}
|
|
final PendingResult result = goAsync();
|
|
new AsyncTask<Void, Void, Void>() {
|
|
@Override
|
|
protected Void doInBackground(Void... params) {
|
|
context.getContentResolver().delete(Uri.parse(uri), null, null);
|
|
result.finish();
|
|
return null;
|
|
}
|
|
}.execute();
|
|
}
|
|
|
|
private void showDumpNotification(Context context, Intent intent) {
|
|
final boolean isUserInitiated = intent.getBooleanExtra(
|
|
EXTRA_IS_USER_INITIATED, false);
|
|
final String procName = intent.getStringExtra(EXTRA_PROCESS_NAME);
|
|
final int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
|
|
|
|
final String reportPackage = intent.getStringExtra(
|
|
EXTRA_REPORT_PACKAGE);
|
|
final long size = intent.getLongExtra(EXTRA_SIZE_BYTES, 0);
|
|
|
|
if (procName == null) {
|
|
Log.e(TAG, "No process name sent over");
|
|
return;
|
|
}
|
|
|
|
NotificationManager nm = NotificationManager.from(context);
|
|
nm.createNotificationChannel(
|
|
new NotificationChannel(NOTIFICATION_CHANNEL_ID,
|
|
"Heap dumps",
|
|
NotificationManager.IMPORTANCE_DEFAULT));
|
|
|
|
final int titleId = isUserInitiated
|
|
? com.android.internal.R.string.dump_heap_ready_notification
|
|
: com.android.internal.R.string.dump_heap_notification;
|
|
final String procDisplayName = uid == Process.SYSTEM_UID
|
|
? context.getString(com.android.internal.R.string.android_system_label)
|
|
: procName;
|
|
String text = context.getString(titleId, procDisplayName);
|
|
|
|
Intent shareIntent = new Intent();
|
|
shareIntent.setClassName(context, HeapDumpActivity.class.getName());
|
|
shareIntent.putExtra(EXTRA_PROCESS_NAME, procName);
|
|
shareIntent.putExtra(EXTRA_SIZE_BYTES, size);
|
|
shareIntent.putExtra(EXTRA_IS_USER_INITIATED, isUserInitiated);
|
|
shareIntent.putExtra(Intent.EXTRA_UID, uid);
|
|
if (reportPackage != null) {
|
|
shareIntent.putExtra(EXTRA_REPORT_PACKAGE, reportPackage);
|
|
}
|
|
final Notification.Builder builder = new Notification.Builder(context,
|
|
NOTIFICATION_CHANNEL_ID)
|
|
.setSmallIcon(
|
|
isTv(context) ? R.drawable.ic_bug_report_black_24dp
|
|
: com.android.internal.R.drawable.stat_sys_adb)
|
|
.setLocalOnly(true)
|
|
.setColor(context.getColor(
|
|
com.android.internal.R.color.system_notification_accent_color))
|
|
.setContentTitle(text)
|
|
.setTicker(text)
|
|
.setAutoCancel(true)
|
|
.setContentText(context.getText(
|
|
com.android.internal.R.string.dump_heap_notification_detail))
|
|
.setContentIntent(PendingIntent.getActivity(context, 2, shareIntent,
|
|
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
|
|
|
|
Log.v(TAG, "Creating share heap dump notification");
|
|
NotificationManager.from(context).notify(NOTIFICATION_ID, builder.build());
|
|
}
|
|
}
|