• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.shell;
18 
19 import static com.android.shell.BugreportPrefs.STATE_SHOW;
20 import static com.android.shell.BugreportPrefs.getWarningState;
21 
22 import android.accounts.Account;
23 import android.accounts.AccountManager;
24 import android.app.Notification;
25 import android.app.NotificationManager;
26 import android.app.PendingIntent;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.net.Uri;
31 import android.os.AsyncTask;
32 import android.os.FileUtils;
33 import android.os.SystemProperties;
34 import android.support.v4.content.FileProvider;
35 import android.text.format.DateUtils;
36 import android.util.Patterns;
37 
38 import com.google.android.collect.Lists;
39 
40 import java.io.File;
41 import java.util.ArrayList;
42 
43 /**
44  * Receiver that handles finished bugreports, usually by attaching them to an
45  * {@link Intent#ACTION_SEND}.
46  */
47 public 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     /**
56      * Always keep the newest 8 bugreport files; 4 reports and 4 screenshots are
57      * roughly 17MB of disk space.
58      */
59     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;
65 
66     @Override
onReceive(Context context, Intent intent)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 
87         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)
95                 .setLocalOnly(true)
96                 .setColor(context.getResources().getColor(
97                         com.android.internal.R.color.system_notification_accent_color));
98 
99         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) {
106                 FileUtils.deleteOlderFiles(
107                         bugreportFile.getParentFile(), MIN_KEEP_COUNT, MIN_KEEP_AGE);
108                 result.finish();
109                 return null;
110             }
111         }.execute();
112     }
113 
buildWarningIntent(Context context, Intent sendIntent)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      */
buildSendIntent(Context context, Uri bugreportUri, Uri screenshotUri)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      */
findSendToAccount(Context context)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 
getFileExtra(Intent intent, String key)177     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     }
185 }
186