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