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.internal.app; 18 19 import android.app.Activity; 20 import android.app.ActivityManager; 21 import android.app.AlertDialog; 22 import android.content.ActivityNotFoundException; 23 import android.content.ClipData; 24 import android.content.DialogInterface; 25 import android.content.Intent; 26 import android.net.Uri; 27 import android.os.Bundle; 28 import android.util.DebugUtils; 29 import android.util.Slog; 30 31 /** 32 * This activity is displayed when the system has collected a heap dump from 33 * a large process and the user has selected to share it. 34 */ 35 public class DumpHeapActivity extends Activity { 36 /** The process we are reporting */ 37 public static final String KEY_PROCESS = "process"; 38 /** The size limit the process reached */ 39 public static final String KEY_SIZE = "size"; 40 /** Whether the user initiated the dump or not. */ 41 public static final String KEY_IS_USER_INITIATED = "is_user_initiated"; 42 /** Whether the process is a system process (eg: Android System) or not. */ 43 public static final String KEY_IS_SYSTEM_PROCESS = "is_system_process"; 44 /** Optional name of package to directly launch */ 45 public static final String KEY_DIRECT_LAUNCH = "direct_launch"; 46 47 // Broadcast action to determine when to delete the current dump heap data. 48 public static final String ACTION_DELETE_DUMPHEAP = "com.android.server.am.DELETE_DUMPHEAP"; 49 50 // Extra for above: delay delete of data, since the user is in the process of sharing it. 51 public static final String EXTRA_DELAY_DELETE = "delay_delete"; 52 53 static final public Uri JAVA_URI = Uri.parse("content://com.android.server.heapdump/java"); 54 55 String mProcess; 56 long mSize; 57 AlertDialog mDialog; 58 boolean mHandled = false; 59 60 @Override onCreate(Bundle savedInstanceState)61 protected void onCreate(Bundle savedInstanceState) { 62 super.onCreate(savedInstanceState); 63 64 mProcess = getIntent().getStringExtra(KEY_PROCESS); 65 mSize = getIntent().getLongExtra(KEY_SIZE, 0); 66 final boolean isUserInitiated = getIntent().getBooleanExtra(KEY_IS_USER_INITIATED, false); 67 final boolean isSystemProcess = getIntent().getBooleanExtra(KEY_IS_SYSTEM_PROCESS, false); 68 69 String directLaunch = getIntent().getStringExtra(KEY_DIRECT_LAUNCH); 70 if (directLaunch != null) { 71 Intent intent = new Intent(ActivityManager.ACTION_REPORT_HEAP_LIMIT); 72 intent.setPackage(directLaunch); 73 ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI); 74 intent.setClipData(clip); 75 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 76 intent.setType(clip.getDescription().getMimeType(0)); 77 intent.putExtra(Intent.EXTRA_STREAM, JAVA_URI); 78 try { 79 startActivity(intent); 80 scheduleDelete(); 81 mHandled = true; 82 finish(); 83 return; 84 } catch (ActivityNotFoundException e) { 85 Slog.i("DumpHeapActivity", "Unable to direct launch to " + directLaunch 86 + ": " + e.getMessage()); 87 } 88 } 89 90 final int messageId; 91 if (isUserInitiated) { 92 messageId = com.android.internal.R.string.dump_heap_ready_text; 93 } else if (isSystemProcess) { 94 messageId = com.android.internal.R.string.dump_heap_system_text; 95 } else { 96 messageId = com.android.internal.R.string.dump_heap_text; 97 } 98 AlertDialog.Builder b = new AlertDialog.Builder(this, 99 android.R.style.Theme_Material_Light_Dialog_Alert); 100 b.setTitle(com.android.internal.R.string.dump_heap_title); 101 b.setMessage(getString( 102 messageId, mProcess, DebugUtils.sizeValueToString(mSize, null))); 103 b.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 104 @Override 105 public void onClick(DialogInterface dialog, int which) { 106 mHandled = true; 107 sendBroadcast(new Intent(ACTION_DELETE_DUMPHEAP)); 108 finish(); 109 } 110 }); 111 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 112 @Override 113 public void onClick(DialogInterface dialog, int which) { 114 mHandled = true; 115 scheduleDelete(); 116 Intent intent = new Intent(Intent.ACTION_SEND); 117 ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI); 118 intent.setClipData(clip); 119 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 120 intent.setType(clip.getDescription().getMimeType(0)); 121 intent.putExtra(Intent.EXTRA_STREAM, JAVA_URI); 122 startActivity(Intent.createChooser(intent, 123 getText(com.android.internal.R.string.dump_heap_title))); 124 finish(); 125 } 126 }); 127 mDialog = b.show(); 128 } 129 scheduleDelete()130 void scheduleDelete() { 131 Intent broadcast = new Intent(ACTION_DELETE_DUMPHEAP); 132 broadcast.putExtra(EXTRA_DELAY_DELETE, true); 133 sendBroadcast(broadcast); 134 } 135 136 @Override onStop()137 protected void onStop() { 138 super.onStop(); 139 if (!isChangingConfigurations()) { 140 if (!mHandled) { 141 sendBroadcast(new Intent(ACTION_DELETE_DUMPHEAP)); 142 } 143 } 144 } 145 146 @Override onDestroy()147 protected void onDestroy() { 148 super.onDestroy(); 149 mDialog.dismiss(); 150 } 151 } 152