diff options
author | 2024-03-26 05:42:45 +0000 | |
---|---|---|
committer | 2024-04-04 17:13:01 +0000 | |
commit | 58296be26c1ed331b7f56b27ac2b044a2f716a91 (patch) | |
tree | 052ab858a894ae66c7affdaebf6b720760714552 | |
parent | 3b44bd77e8cccfd7040d817db921b0c351594f1e (diff) |
Allow SystemUI to trigger a bugreport and share with BetterBug from the Record Issue QS Tile.
On the UI side of things, I added a switch to allow users to choose between sharing traces directly, or via bugreport.
Bug: 305049544
Test: Locally verified that this worked on my device.
https://b.corp.google.com/issues/331268833#comment5 is an example of
this working properly.
Screenshot: https://photos.app.goo.gl/LmNFhuWLCPyEygDw6
Flag: ACONFIG record_issue_qs_tile DEVELOPMENT
Change-Id: I01547162989b7f909fe849aa21ffbb604905dfab
11 files changed, 154 insertions, 24 deletions
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 85611e8303bf..53f72223a880 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -499,6 +499,7 @@ interface IActivityManager { in String shareDescription); void requestInteractiveBugReport(); + void requestBugReportWithExtraAttachment(in Uri extraAttachment); void requestFullBugReport(); void requestRemoteBugReport(long nonce); boolean launchBugReportHandlerApp(); diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 42952de1b2b9..5ac0e449b8e1 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -50,7 +50,6 @@ import android.os.AsyncTask; import android.os.Binder; import android.os.BugreportManager; import android.os.BugreportManager.BugreportCallback; -import android.os.BugreportManager.BugreportCallback.BugreportErrorCode; import android.os.BugreportParams; import android.os.Bundle; import android.os.FileUtils; @@ -169,6 +168,8 @@ public class BugreportProgressService extends Service { static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION"; static final String EXTRA_ORIGINAL_INTENT = "android.intent.extra.ORIGINAL_INTENT"; static final String EXTRA_INFO = "android.intent.extra.INFO"; + static final String EXTRA_EXTRA_ATTACHMENT_URI = + "android.intent.extra.EXTRA_ATTACHMENT_URI"; private static final int MSG_SERVICE_COMMAND = 1; private static final int MSG_DELAYED_SCREENSHOT = 2; @@ -634,9 +635,10 @@ public class BugreportProgressService extends Service { long nonce = intent.getLongExtra(EXTRA_BUGREPORT_NONCE, 0); String baseName = getBugreportBaseName(bugreportType); String name = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); + Uri extraAttachment = intent.getParcelableExtra(EXTRA_EXTRA_ATTACHMENT_URI, Uri.class); - BugreportInfo info = new BugreportInfo(mContext, baseName, name, - shareTitle, shareDescription, bugreportType, mBugreportsDir, nonce); + BugreportInfo info = new BugreportInfo(mContext, baseName, name, shareTitle, + shareDescription, bugreportType, mBugreportsDir, nonce, extraAttachment); synchronized (mLock) { if (info.bugreportFile.exists()) { Log.e(TAG, "Failed to start bugreport generation, the requested bugreport file " @@ -1184,6 +1186,10 @@ public class BugreportProgressService extends Service { clipData.addItem(new ClipData.Item(null, null, null, screenshotUri)); attachments.add(screenshotUri); } + if (info.extraAttachment != null) { + clipData.addItem(new ClipData.Item(null, null, null, info.extraAttachment)); + attachments.add(info.extraAttachment); + } intent.setClipData(clipData); intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachments); @@ -2042,6 +2048,9 @@ public class BugreportProgressService extends Service { */ final long nonce; + @Nullable + public Uri extraAttachment = null; + private final Object mLock = new Object(); /** @@ -2049,7 +2058,8 @@ public class BugreportProgressService extends Service { */ BugreportInfo(Context context, String baseName, String name, @Nullable String shareTitle, @Nullable String shareDescription, - @BugreportParams.BugreportMode int type, File bugreportsDir, long nonce) { + @BugreportParams.BugreportMode int type, File bugreportsDir, long nonce, + @Nullable Uri extraAttachment) { this.context = context; this.name = this.initialName = name; this.shareTitle = shareTitle == null ? "" : shareTitle; @@ -2058,6 +2068,7 @@ public class BugreportProgressService extends Service { this.nonce = nonce; this.baseName = baseName; this.bugreportFile = new File(bugreportsDir, getFileName(this, ".zip")); + this.extraAttachment = extraAttachment; } void createBugreportFile() { diff --git a/packages/SystemUI/res/drawable/ic_bugreport.xml b/packages/SystemUI/res/drawable/ic_bugreport.xml new file mode 100644 index 000000000000..ed1c6c723543 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_bugreport.xml @@ -0,0 +1,32 @@ +<!-- + ~ Copyright (C) 2024 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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="#FF000000" + android:pathData="M20,10V8h-2.81c-0.45,-0.78 -1.07,-1.46 -1.82,-1.96L17,4.41L15.59,3l-2.17,2.17c-0.03,-0.01 -0.05,-0.01 -0.08,-0.01c-0.16,-0.04 -0.32,-0.06 -0.49,-0.09c-0.06,-0.01 -0.11,-0.02 -0.17,-0.03C12.46,5.02 12.23,5 12,5h0c-0.49,0 -0.97,0.07 -1.42,0.18l0.02,-0.01L8.41,3L7,4.41l1.62,1.63l0.01,0C7.88,6.54 7.26,7.22 6.81,8H4v2h2.09C6.03,10.33 6,10.66 6,11v1H4v2h2v1c0,0.34 0.04,0.67 0.09,1H4v2h2.81c1.04,1.79 2.97,3 5.19,3h0c2.22,0 4.15,-1.21 5.19,-3H20v-2h-2.09l0,0c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1l0,0H20zM16,15c0,2.21 -1.79,4 -4,4c-2.21,0 -4,-1.79 -4,-4v-4c0,-2.21 1.79,-4 4,-4h0c2.21,0 4,1.79 4,4V15z"/> + <path + android:fillColor="#FF000000" + android:pathData="M10,14h4v2h-4z"/> + <path + android:fillColor="#FF000000" + android:pathData="M10,10h4v2h-4z"/> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/record_issue_dialog.xml b/packages/SystemUI/res/layout/record_issue_dialog.xml index 53ad9f157a2e..30d7b0ae739a 100644 --- a/packages/SystemUI/res/layout/record_issue_dialog.xml +++ b/packages/SystemUI/res/layout/record_issue_dialog.xml @@ -54,6 +54,7 @@ android:layout_weight="0" android:src="@drawable/ic_screenrecord" app:tint="?androidprv:attr/materialColorOnSurface" + android:importantForAccessibility="no" android:layout_gravity="center" android:layout_marginEnd="@dimen/screenrecord_option_padding" /> @@ -78,4 +79,44 @@ android:layout_weight="0" android:contentDescription="@string/quick_settings_screen_record_label" /> </LinearLayout> + + <!-- Bug Report Switch --> + <LinearLayout + android:id="@+id/bugreport_switch_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/qqs_layout_margin_top" + android:orientation="horizontal"> + + <ImageView + android:layout_width="@dimen/screenrecord_option_icon_size" + android:layout_height="@dimen/screenrecord_option_icon_size" + android:layout_weight="0" + android:src="@drawable/ic_bugreport" + app:tint="?androidprv:attr/materialColorOnSurface" + android:importantForAccessibility="no" + android:layout_gravity="center" + android:layout_marginEnd="@dimen/screenrecord_option_padding" /> + + <TextView + android:layout_width="0dp" + android:layout_height="wrap_content" + android:minHeight="@dimen/screenrecord_option_icon_size" + android:layout_weight="1" + android:layout_gravity="fill_vertical" + android:gravity="center" + android:text="@string/qs_record_issue_bug_report" + android:textAppearance="@style/TextAppearance.Dialog.Body.Message" + android:importantForAccessibility="no"/> + + <Switch + android:id="@+id/bugreport_switch" + android:layout_width="wrap_content" + android:minHeight="@dimen/screenrecord_option_icon_size" + android:layout_height="wrap_content" + android:gravity="center" + android:layout_gravity="fill_vertical" + android:layout_weight="0" + android:contentDescription="@string/qs_record_issue_bug_report" /> + </LinearLayout> </LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 71353b6774af..581ffd4158db 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -872,6 +872,8 @@ <string name="qs_record_issue_start">Start</string> <!-- QuickSettings: Text to prompt the user to stop an ongoing recording [CHAR LIMIT=20] --> <string name="qs_record_issue_stop">Stop</string> + <!-- QuickSettings: Should user take a bugreport or only share trace files [CHAR LIMIT=20] --> + <string name="qs_record_issue_bug_report">Bug Report</string> <!-- QuickSettings: Issue Type Drop down options in Record Issue Start Dialog [CHAR LIMIT=50] --> <string name="qs_record_issue_dropdown_header">What part of your device experience was affected?</string> diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt index 5e4919d44f23..4d34a869002a 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt @@ -16,6 +16,7 @@ package com.android.systemui.recordissue +import android.app.IActivityManager import android.app.NotificationManager import android.content.Context import android.content.Intent @@ -51,7 +52,7 @@ class IssueRecordingService @Inject constructor( controller: RecordingController, - @LongRunning executor: Executor, + @LongRunning private val bgExecutor: Executor, @Main handler: Handler, uiEventLogger: UiEventLogger, notificationManager: NotificationManager, @@ -60,10 +61,12 @@ constructor( private val dialogTransitionAnimator: DialogTransitionAnimator, private val panelInteractor: PanelInteractor, private val issueRecordingState: IssueRecordingState, + private val iActivityManager: IActivityManager, + private val launcherApps: LauncherApps, ) : RecordingService( controller, - executor, + bgExecutor, handler, uiEventLogger, notificationManager, @@ -103,12 +106,26 @@ constructor( // ViewCapture needs to save it's data before it is disabled, or else the data will // be lost. This is expected to change in the near future, and when that happens // this line should be removed. - getSystemService(LauncherApps::class.java)?.saveViewCaptureData() + launcherApps.saveViewCaptureData() TraceUtils.traceStop(contentResolver) issueRecordingState.isRecording = false } ACTION_SHARE -> { - shareRecording(intent) + bgExecutor.execute { + mNotificationManager.cancelAsUser( + null, + mNotificationId, + UserHandle(mUserContextTracker.userContext.userId) + ) + + val screenRecording = intent.getParcelableExtra(EXTRA_PATH, Uri::class.java) + if (issueRecordingState.takeBugReport) { + iActivityManager.requestBugReportWithExtraAttachment(screenRecording) + } else { + shareRecording(screenRecording) + } + } + dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations() panelInteractor.collapsePanels() @@ -122,23 +139,17 @@ constructor( return super.onStartCommand(intent, flags, startId) } - private fun shareRecording(intent: Intent) { + private fun shareRecording(screenRecording: Uri?) { val sharableUri: Uri = zipAndPackageRecordings( TraceUtils.traceDump(contentResolver, TRACE_FILE_NAME).get(), - intent.getStringExtra(EXTRA_PATH) + screenRecording ) ?: return val sendIntent = FileSender.buildSendIntent(this, listOf(sharableUri)) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - mNotificationManager.cancelAsUser( - null, - mNotificationId, - UserHandle(mUserContextTracker.userContext.userId) - ) - // TODO: Debug why the notification shade isn't closing upon starting the BetterBug activity mKeyguardDismissUtil.executeWhenUnlocked( { @@ -150,7 +161,7 @@ constructor( ) } - private fun zipAndPackageRecordings(traceFiles: List<File>, screenRecordingUri: String?): Uri? { + private fun zipAndPackageRecordings(traceFiles: List<File>, screenRecording: Uri?): Uri? { try { externalCacheDir?.mkdirs() val outZip: File = File.createTempFile(TEMP_FILE_PREFIX, ZIP_SUFFIX, externalCacheDir) @@ -160,8 +171,8 @@ constructor( Files.copy(file.toPath(), os) os.closeEntry() } - if (screenRecordingUri != null) { - contentResolver.openInputStream(Uri.parse(screenRecordingUri))?.use { + if (screenRecording != null) { + contentResolver.openInputStream(screenRecording)?.use { os.putNextEntry(ZipEntry(SCREEN_RECORDING_ZIP_LABEL)) it.transferTo(os) os.closeEntry() @@ -215,7 +226,7 @@ constructor( fun getStartIntent( context: Context, screenRecord: Boolean, - winscopeTracing: Boolean + winscopeTracing: Boolean, ): Intent = Intent(context, IssueRecordingService::class.java) .setAction(ACTION_START) diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt index 394c5c2775a4..12ed06d75ce3 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt @@ -25,6 +25,8 @@ class IssueRecordingState @Inject constructor() { private val listeners = CopyOnWriteArrayList<Runnable>() + var takeBugReport: Boolean = false + var isRecording = false set(value) { field = value diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt index dab61fa54908..68b88362427a 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt @@ -63,6 +63,7 @@ constructor( private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger, private val userFileManager: UserFileManager, private val screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate, + private val issueRecordingState: IssueRecordingState, @Assisted private val onStarted: Consumer<IssueRecordingConfig>, ) : SystemUIDialog.Delegate { @@ -74,6 +75,7 @@ constructor( } @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch + @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var bugReportSwitch: Switch private lateinit var issueTypeButton: Button @MainThread @@ -86,6 +88,7 @@ constructor( setPositiveButton( R.string.qs_record_issue_start, { _, _ -> + issueRecordingState.takeBugReport = bugReportSwitch.isChecked onStarted.accept( IssueRecordingConfig( screenRecordSwitch.isChecked, @@ -113,6 +116,7 @@ constructor( bgExecutor.execute { onScreenRecordSwitchClicked() } } } + bugReportSwitch = requireViewById(R.id.bugreport_switch) val startButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE) issueTypeButton = requireViewById(R.id.issue_type_button) issueTypeButton.setOnClickListener { diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index b2c01e180ec4..cbb61b37b7a4 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -206,7 +206,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList break; case ACTION_SHARE: - Uri shareUri = Uri.parse(intent.getStringExtra(EXTRA_PATH)); + Uri shareUri = intent.getParcelableExtra(EXTRA_PATH, Uri.class); Intent shareIntent = new Intent(Intent.ACTION_SEND) .setType("video/mp4") @@ -356,7 +356,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList PendingIntent.getService( this, REQUEST_CODE, - getShareIntent(this, uri != null ? uri.toString() : null), + getShareIntent(this, uri), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) .build(); @@ -512,7 +512,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList return new Intent(context, this.getClass()).setAction(ACTION_STOP_NOTIF); } - private Intent getShareIntent(Context context, String path) { + private Intent getShareIntent(Context context, Uri path) { return new Intent(context, this.getClass()).setAction(ACTION_SHARE) .putExtra(EXTRA_PATH, path); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt index 9104f8e43892..6846c7227d9c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt @@ -88,6 +88,7 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() { private lateinit var dialog: SystemUIDialog private lateinit var factory: SystemUIDialog.Factory private lateinit var latch: CountDownLatch + private var issueRecordingState = IssueRecordingState() @Before fun setup() { @@ -128,6 +129,7 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() { mediaProjectionMetricsLogger, userFileManager, screenCaptureDisabledDialogDelegate, + issueRecordingState, ) { latch.countDown() } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index a66c23f7cf63..0945d5d9e01f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -635,7 +635,8 @@ public class ActivityManagerService extends IActivityManager.Stub static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION"; static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE"; static final String EXTRA_BUGREPORT_NONCE = "android.intent.extra.BUGREPORT_NONCE"; - + static final String EXTRA_EXTRA_ATTACHMENT_URI = + "android.intent.extra.EXTRA_ATTACHMENT_URI"; /** * It is now required for apps to explicitly set either * {@link android.content.Context#RECEIVER_EXPORTED} or @@ -7654,6 +7655,16 @@ public class ActivityManagerService extends IActivityManager.Stub */ public void requestBugReportWithDescription(@Nullable String shareTitle, @Nullable String shareDescription, int bugreportType, long nonce) { + requestBugReportWithDescription(shareTitle, shareDescription, bugreportType, nonce, null); + } + + /** + * Takes a bugreport using bug report API ({@code BugreportManager}) which gets + * triggered by sending a broadcast to Shell. Optionally adds an extra attachment. + */ + public void requestBugReportWithDescription(@Nullable String shareTitle, + @Nullable String shareDescription, int bugreportType, long nonce, + @Nullable Uri extraAttachment) { String type = null; switch (bugreportType) { case BugreportParams.BUGREPORT_MODE_FULL: @@ -7708,6 +7719,10 @@ public class ActivityManagerService extends IActivityManager.Stub triggerShellBugreport.setPackage(SHELL_APP_PACKAGE); triggerShellBugreport.putExtra(EXTRA_BUGREPORT_TYPE, bugreportType); triggerShellBugreport.putExtra(EXTRA_BUGREPORT_NONCE, nonce); + if (extraAttachment != null) { + triggerShellBugreport.putExtra(EXTRA_EXTRA_ATTACHMENT_URI, extraAttachment); + triggerShellBugreport.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + } triggerShellBugreport.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); triggerShellBugreport.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); if (shareTitle != null) { @@ -7761,6 +7776,15 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * Takes an interactive bugreport with a progress notification. Also attaches given file uri. + */ + @Override + public void requestBugReportWithExtraAttachment(@NonNull Uri extraAttachment) { + requestBugReportWithDescription(null, null, BugreportParams.BUGREPORT_MODE_INTERACTIVE, 0L, + extraAttachment); + } + + /** * Takes an interactive bugreport with a progress notification. Also, shows the given title and * description on the final share notification */ |