Merge "Move heap dump sharing to SHELL."
This commit is contained in:
@@ -173,6 +173,8 @@
|
|||||||
<uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
|
<uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
|
||||||
<!-- Permission needed to invoke DynamicSystem (AOT) -->
|
<!-- Permission needed to invoke DynamicSystem (AOT) -->
|
||||||
<uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" />
|
<uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" />
|
||||||
|
<!-- Used to clean up heap dumps on boot. -->
|
||||||
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
|
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
|
||||||
@@ -231,12 +233,25 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<provider android:name=".HeapDumpProvider"
|
||||||
|
android:authorities="com.android.shell.heapdump"
|
||||||
|
android:grantUriPermissions="true"
|
||||||
|
android:exported="true" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".BugreportWarningActivity"
|
android:name=".BugreportWarningActivity"
|
||||||
android:finishOnCloseSystemDialogs="true"
|
android:finishOnCloseSystemDialogs="true"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity android:name=".HeapDumpActivity"
|
||||||
|
android:theme="@*android:style/Theme.Translucent.NoTitleBar"
|
||||||
|
android:label="@*android:string/dump_heap_title"
|
||||||
|
android:finishOnCloseSystemDialogs="true"
|
||||||
|
android:noHistory="true"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".BugreportReceiver"
|
android:name=".BugreportReceiver"
|
||||||
android:permission="android.permission.DUMP">
|
android:permission="android.permission.DUMP">
|
||||||
@@ -254,6 +269,16 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".HeapDumpReceiver"
|
||||||
|
android:permission="android.permission.DUMP">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
<action android:name="com.android.internal.intent.action.HEAP_DUMP_FINISHED" />
|
||||||
|
<action android:name="com.android.shell.action.DELETE_HEAP_DUMP" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".BugreportProgressService"
|
android:name=".BugreportProgressService"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
|
|||||||
@@ -1410,7 +1410,7 @@ public class BugreportProgressService extends Service {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isTv(Context context) {
|
static boolean isTv(Context context) {
|
||||||
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
|
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
142
packages/Shell/src/com/android/shell/HeapDumpActivity.java
Normal file
142
packages/Shell/src/com/android/shell/HeapDumpActivity.java
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* 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.HeapDumpProvider.makeUri;
|
||||||
|
import static com.android.shell.HeapDumpReceiver.ACTION_DELETE_HEAP_DUMP;
|
||||||
|
import static com.android.shell.HeapDumpReceiver.EXTRA_IS_USER_INITIATED;
|
||||||
|
import static com.android.shell.HeapDumpReceiver.EXTRA_PROCESS_NAME;
|
||||||
|
import static com.android.shell.HeapDumpReceiver.EXTRA_REPORT_PACKAGE;
|
||||||
|
import static com.android.shell.HeapDumpReceiver.EXTRA_SIZE_BYTES;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.ActivityManager;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.util.DebugUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This activity is displayed when the system has collected a heap dump.
|
||||||
|
*/
|
||||||
|
public class HeapDumpActivity extends Activity {
|
||||||
|
private static final String TAG = "HeapDumpActivity";
|
||||||
|
|
||||||
|
static final String KEY_URI = "uri";
|
||||||
|
|
||||||
|
private AlertDialog mDialog;
|
||||||
|
private Uri mDumpUri;
|
||||||
|
private boolean mHandled = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
String process = getIntent().getStringExtra(EXTRA_PROCESS_NAME);
|
||||||
|
long size = getIntent().getLongExtra(EXTRA_SIZE_BYTES, 0);
|
||||||
|
final boolean isUserInitiated = getIntent().getBooleanExtra(EXTRA_IS_USER_INITIATED, false);
|
||||||
|
final int uid = getIntent().getIntExtra(Intent.EXTRA_UID, 0);
|
||||||
|
final boolean isSystemProcess = uid == Process.SYSTEM_UID;
|
||||||
|
mDumpUri = makeUri(process);
|
||||||
|
final String procDisplayName = isSystemProcess
|
||||||
|
? getString(com.android.internal.R.string.android_system_label)
|
||||||
|
: process;
|
||||||
|
|
||||||
|
final Intent sendIntent = new Intent();
|
||||||
|
ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", mDumpUri);
|
||||||
|
sendIntent.setClipData(clip);
|
||||||
|
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
sendIntent.setType(clip.getDescription().getMimeType(0));
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_STREAM, mDumpUri);
|
||||||
|
|
||||||
|
String directLaunchPackage = getIntent().getStringExtra(EXTRA_REPORT_PACKAGE);
|
||||||
|
if (directLaunchPackage != null) {
|
||||||
|
sendIntent.setAction(ActivityManager.ACTION_REPORT_HEAP_LIMIT);
|
||||||
|
sendIntent.setPackage(directLaunchPackage);
|
||||||
|
try {
|
||||||
|
startActivity(sendIntent);
|
||||||
|
mHandled = true;
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Log.e(TAG, "Unable to direct launch to " + directLaunchPackage, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final int messageId;
|
||||||
|
if (isUserInitiated) {
|
||||||
|
messageId = com.android.internal.R.string.dump_heap_ready_text;
|
||||||
|
} else if (isSystemProcess) {
|
||||||
|
messageId = com.android.internal.R.string.dump_heap_system_text;
|
||||||
|
} else {
|
||||||
|
messageId = com.android.internal.R.string.dump_heap_text;
|
||||||
|
}
|
||||||
|
mDialog = new AlertDialog.Builder(this, android.R.style.Theme_Material_Light_Dialog_Alert)
|
||||||
|
.setTitle(com.android.internal.R.string.dump_heap_title)
|
||||||
|
.setMessage(getString(messageId, procDisplayName,
|
||||||
|
DebugUtils.sizeValueToString(size, null)))
|
||||||
|
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
|
||||||
|
mHandled = true;
|
||||||
|
finish();
|
||||||
|
})
|
||||||
|
.setNeutralButton(R.string.delete, (dialog, which) -> {
|
||||||
|
mHandled = true;
|
||||||
|
Intent deleteIntent = new Intent(ACTION_DELETE_HEAP_DUMP);
|
||||||
|
deleteIntent.setClass(getApplicationContext(), HeapDumpReceiver.class);
|
||||||
|
deleteIntent.putExtra(KEY_URI, mDumpUri.toString());
|
||||||
|
sendBroadcast(deleteIntent);
|
||||||
|
finish();
|
||||||
|
})
|
||||||
|
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||||
|
mHandled = true;
|
||||||
|
sendIntent.setAction(Intent.ACTION_SEND);
|
||||||
|
sendIntent.setPackage(null);
|
||||||
|
startActivity(Intent.createChooser(sendIntent,
|
||||||
|
getText(com.android.internal.R.string.dump_heap_title)));
|
||||||
|
finish();
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
if (!isChangingConfigurations()) {
|
||||||
|
if (!mHandled) {
|
||||||
|
Intent deleteIntent = new Intent(ACTION_DELETE_HEAP_DUMP);
|
||||||
|
deleteIntent.setClass(getApplicationContext(), HeapDumpReceiver.class);
|
||||||
|
deleteIntent.putExtra(KEY_URI, mDumpUri.toString());
|
||||||
|
sendBroadcast(deleteIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (mDialog != null) {
|
||||||
|
mDialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
101
packages/Shell/src/com/android/shell/HeapDumpProvider.java
Normal file
101
packages/Shell/src/com/android/shell/HeapDumpProvider.java
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.annotation.NonNull;
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.os.Process;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
|
/** ContentProvider to write and access heap dumps. */
|
||||||
|
public class HeapDumpProvider extends ContentProvider {
|
||||||
|
private static final String FILENAME_SUFFIX = "_javaheap.bin";
|
||||||
|
private static final Object sLock = new Object();
|
||||||
|
|
||||||
|
private File mRoot;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
synchronized (sLock) {
|
||||||
|
mRoot = new File(getContext().createCredentialProtectedStorageContext().getFilesDir(),
|
||||||
|
"heapdumps");
|
||||||
|
return mRoot.mkdir();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||||
|
String sortOrder) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType(Uri uri) {
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri insert(Uri uri, ContentValues values) {
|
||||||
|
throw new UnsupportedOperationException("Insert not allowed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||||
|
String path = sanitizePath(uri.getEncodedPath());
|
||||||
|
String tag = Uri.decode(path);
|
||||||
|
return (new File(mRoot, tag)).delete() ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||||
|
throw new UnsupportedOperationException("Update not allowed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
||||||
|
String path = sanitizePath(uri.getEncodedPath());
|
||||||
|
String tag = Uri.decode(path);
|
||||||
|
final int pMode;
|
||||||
|
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
|
||||||
|
pMode = ParcelFileDescriptor.MODE_CREATE
|
||||||
|
| ParcelFileDescriptor.MODE_TRUNCATE
|
||||||
|
| ParcelFileDescriptor.MODE_WRITE_ONLY;
|
||||||
|
} else {
|
||||||
|
pMode = ParcelFileDescriptor.MODE_READ_ONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (sLock) {
|
||||||
|
return ParcelFileDescriptor.open(new File(mRoot, tag), pMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
static Uri makeUri(@NonNull String procName) {
|
||||||
|
return Uri.parse("content://com.android.shell.heapdump/" + procName + FILENAME_SUFFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String sanitizePath(String path) {
|
||||||
|
return path.replaceAll("[^a-zA-Z0-9_.]", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
188
packages/Shell/src/com/android/shell/HeapDumpReceiver.java
Normal file
188
packages/Shell/src/com/android/shell/HeapDumpReceiver.java
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
* 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));
|
||||||
|
|
||||||
|
Log.v(TAG, "Creating share heap dump notification");
|
||||||
|
NotificationManager.from(context).notify(NOTIFICATION_ID, builder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user