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 */ 16 package com.android.commands.sendbug; 17 18 import android.accounts.Account; 19 import android.accounts.IAccountManager; 20 import android.app.ActivityManagerNative; 21 import android.app.IActivityManager; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.IPackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.net.Uri; 27 import android.os.RemoteException; 28 import android.os.ServiceManager; 29 import android.os.SystemProperties; 30 import android.os.UserHandle; 31 import android.util.Log; 32 33 import java.io.File; 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.regex.Pattern; 37 38 public class SendBug { 39 40 private static final String LOG_TAG = SendBug.class.getSimpleName(); 41 private static final Pattern EMAIL_REGEX = Pattern.compile( 42 "^[\\w.%+-]+@[\\w.-]+\\.[a-zA-Z]{2,}$"); 43 private static final String SEND_BUG_INTENT_ACTION = "android.testing.SEND_BUG"; 44 main(String[] args)45 public static void main(String[] args) { 46 if (args.length == 1) { 47 new SendBug().run(args[0]); 48 } else if (args.length == 2) { 49 new SendBug().run(args[0], args[1]); 50 } 51 } 52 run(String bugreportPath)53 private void run(String bugreportPath) { 54 run(bugreportPath, null); 55 } 56 run(String bugreportPath, String screenShotPath)57 private void run(String bugreportPath, String screenShotPath) { 58 final File bugreport = new File(bugreportPath); 59 File screenShot = null; 60 if (screenShotPath != null) { 61 screenShot = new File(screenShotPath); 62 } 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 Uri screenshotUri = screenShot != null 68 ? Uri.fromFile(screenShot) : null; 69 intent = getSendMailIntent(bugreportUri, screenshotUri); 70 } 71 if (intent != null) { 72 final IActivityManager am = ActivityManagerNative.getDefault(); 73 if (am == null) { 74 Log.e(LOG_TAG, "Cannot get ActivityManager, is the system running?"); 75 return; 76 } 77 try { 78 am.startActivityAsUser(null, intent, intent.getType(), null, null, 0, 0, 79 null, null, null, UserHandle.USER_CURRENT); 80 } catch (RemoteException e) { 81 // ignore 82 } 83 } else { 84 Log.w(LOG_TAG, "Cannot find account to send bugreport, local path: " 85 + bugreportPath); 86 } 87 } 88 tryBugReporter(Uri bugreportUri)89 private Intent tryBugReporter(Uri bugreportUri) { 90 final Intent intent = new Intent(SEND_BUG_INTENT_ACTION); 91 intent.setData(bugreportUri); 92 final IPackageManager pm = IPackageManager.Stub.asInterface( 93 ServiceManager.getService("package")); 94 if (pm == null) { 95 Log.e(LOG_TAG, "Cannot get PackageManager, is the system running?"); 96 return null; 97 } 98 final List<ResolveInfo> results; 99 try { 100 results = pm.queryIntentActivities(intent, null, 0, 0); 101 } catch (RemoteException e) { 102 return null; 103 } 104 if (results != null && results.size() > 0) { 105 final ResolveInfo info = results.get(0); 106 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 107 intent.setClassName(info.activityInfo.applicationInfo.packageName, 108 info.activityInfo.name); 109 return intent; 110 } else { 111 return null; 112 } 113 } 114 getSendMailIntent(Uri bugreportUri, Uri screenshotUri)115 private Intent getSendMailIntent(Uri bugreportUri, Uri screenshotUri) { 116 final Account sendToAccount = findSendToAccount(); 117 final Intent intent = new Intent(Intent.ACTION_SEND); 118 intent.addCategory(Intent.CATEGORY_DEFAULT); 119 intent.setType("application/octet-stream"); 120 intent.putExtra(Intent.EXTRA_SUBJECT, bugreportUri.getLastPathSegment()); 121 final StringBuilder sb = new StringBuilder(); 122 sb.append(SystemProperties.get("ro.build.description")); 123 sb.append("\n(Sent from BugMailer)"); 124 intent.putExtra(Intent.EXTRA_TEXT, (CharSequence)sb); 125 if (screenshotUri != null) { 126 final ArrayList<Uri> attachments = new ArrayList<Uri>(); 127 attachments.add(bugreportUri); 128 attachments.add(screenshotUri); 129 intent.setAction(Intent.ACTION_SEND_MULTIPLE); 130 intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachments); 131 } else { 132 intent.putExtra(Intent.EXTRA_STREAM, bugreportUri); 133 } 134 if (sendToAccount != null) { 135 intent.putExtra(Intent.EXTRA_EMAIL, new String[]{sendToAccount.name}); 136 return intent; 137 } 138 return null; 139 } 140 findSendToAccount()141 private Account findSendToAccount() { 142 final IAccountManager accountManager = IAccountManager.Stub.asInterface(ServiceManager 143 .getService(Context.ACCOUNT_SERVICE)); 144 if (accountManager == null) { 145 Log.e(LOG_TAG, "Cannot get AccountManager, is the system running?"); 146 return null; 147 } 148 Account[] accounts = null; 149 Account foundAccount = null; 150 String preferredDomain = SystemProperties.get("sendbug.preferred.domain"); 151 if (!preferredDomain.startsWith("@")) { 152 preferredDomain = "@" + preferredDomain; 153 } 154 try { 155 accounts = accountManager.getAccounts(null); 156 } catch (RemoteException e) { 157 // ignore 158 } 159 if (accounts != null) { 160 for (Account account : accounts) { 161 if (EMAIL_REGEX.matcher(account.name).matches()) { 162 if (!preferredDomain.isEmpty()) { 163 // if we have a preferred domain and it matches, return; otherwise keep 164 // looking 165 if (account.name.endsWith(preferredDomain)) { 166 return account; 167 } else { 168 foundAccount = account; 169 } 170 // if we don't have a preferred domain, just return since it looks like 171 // an email address 172 } else { 173 return account; 174 } 175 } 176 } 177 } 178 return foundAccount; 179 } 180 } 181