1 /* 2 * Copyright (C) 2015 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.traceur; 18 19 import android.accounts.Account; 20 import android.accounts.AccountManager; 21 import android.app.Notification; 22 import android.app.NotificationManager; 23 import android.app.PendingIntent; 24 import android.content.Context; 25 import android.support.v4.content.FileProvider; 26 import android.content.Intent; 27 import android.net.Uri; 28 import android.os.SystemProperties; 29 import android.util.Patterns; 30 31 import java.io.File; 32 33 /** 34 * Sends bugreport-y files, adapted from fw/base/packages/Shell's BugreportReceiver. 35 */ 36 public class FileSender { 37 38 private static final String AUTHORITY = "com.android.traceur.files"; 39 40 public static void postNotification(Context context, File file) { 41 // Files are kept on private storage, so turn into Uris that we can 42 // grant temporary permissions for. 43 final Uri traceUri = getUriForFile(context, file); 44 45 Intent sendIntent = buildSendIntent(context, traceUri); 46 sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 47 48 final Notification.Builder builder = 49 new Notification.Builder(context, Receiver.NOTIFICATION_CHANNEL) 50 .setSmallIcon(R.drawable.stat_sys_adb) 51 .setContentTitle(context.getString(R.string.trace_saved)) 52 .setTicker(context.getString(R.string.trace_saved)) 53 .setContentText(context.getString(R.string.tap_to_share)) 54 .setContentIntent(PendingIntent.getActivity( 55 context, traceUri.hashCode(), sendIntent, PendingIntent.FLAG_ONE_SHOT 56 | PendingIntent.FLAG_CANCEL_CURRENT)) 57 .setAutoCancel(true) 58 .setLocalOnly(true) 59 .setColor(context.getColor( 60 com.android.internal.R.color.system_notification_accent_color)); 61 62 NotificationManager.from(context).notify(file.getName(), 0, builder.build()); 63 } 64 65 public static void send(Context context, File file) { 66 // Files are kept on private storage, so turn into Uris that we can 67 // grant temporary permissions for. 68 final Uri traceUri = getUriForFile(context, file); 69 70 Intent sendIntent = buildSendIntent(context, traceUri); 71 sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 72 73 context.startActivity(sendIntent); 74 } 75 76 private static Uri getUriForFile(Context context, File file) { 77 return FileProvider.getUriForFile(context, AUTHORITY, file); 78 } 79 80 /** 81 * Build {@link Intent} that can be used to share the given bugreport. 82 */ 83 private static Intent buildSendIntent(Context context, Uri traceUri) { 84 final Intent intent = new Intent(Intent.ACTION_SEND); 85 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 86 intent.addCategory(Intent.CATEGORY_DEFAULT); 87 intent.setType("application/vnd.android.systrace"); 88 89 intent.putExtra(Intent.EXTRA_SUBJECT, traceUri.getLastPathSegment()); 90 intent.putExtra(Intent.EXTRA_TEXT, SystemProperties.get("ro.build.description")); 91 intent.putExtra(Intent.EXTRA_STREAM, traceUri); 92 93 final Account sendToAccount = findSendToAccount(context); 94 if (sendToAccount != null) { 95 intent.putExtra(Intent.EXTRA_EMAIL, new String[] { sendToAccount.name }); 96 } 97 98 return intent; 99 } 100 101 /** 102 * Find the best matching {@link Account} based on build properties. 103 */ 104 private static Account findSendToAccount(Context context) { 105 final AccountManager am = (AccountManager) context.getSystemService( 106 Context.ACCOUNT_SERVICE); 107 108 String preferredDomain = SystemProperties.get("sendbug.preferred.domain"); 109 if (!preferredDomain.startsWith("@")) { 110 preferredDomain = "@" + preferredDomain; 111 } 112 113 final Account[] accounts = am.getAccounts(); 114 Account foundAccount = null; 115 for (Account account : accounts) { 116 if (Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) { 117 if (!preferredDomain.isEmpty()) { 118 // if we have a preferred domain and it matches, return; otherwise keep 119 // looking 120 if (account.name.endsWith(preferredDomain)) { 121 return account; 122 } else { 123 foundAccount = account; 124 } 125 // if we don't have a preferred domain, just return since it looks like 126 // an email address 127 } else { 128 return account; 129 } 130 } 131 } 132 return foundAccount; 133 } 134 } 135