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     /** Optional name of package to directly launch */
41     public static final String KEY_DIRECT_LAUNCH = "direct_launch";
42 
43     // Broadcast action to determine when to delete the current dump heap data.
44     public static final String ACTION_DELETE_DUMPHEAP = "com.android.server.am.DELETE_DUMPHEAP";
45 
46     // Extra for above: delay delete of data, since the user is in the process of sharing it.
47     public static final String EXTRA_DELAY_DELETE = "delay_delete";
48 
49     static final public Uri JAVA_URI = Uri.parse("content://com.android.server.heapdump/java");
50 
51     String mProcess;
52     long mSize;
53     AlertDialog mDialog;
54     boolean mHandled = false;
55 
56     @Override
onCreate(Bundle savedInstanceState)57     protected void onCreate(Bundle savedInstanceState) {
58         super.onCreate(savedInstanceState);
59 
60         mProcess = getIntent().getStringExtra(KEY_PROCESS);
61         mSize = getIntent().getLongExtra(KEY_SIZE, 0);
62 
63         String directLaunch = getIntent().getStringExtra(KEY_DIRECT_LAUNCH);
64         if (directLaunch != null) {
65             Intent intent = new Intent(ActivityManager.ACTION_REPORT_HEAP_LIMIT);
66             intent.setPackage(directLaunch);
67             ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI);
68             intent.setClipData(clip);
69             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
70             intent.setType(clip.getDescription().getMimeType(0));
71             intent.putExtra(Intent.EXTRA_STREAM, JAVA_URI);
72             try {
73                 startActivity(intent);
74                 scheduleDelete();
75                 mHandled = true;
76                 finish();
77                 return;
78             } catch (ActivityNotFoundException e) {
79                 Slog.i("DumpHeapActivity", "Unable to direct launch to " + directLaunch
80                         + ": " + e.getMessage());
81             }
82         }
83 
84         AlertDialog.Builder b = new AlertDialog.Builder(this,
85                 android.R.style.Theme_Material_Light_Dialog_Alert);
86         b.setTitle(com.android.internal.R.string.dump_heap_title);
87         b.setMessage(getString(com.android.internal.R.string.dump_heap_text,
88                 mProcess, DebugUtils.sizeValueToString(mSize, null)));
89         b.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
90             @Override
91             public void onClick(DialogInterface dialog, int which) {
92                 mHandled = true;
93                 sendBroadcast(new Intent(ACTION_DELETE_DUMPHEAP));
94                 finish();
95             }
96         });
97         b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
98             @Override
99             public void onClick(DialogInterface dialog, int which) {
100                 mHandled = true;
101                 scheduleDelete();
102                 Intent intent = new Intent(Intent.ACTION_SEND);
103                 ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI);
104                 intent.setClipData(clip);
105                 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
106                 intent.setType(clip.getDescription().getMimeType(0));
107                 intent.putExtra(Intent.EXTRA_STREAM, JAVA_URI);
108                 startActivity(Intent.createChooser(intent,
109                         getText(com.android.internal.R.string.dump_heap_title)));
110                 finish();
111         }
112         });
113         mDialog = b.show();
114     }
115 
scheduleDelete()116     void scheduleDelete() {
117         Intent broadcast = new Intent(ACTION_DELETE_DUMPHEAP);
118         broadcast.putExtra(EXTRA_DELAY_DELETE, true);
119         sendBroadcast(broadcast);
120     }
121 
122     @Override
onStop()123     protected void onStop() {
124         super.onStop();
125         if (!isChangingConfigurations()) {
126             if (!mHandled) {
127                 sendBroadcast(new Intent(ACTION_DELETE_DUMPHEAP));
128             }
129         }
130     }
131 
132     @Override
onDestroy()133     protected void onDestroy() {
134         super.onDestroy();
135         mDialog.dismiss();
136     }
137 }
138