SendBug.java revision 01cef7d875c77ad5efed9e73d0e3d60a258f79de
1/*
2 * Copyright (C) 2011 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 */
16package com.android.commands.sendbug;
17
18import android.accounts.Account;
19import android.accounts.IAccountManager;
20import android.app.ActivityManagerNative;
21import android.app.IActivityManager;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.IPackageManager;
25import android.content.pm.ResolveInfo;
26import android.net.Uri;
27import android.os.Environment;
28import android.os.RemoteException;
29import android.os.ServiceManager;
30import android.os.SystemProperties;
31
32import java.io.File;
33import java.io.FilenameFilter;
34import java.text.ParseException;
35import java.text.SimpleDateFormat;
36import java.util.ArrayList;
37import java.util.Date;
38import java.util.List;
39import java.util.regex.Matcher;
40import java.util.regex.Pattern;
41
42public class SendBug {
43
44    private static final String GOOGLE_ACCOUNT_TYPE = "com.google";
45    private static final String EMAIL_ACCOUNT_TYPE = "com.android.email";
46    private static final String SEND_BUG_INTENT_ACTION = "android.testing.SEND_BUG";
47
48    private static final Pattern datePattern = Pattern.compile(
49            ".*(\\d\\d\\d\\d[-_.]\\d\\d[-_.]\\d\\d[-_.]\\d\\d[-_.]\\d\\d[-_.]\\d\\d).*");
50    private static final File screenshotDir = new File(
51            Environment.getExternalStorageDirectory() + "/Pictures/Screenshots");
52    private static final long MAX_SCREENSHOT_AGE_MS = 5 * 50 * 1000;
53
54    public static void main(String[] args) {
55        if (args.length >= 1) {
56            new SendBug().run(args[0]);
57        }
58    }
59
60    private void run(String bugreportPath) {
61        final File bugreport = new File(bugreportPath);
62        if (bugreport.exists()) {
63            final Uri bugreportUri = Uri.fromFile(bugreport);
64            // todo (aalbert): investigate adding a screenshot to BugReporter
65            Intent intent = tryBugReporter(bugreportUri);
66            if (intent == null) {
67                final File screenshotFile = findScreenshotFile(bugreportPath);
68                final Uri screenshotUri = screenshotFile != null
69                        ? Uri.fromFile(screenshotFile) : null;
70                intent = getSendMailIntent(bugreportUri, screenshotUri);
71            }
72            final IActivityManager mAm = ActivityManagerNative.getDefault();
73            try {
74                mAm.startActivity(null, intent, intent.getType(), null, 0, null, null, 0, false,
75                        false, null, null, false);
76            } catch (RemoteException e) {
77                // ignore
78            }
79        }
80    }
81
82    private Intent tryBugReporter(Uri bugreportUri) {
83        final Intent intent = new Intent(SEND_BUG_INTENT_ACTION);
84        intent.setData(bugreportUri);
85        final IPackageManager mPm = IPackageManager.Stub.asInterface(
86                ServiceManager.getService("package"));
87        if (mPm != null) {
88            final List<ResolveInfo> results;
89            try {
90                results = mPm.queryIntentActivities(intent, null, 0);
91            } catch (RemoteException e) {
92                return null;
93            }
94            if (results != null && results.size() > 0) {
95                final ResolveInfo info = results.get(0);
96                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
97                intent.setClassName(info.activityInfo.applicationInfo.packageName,
98                        info.activityInfo.name);
99                return intent;
100            } else {
101                return null;
102            }
103        }
104        return null;
105    }
106
107    private Intent getSendMailIntent(Uri bugreportUri, Uri screenshotUri) {
108        final Account sendToAccount = findSendToAccount();
109        final Intent intent = new Intent(Intent.ACTION_SEND);
110        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
111        intent.setType("application/octet-stream");
112        intent.putExtra("subject", bugreportUri.getLastPathSegment());
113        final StringBuilder sb = new StringBuilder();
114        sb.append(SystemProperties.get("ro.build.description"));
115        sb.append("\n(Sent from BugMailer)");
116        intent.putExtra("body", sb.toString());
117        if (screenshotUri != null) {
118            final ArrayList<Uri> attachments = new ArrayList<Uri>();
119            attachments.add(bugreportUri);
120            attachments.add(screenshotUri);
121            intent.setAction(Intent.ACTION_SEND_MULTIPLE);
122            intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachments);
123        } else {
124            intent.putExtra(Intent.EXTRA_STREAM, bugreportUri);
125        }
126        if (sendToAccount != null) {
127            intent.putExtra("to", sendToAccount.name);
128        }
129        return intent;
130    }
131
132    private Account findSendToAccount() {
133        final IAccountManager accountManager = IAccountManager.Stub.asInterface(ServiceManager
134                .getService(Context.ACCOUNT_SERVICE));
135        Account[] accounts = null;
136        Account foundAccount = null;
137        try {
138            accounts = accountManager.getAccounts(null);
139        } catch (RemoteException e) {
140            // ignore
141        }
142        if (accounts != null) {
143            for (Account account : accounts) {
144                if (GOOGLE_ACCOUNT_TYPE.equals(account.type)) {
145                    // return first gmail account found
146                    return account;
147                } else if (EMAIL_ACCOUNT_TYPE.equals(account.type)) {
148                    // keep regular email account for now in case there are gmail accounts
149                    // found later
150                    foundAccount = account;
151                }
152            }
153        }
154        return foundAccount;
155    }
156
157    // Try to find a screenshot that was taken shortly before this bugreport was.
158    private File findScreenshotFile(String bugreportPath) {
159        final Date bugreportDate = getDate(bugreportPath);
160        if (bugreportDate == null) {
161            return null;
162        }
163
164        final String[] screenshotFiles = screenshotDir.list(
165                new FilenameFilter() {
166                    private final Pattern pattern = Pattern.compile("[Ss]creenshot.*\\.png");
167
168                    public boolean accept(File dir, String filename) {
169                        return pattern.matcher(filename).matches();
170                    }
171                });
172        long minDiff = Long.MAX_VALUE;
173        String bestMatch = null;
174        for (String screenshotFile : screenshotFiles) {
175            final Date date = getDate(screenshotFile);
176            if (date == null) {
177                continue;
178            }
179            final long diff = bugreportDate.getTime() - date.getTime();
180            if (diff < minDiff) {
181                minDiff = diff;
182                bestMatch = screenshotFile;
183            }
184        }
185
186        if (minDiff < MAX_SCREENSHOT_AGE_MS) {
187            return new File(screenshotDir, bestMatch);
188        }
189
190        return null;
191    }
192
193    private static Date getDate(final String string) {
194        final Matcher matcher = datePattern.matcher(string);
195        if (!matcher.matches()) {
196            return null;
197        }
198        final String dateString = matcher.group(1);
199        final char sep1 = dateString.charAt(4);
200        final char sep2 = dateString.charAt(7);
201        final char sep3 = dateString.charAt(10);
202        final char sep4 = dateString.charAt(13);
203        final char sep5 = dateString.charAt(16);
204        final SimpleDateFormat format = new SimpleDateFormat(
205                "yyyy" + sep1 + "MM" + sep2 + "dd" + sep3 + "HH" + sep4 + "mm" + sep5 + "ss");
206        try {
207            return format.parse(dateString);
208        } catch (ParseException e) {
209            return null;
210        }
211    }
212}
213