blob: 48ecaa5445be299c1c3e1efcc113c0fa3906f139 [file] [log] [blame]
Jeff Sharkey02ffba92013-03-08 16:13:15 -08001/*
2 * Copyright (C) 2013 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
17package com.android.shell;
18
19import static com.android.shell.BugreportPrefs.STATE_SHOW;
20import static com.android.shell.BugreportPrefs.getWarningState;
21
22import android.accounts.Account;
23import android.accounts.AccountManager;
24import android.app.Notification;
25import android.app.NotificationManager;
26import android.app.PendingIntent;
27import android.content.BroadcastReceiver;
28import android.content.Context;
29import android.content.Intent;
30import android.net.Uri;
31import android.os.AsyncTask;
Jeff Sharkeyd9526902013-03-14 14:11:57 -070032import android.os.FileUtils;
Jeff Sharkey02ffba92013-03-08 16:13:15 -080033import android.os.SystemProperties;
34import android.support.v4.content.FileProvider;
Jeff Sharkeyd9526902013-03-14 14:11:57 -070035import android.text.format.DateUtils;
Jeff Sharkey02ffba92013-03-08 16:13:15 -080036import android.util.Patterns;
37
38import com.google.android.collect.Lists;
39
40import java.io.File;
41import java.util.ArrayList;
Jeff Sharkey02ffba92013-03-08 16:13:15 -080042
43/**
44 * Receiver that handles finished bugreports, usually by attaching them to an
45 * {@link Intent#ACTION_SEND}.
46 */
47public class BugreportReceiver extends BroadcastReceiver {
48 private static final String TAG = "Shell";
49
50 private static final String AUTHORITY = "com.android.shell";
51
52 private static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
53 private static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
54
55 /**
Jeff Sharkeyd9526902013-03-14 14:11:57 -070056 * Always keep the newest 8 bugreport files; 4 reports and 4 screenshots are
57 * roughly 17MB of disk space.
Jeff Sharkey02ffba92013-03-08 16:13:15 -080058 */
Jeff Sharkeyd9526902013-03-14 14:11:57 -070059 private static final int MIN_KEEP_COUNT = 8;
60
61 /**
62 * Always keep bugreports taken in the last week.
63 */
64 private static final long MIN_KEEP_AGE = DateUtils.WEEK_IN_MILLIS;
Jeff Sharkey02ffba92013-03-08 16:13:15 -080065
66 @Override
67 public void onReceive(Context context, Intent intent) {
68 final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
69 final File screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT);
70
71 // Files are kept on private storage, so turn into Uris that we can
72 // grant temporary permissions for.
73 final Uri bugreportUri = FileProvider.getUriForFile(context, AUTHORITY, bugreportFile);
74 final Uri screenshotUri = FileProvider.getUriForFile(context, AUTHORITY, screenshotFile);
75
76 Intent sendIntent = buildSendIntent(context, bugreportUri, screenshotUri);
77 Intent notifIntent;
78
79 // Send through warning dialog by default
80 if (getWarningState(context, STATE_SHOW) == STATE_SHOW) {
81 notifIntent = buildWarningIntent(context, sendIntent);
82 } else {
83 notifIntent = sendIntent;
84 }
85 notifIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
86
Selim Cinek255dd042014-08-19 22:29:02 +020087 final Notification.Builder builder = new Notification.Builder(context)
88 .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
89 .setContentTitle(context.getString(R.string.bugreport_finished_title))
90 .setTicker(context.getString(R.string.bugreport_finished_title))
91 .setContentText(context.getString(R.string.bugreport_finished_text))
92 .setContentIntent(PendingIntent.getActivity(
93 context, 0, notifIntent, PendingIntent.FLAG_CANCEL_CURRENT))
94 .setAutoCancel(true)
Wei Liue255e4e2014-10-06 14:50:10 -070095 .setLocalOnly(true)
Selim Cinek255dd042014-08-19 22:29:02 +020096 .setColor(context.getResources().getColor(
97 com.android.internal.R.color.system_notification_accent_color));
98
Jeff Sharkey02ffba92013-03-08 16:13:15 -080099 NotificationManager.from(context).notify(TAG, 0, builder.build());
100
101 // Clean up older bugreports in background
102 final PendingResult result = goAsync();
103 new AsyncTask<Void, Void, Void>() {
104 @Override
105 protected Void doInBackground(Void... params) {
Jeff Sharkeyd9526902013-03-14 14:11:57 -0700106 FileUtils.deleteOlderFiles(
107 bugreportFile.getParentFile(), MIN_KEEP_COUNT, MIN_KEEP_AGE);
Jeff Sharkey02ffba92013-03-08 16:13:15 -0800108 result.finish();
109 return null;
110 }
111 }.execute();
112 }
113
114 private static Intent buildWarningIntent(Context context, Intent sendIntent) {
115 final Intent intent = new Intent(context, BugreportWarningActivity.class);
116 intent.putExtra(Intent.EXTRA_INTENT, sendIntent);
117 return intent;
118 }
119
120 /**
121 * Build {@link Intent} that can be used to share the given bugreport.
122 */
123 private static Intent buildSendIntent(Context context, Uri bugreportUri, Uri screenshotUri) {
124 final Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
125 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
126 intent.addCategory(Intent.CATEGORY_DEFAULT);
127 intent.setType("application/vnd.android.bugreport");
128
129 intent.putExtra(Intent.EXTRA_SUBJECT, bugreportUri.getLastPathSegment());
130 intent.putExtra(Intent.EXTRA_TEXT, SystemProperties.get("ro.build.description"));
131
132 final ArrayList<Uri> attachments = Lists.newArrayList(bugreportUri, screenshotUri);
133 intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachments);
134
135 final Account sendToAccount = findSendToAccount(context);
136 if (sendToAccount != null) {
137 intent.putExtra(Intent.EXTRA_EMAIL, new String[] { sendToAccount.name });
138 }
139
140 return intent;
141 }
142
143 /**
144 * Find the best matching {@link Account} based on build properties.
145 */
146 private static Account findSendToAccount(Context context) {
147 final AccountManager am = (AccountManager) context.getSystemService(
148 Context.ACCOUNT_SERVICE);
149
150 String preferredDomain = SystemProperties.get("sendbug.preferred.domain");
151 if (!preferredDomain.startsWith("@")) {
152 preferredDomain = "@" + preferredDomain;
153 }
154
155 final Account[] accounts = am.getAccounts();
156 Account foundAccount = null;
157 for (Account account : accounts) {
158 if (Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) {
159 if (!preferredDomain.isEmpty()) {
160 // if we have a preferred domain and it matches, return; otherwise keep
161 // looking
162 if (account.name.endsWith(preferredDomain)) {
163 return account;
164 } else {
165 foundAccount = account;
166 }
167 // if we don't have a preferred domain, just return since it looks like
168 // an email address
169 } else {
170 return account;
171 }
172 }
173 }
174 return foundAccount;
175 }
176
Jeff Sharkey02ffba92013-03-08 16:13:15 -0800177 private static File getFileExtra(Intent intent, String key) {
178 final String path = intent.getStringExtra(key);
179 if (path != null) {
180 return new File(path);
181 } else {
182 return null;
183 }
184 }
Jeff Sharkey02ffba92013-03-08 16:13:15 -0800185}