diff options
author | Jeff Sharkey <jsharkey@android.com> | 2013-03-14 15:48:28 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2013-03-14 15:48:28 +0000 |
commit | 2044158fe12fada339762825d0b4ec3961a14d34 (patch) | |
tree | b8018e5786609f7114c8cd49971d1f531b8a7538 /packages | |
parent | addabb23586cffceeaf97ddc579d292151d77fee (diff) | |
parent | 5d7b4a4448e0f29f36ba27aee2de06ce25ff2ee1 (diff) | |
download | frameworks_base-2044158fe12fada339762825d0b4ec3961a14d34.zip frameworks_base-2044158fe12fada339762825d0b4ec3961a14d34.tar.gz frameworks_base-2044158fe12fada339762825d0b4ec3961a14d34.tar.bz2 |
am 5d7b4a44: am 34f37e74: Merge "Handle finished bugreports, share from private." into jb-mr2-dev
* commit '5d7b4a4448e0f29f36ba27aee2de06ce25ff2ee1':
Handle finished bugreports, share from private.
Diffstat (limited to 'packages')
-rw-r--r-- | packages/Shell/Android.mk | 2 | ||||
-rw-r--r-- | packages/Shell/AndroidManifest.xml | 29 | ||||
-rw-r--r-- | packages/Shell/res/layout/confirm_repeat.xml | 37 | ||||
-rw-r--r-- | packages/Shell/res/values/strings.xml | 10 | ||||
-rw-r--r-- | packages/Shell/res/xml/file_provider_paths.xml | 3 | ||||
-rw-r--r-- | packages/Shell/src/com/android/shell/BugreportPrefs.java | 45 | ||||
-rw-r--r-- | packages/Shell/src/com/android/shell/BugreportReceiver.java | 198 | ||||
-rw-r--r-- | packages/Shell/src/com/android/shell/BugreportWarningActivity.java | 77 |
8 files changed, 399 insertions, 2 deletions
diff --git a/packages/Shell/Android.mk b/packages/Shell/Android.mk index f993ab5..fc4c0f5 100644 --- a/packages/Shell/Android.mk +++ b/packages/Shell/Android.mk @@ -5,6 +5,8 @@ LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 + LOCAL_PACKAGE_NAME := Shell LOCAL_CERTIFICATE := platform diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index b42db45..ffb4c20 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -68,7 +68,32 @@ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.MANAGE_USERS" /> <uses-permission android:name="android.permission.BLUETOOTH_STACK" /> - - <application android:hasCode="false" android:label="@string/app_label"> + <uses-permission android:name="android.permission.GET_ACCOUNTS" /> + + <application android:label="@string/app_label"> + <provider + android:name="android.support.v4.content.FileProvider" + android:authorities="com.android.shell" + android:grantUriPermissions="true" + android:exported="false"> + <meta-data + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/file_provider_paths" /> + </provider> + + <activity + android:name=".BugreportWarningActivity" + android:theme="@*android:style/Theme.Holo.Dialog.Alert" + android:finishOnCloseSystemDialogs="true" + android:excludeFromRecents="true" + android:exported="false" /> + + <receiver + android:name=".BugreportReceiver" + android:permission="android.permission.DUMP"> + <intent-filter> + <action android:name="android.intent.action.BUGREPORT_FINISHED" /> + </intent-filter> + </receiver> </application> </manifest> diff --git a/packages/Shell/res/layout/confirm_repeat.xml b/packages/Shell/res/layout/confirm_repeat.xml new file mode 100644 index 0000000..dc250d6 --- /dev/null +++ b/packages/Shell/res/layout/confirm_repeat.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="16dip" + android:paddingEnd="16dip" + android:paddingTop="8dip" + android:paddingBottom="16dip" + android:orientation="vertical"> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/bugreport_confirm" + android:paddingBottom="16dip" + style="?android:attr/textAppearanceMedium" /> + <CheckBox + android:id="@android:id/checkbox" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/bugreport_confirm_repeat" /> +</LinearLayout> diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml index 50610d5..e5606c7 100644 --- a/packages/Shell/res/values/strings.xml +++ b/packages/Shell/res/values/strings.xml @@ -16,4 +16,14 @@ <resources> <string name="app_label">Shell</string> + + <!-- Title of notification indicating a bugreport has been successfully captured. [CHAR LIMIT=50] --> + <string name="bugreport_finished_title">Bug report captured</string> + <!-- Text of notification indicating that touching will share the captured bugreport. [CHAR LIMIT=100] --> + <string name="bugreport_finished_text">Touch to share your bug report</string> + + <!-- Body of dialog informing user about contents of a bugreport. [CHAR LIMIT=NONE] --> + <string name="bugreport_confirm">Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people you trust.</string> + <!-- Checkbox that indicates this dialog should be shown again when the next bugreport is taken. [CHAR LIMIT=50] --> + <string name="bugreport_confirm_repeat">Show this message next time</string> </resources> diff --git a/packages/Shell/res/xml/file_provider_paths.xml b/packages/Shell/res/xml/file_provider_paths.xml new file mode 100644 index 0000000..225c757 --- /dev/null +++ b/packages/Shell/res/xml/file_provider_paths.xml @@ -0,0 +1,3 @@ +<paths xmlns:android="http://schemas.android.com/apk/res/android"> + <files-path name="bugreports" path="bugreports/" /> +</paths> diff --git a/packages/Shell/src/com/android/shell/BugreportPrefs.java b/packages/Shell/src/com/android/shell/BugreportPrefs.java new file mode 100644 index 0000000..3748e89 --- /dev/null +++ b/packages/Shell/src/com/android/shell/BugreportPrefs.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.shell; + +import android.content.Context; +import android.content.SharedPreferences; + +/** + * Preferences related to bug reports. + */ +public class BugreportPrefs { + private static final String PREFS_BUGREPORT = "bugreports"; + + private static final String KEY_WARNING_STATE = "warning-state"; + + public static final int STATE_UNKNOWN = 0; + public static final int STATE_SHOW = 1; + public static final int STATE_HIDE = 2; + + public static int getWarningState(Context context, int def) { + final SharedPreferences prefs = context.getSharedPreferences( + PREFS_BUGREPORT, Context.MODE_PRIVATE); + return prefs.getInt(KEY_WARNING_STATE, def); + } + + public static void setWarningState(Context context, int value) { + final SharedPreferences prefs = context.getSharedPreferences( + PREFS_BUGREPORT, Context.MODE_PRIVATE); + prefs.edit().putInt(KEY_WARNING_STATE, value).apply(); + } +} diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java new file mode 100644 index 0000000..3b1ebf4 --- /dev/null +++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.shell; + +import static com.android.shell.BugreportPrefs.STATE_SHOW; +import static com.android.shell.BugreportPrefs.getWarningState; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.SystemProperties; +import android.support.v4.content.FileProvider; +import android.util.Log; +import android.util.Patterns; + +import com.google.android.collect.Lists; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; + +/** + * Receiver that handles finished bugreports, usually by attaching them to an + * {@link Intent#ACTION_SEND}. + */ +public class BugreportReceiver extends BroadcastReceiver { + private static final String TAG = "Shell"; + + private static final String AUTHORITY = "com.android.shell"; + + private static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT"; + private static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT"; + + /** + * Number of bugreports to retain before deleting the oldest; 4 reports and + * 4 screenshots are roughly 17MB of disk space. + */ + private static final int NUM_OLD_FILES = 8; + + @Override + public void onReceive(Context context, Intent intent) { + final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT); + final File screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT); + + // Files are kept on private storage, so turn into Uris that we can + // grant temporary permissions for. + final Uri bugreportUri = FileProvider.getUriForFile(context, AUTHORITY, bugreportFile); + final Uri screenshotUri = FileProvider.getUriForFile(context, AUTHORITY, screenshotFile); + + Intent sendIntent = buildSendIntent(context, bugreportUri, screenshotUri); + Intent notifIntent; + + // Send through warning dialog by default + if (getWarningState(context, STATE_SHOW) == STATE_SHOW) { + notifIntent = buildWarningIntent(context, sendIntent); + } else { + notifIntent = sendIntent; + } + notifIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + final Notification.Builder builder = new Notification.Builder(context); + builder.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb); + builder.setContentTitle(context.getString(R.string.bugreport_finished_title)); + builder.setContentText(context.getString(R.string.bugreport_finished_text)); + builder.setContentIntent(PendingIntent.getActivity( + context, 0, notifIntent, PendingIntent.FLAG_CANCEL_CURRENT)); + builder.setAutoCancel(true); + NotificationManager.from(context).notify(TAG, 0, builder.build()); + + // Clean up older bugreports in background + final PendingResult result = goAsync(); + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... params) { + deleteOlderFiles(bugreportFile.getParentFile(), NUM_OLD_FILES); + result.finish(); + return null; + } + }.execute(); + } + + private static Intent buildWarningIntent(Context context, Intent sendIntent) { + final Intent intent = new Intent(context, BugreportWarningActivity.class); + intent.putExtra(Intent.EXTRA_INTENT, sendIntent); + return intent; + } + + /** + * Build {@link Intent} that can be used to share the given bugreport. + */ + private static Intent buildSendIntent(Context context, Uri bugreportUri, Uri screenshotUri) { + final Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setType("application/vnd.android.bugreport"); + + intent.putExtra(Intent.EXTRA_SUBJECT, bugreportUri.getLastPathSegment()); + intent.putExtra(Intent.EXTRA_TEXT, SystemProperties.get("ro.build.description")); + + final ArrayList<Uri> attachments = Lists.newArrayList(bugreportUri, screenshotUri); + intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachments); + + final Account sendToAccount = findSendToAccount(context); + if (sendToAccount != null) { + intent.putExtra(Intent.EXTRA_EMAIL, new String[] { sendToAccount.name }); + } + + return intent; + } + + /** + * Find the best matching {@link Account} based on build properties. + */ + private static Account findSendToAccount(Context context) { + final AccountManager am = (AccountManager) context.getSystemService( + Context.ACCOUNT_SERVICE); + + String preferredDomain = SystemProperties.get("sendbug.preferred.domain"); + if (!preferredDomain.startsWith("@")) { + preferredDomain = "@" + preferredDomain; + } + + final Account[] accounts = am.getAccounts(); + Account foundAccount = null; + for (Account account : accounts) { + if (Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) { + if (!preferredDomain.isEmpty()) { + // if we have a preferred domain and it matches, return; otherwise keep + // looking + if (account.name.endsWith(preferredDomain)) { + return account; + } else { + foundAccount = account; + } + // if we don't have a preferred domain, just return since it looks like + // an email address + } else { + return account; + } + } + } + return foundAccount; + } + + /** + * Delete the oldest files in given directory until only the requested + * number remain. + */ + private static void deleteOlderFiles(File dir, int retainNum) { + final File[] files = dir.listFiles(); + if (files == null) return; + + Arrays.sort(files, new ModifiedComparator()); + for (int i = retainNum; i < files.length; i++) { + Log.d(TAG, "Deleting old file " + files[i]); + files[i].delete(); + } + } + + private static class ModifiedComparator implements Comparator<File> { + @Override + public int compare(File lhs, File rhs) { + return (int) (rhs.lastModified() - lhs.lastModified()); + } + } + + private static File getFileExtra(Intent intent, String key) { + final String path = intent.getStringExtra(key); + if (path != null) { + return new File(path); + } else { + return null; + } + } + +} diff --git a/packages/Shell/src/com/android/shell/BugreportWarningActivity.java b/packages/Shell/src/com/android/shell/BugreportWarningActivity.java new file mode 100644 index 0000000..a1d879a --- /dev/null +++ b/packages/Shell/src/com/android/shell/BugreportWarningActivity.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.shell; + +import static com.android.shell.BugreportPrefs.STATE_HIDE; +import static com.android.shell.BugreportPrefs.STATE_SHOW; +import static com.android.shell.BugreportPrefs.STATE_UNKNOWN; +import static com.android.shell.BugreportPrefs.getWarningState; +import static com.android.shell.BugreportPrefs.setWarningState; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.widget.CheckBox; + +import com.android.internal.app.AlertActivity; +import com.android.internal.app.AlertController; + +/** + * Dialog that warns about contents of a bugreport. + */ +public class BugreportWarningActivity extends AlertActivity + implements DialogInterface.OnClickListener { + + private Intent mSendIntent; + private CheckBox mConfirmRepeat; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mSendIntent = getIntent().getParcelableExtra(Intent.EXTRA_INTENT); + + // We need to touch the extras to unpack them so they get migrated to + // ClipData correctly. + mSendIntent.hasExtra(Intent.EXTRA_STREAM); + + final AlertController.AlertParams ap = mAlertParams; + ap.mView = LayoutInflater.from(this).inflate(R.layout.confirm_repeat, null); + ap.mPositiveButtonText = getString(android.R.string.ok); + ap.mNegativeButtonText = getString(android.R.string.cancel); + ap.mPositiveButtonListener = this; + ap.mNegativeButtonListener = this; + + mConfirmRepeat = (CheckBox) ap.mView.findViewById(android.R.id.checkbox); + mConfirmRepeat.setChecked(getWarningState(this, STATE_UNKNOWN) == STATE_SHOW); + + setupAlert(); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == AlertDialog.BUTTON_POSITIVE) { + // Remember confirm state, and launch target + setWarningState(this, mConfirmRepeat.isChecked() ? STATE_SHOW : STATE_HIDE); + startActivity(mSendIntent); + } + + finish(); + } +} |