1 /* 2 * Copyright (C) 2019 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.shell; 18 19 import static com.android.shell.HeapDumpProvider.makeUri; 20 import static com.android.shell.HeapDumpReceiver.ACTION_DELETE_HEAP_DUMP; 21 import static com.android.shell.HeapDumpReceiver.EXTRA_IS_USER_INITIATED; 22 import static com.android.shell.HeapDumpReceiver.EXTRA_PROCESS_NAME; 23 import static com.android.shell.HeapDumpReceiver.EXTRA_REPORT_PACKAGE; 24 import static com.android.shell.HeapDumpReceiver.EXTRA_SIZE_BYTES; 25 26 import android.app.Activity; 27 import android.app.ActivityManager; 28 import android.app.AlertDialog; 29 import android.content.ActivityNotFoundException; 30 import android.content.ClipData; 31 import android.content.Intent; 32 import android.net.Uri; 33 import android.os.Bundle; 34 import android.os.Process; 35 import android.util.DebugUtils; 36 import android.util.Log; 37 38 import com.android.internal.R; 39 40 /** 41 * This activity is displayed when the system has collected a heap dump. 42 */ 43 public class HeapDumpActivity extends Activity { 44 private static final String TAG = "HeapDumpActivity"; 45 46 static final String KEY_URI = "uri"; 47 48 private AlertDialog mDialog; 49 private Uri mDumpUri; 50 private boolean mHandled = false; 51 52 @Override onCreate(Bundle savedInstanceState)53 protected void onCreate(Bundle savedInstanceState) { 54 super.onCreate(savedInstanceState); 55 56 String process = getIntent().getStringExtra(EXTRA_PROCESS_NAME); 57 long size = getIntent().getLongExtra(EXTRA_SIZE_BYTES, 0); 58 final boolean isUserInitiated = getIntent().getBooleanExtra(EXTRA_IS_USER_INITIATED, false); 59 final int uid = getIntent().getIntExtra(Intent.EXTRA_UID, 0); 60 final boolean isSystemProcess = uid == Process.SYSTEM_UID; 61 mDumpUri = makeUri(process); 62 final String procDisplayName = isSystemProcess 63 ? getString(com.android.internal.R.string.android_system_label) 64 : process; 65 66 final Intent sendIntent = new Intent(); 67 ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", mDumpUri); 68 sendIntent.setClipData(clip); 69 sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 70 sendIntent.setType(clip.getDescription().getMimeType(0)); 71 sendIntent.putExtra(Intent.EXTRA_STREAM, mDumpUri); 72 73 String directLaunchPackage = getIntent().getStringExtra(EXTRA_REPORT_PACKAGE); 74 if (directLaunchPackage != null) { 75 sendIntent.setAction(ActivityManager.ACTION_REPORT_HEAP_LIMIT); 76 sendIntent.setPackage(directLaunchPackage); 77 try { 78 startActivity(sendIntent); 79 mHandled = true; 80 finish(); 81 return; 82 } catch (ActivityNotFoundException e) { 83 Log.e(TAG, "Unable to direct launch to " + directLaunchPackage, e); 84 } 85 } 86 87 final int messageId; 88 if (isUserInitiated) { 89 messageId = com.android.internal.R.string.dump_heap_ready_text; 90 } else if (isSystemProcess) { 91 messageId = com.android.internal.R.string.dump_heap_system_text; 92 } else { 93 messageId = com.android.internal.R.string.dump_heap_text; 94 } 95 mDialog = new AlertDialog.Builder(this, android.R.style.Theme_Material_Light_Dialog_Alert) 96 .setTitle(com.android.internal.R.string.dump_heap_title) 97 .setMessage(getString(messageId, procDisplayName, 98 DebugUtils.sizeValueToString(size, null))) 99 .setNegativeButton(android.R.string.cancel, (dialog, which) -> { 100 mHandled = true; 101 finish(); 102 }) 103 .setNeutralButton(R.string.delete, (dialog, which) -> { 104 mHandled = true; 105 Intent deleteIntent = new Intent(ACTION_DELETE_HEAP_DUMP); 106 deleteIntent.setClass(getApplicationContext(), HeapDumpReceiver.class); 107 deleteIntent.putExtra(KEY_URI, mDumpUri.toString()); 108 sendBroadcast(deleteIntent); 109 finish(); 110 }) 111 .setPositiveButton(android.R.string.ok, (dialog, which) -> { 112 mHandled = true; 113 sendIntent.setAction(Intent.ACTION_SEND); 114 sendIntent.setPackage(null); 115 startActivity(Intent.createChooser(sendIntent, 116 getText(com.android.internal.R.string.dump_heap_title))); 117 finish(); 118 }) 119 .show(); 120 } 121 122 @Override onStop()123 protected void onStop() { 124 super.onStop(); 125 if (!isChangingConfigurations()) { 126 if (!mHandled) { 127 Intent deleteIntent = new Intent(ACTION_DELETE_HEAP_DUMP); 128 deleteIntent.setClass(getApplicationContext(), HeapDumpReceiver.class); 129 deleteIntent.putExtra(KEY_URI, mDumpUri.toString()); 130 sendBroadcast(deleteIntent); 131 } 132 } 133 } 134 135 @Override onDestroy()136 protected void onDestroy() { 137 super.onDestroy(); 138 if (mDialog != null) { 139 mDialog.dismiss(); 140 } 141 } 142 } 143