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