Revert^2 "Record an issue from the Record Issue QS tile."
This reverts commit 26e6cc9ec782d183ef564cdad5f14b59fa9db3e9.
Reason for revert: Fix was pushed here: ag/26503607
Change-Id: Ice8dd2308f9a106dc7447bcf4e4d2d23b41bcea0
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 04cb88d..34b4beea 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -155,6 +155,7 @@
"jsr330",
"lottie",
"LowLightDreamLib",
+ "TraceurCommon",
"motion_tool_lib",
"notification_flags_lib",
"PlatformComposeCore",
@@ -301,6 +302,7 @@
"androidx.compose.material_material-icons-extended",
"androidx.activity_activity-compose",
"androidx.compose.animation_animation-graphics",
+ "TraceurCommon",
],
}
diff --git a/packages/SystemUI/res/xml/fileprovider.xml b/packages/SystemUI/res/xml/fileprovider.xml
index b67378e..71cc05d 100644
--- a/packages/SystemUI/res/xml/fileprovider.xml
+++ b/packages/SystemUI/res/xml/fileprovider.xml
@@ -19,4 +19,5 @@
<cache-path name="leak" path="leak/"/>
<external-path name="screenrecord" path="."/>
<cache-path name="multi_user" path="multi_user/" />
-</paths>
\ No newline at end of file
+ <root-path name="traces" path="/data/local/traces"/>
+</paths>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index 20f3c4d..bd66843 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -103,14 +103,27 @@
public override fun handleClick(view: View?) {
if (isRecording) {
isRecording = false
- stopScreenRecord()
+ stopIssueRecordingService()
} else {
mUiHandler.post { showPrompt(view) }
}
refreshState()
}
- private fun stopScreenRecord() =
+ private fun startIssueRecordingService(screenRecord: Boolean, winscopeTracing: Boolean) =
+ PendingIntent.getForegroundService(
+ userContextProvider.userContext,
+ RecordingService.REQUEST_CODE,
+ IssueRecordingService.getStartIntent(
+ userContextProvider.userContext,
+ screenRecord,
+ winscopeTracing
+ ),
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
+
+ private fun stopIssueRecordingService() =
PendingIntent.getService(
userContextProvider.userContext,
RecordingService.REQUEST_CODE,
@@ -124,6 +137,7 @@
delegateFactory
.create {
isRecording = true
+ startIssueRecordingService(it.screenRecord, it.winscopeTracing)
refreshState()
}
.createDialog()
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
new file mode 100644
index 0000000..bb3b654
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.recordissue
+
+data class IssueRecordingConfig(val screenRecord: Boolean, val winscopeTracing: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
index f487258..55d7f8e 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
@@ -20,7 +20,9 @@
import android.content.Context
import android.content.Intent
import android.content.res.Resources
+import android.net.Uri
import android.os.Handler
+import android.os.UserHandle
import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.qualifiers.LongRunning
import com.android.systemui.dagger.qualifiers.Main
@@ -30,6 +32,8 @@
import com.android.systemui.screenrecord.RecordingServiceStrings
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
+import com.android.traceur.FileSender
+import com.android.traceur.TraceUtils
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -60,9 +64,89 @@
override fun provideRecordingServiceStrings(): RecordingServiceStrings = IrsStrings(resources)
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ when (intent?.action) {
+ ACTION_START -> {
+ TraceUtils.traceStart(
+ contentResolver,
+ DEFAULT_TRACE_TAGS,
+ DEFAULT_BUFFER_SIZE,
+ DEFAULT_IS_INCLUDING_WINSCOPE,
+ DEFAULT_IS_INCLUDING_APP_TRACE,
+ DEFAULT_IS_LONG_TRACE,
+ DEFAULT_ATTACH_TO_BUGREPORT,
+ DEFAULT_MAX_TRACE_SIZE,
+ DEFAULT_MAX_TRACE_DURATION_IN_MINUTES
+ )
+ if (!intent.getBooleanExtra(EXTRA_SCREEN_RECORD, false)) {
+ // If we don't want to record the screen, the ACTION_SHOW_START_NOTIF action
+ // will circumvent the RecordingService's screen recording start code.
+ return super.onStartCommand(Intent(ACTION_SHOW_START_NOTIF), flags, startId)
+ }
+ }
+ ACTION_STOP,
+ ACTION_STOP_NOTIF -> {
+ TraceUtils.traceStop(contentResolver)
+ }
+ ACTION_SHARE -> {
+ shareRecording(intent)
+
+ // Unlike all other actions, action_share has different behavior for the screen
+ // recording qs tile than it does for the record issue qs tile. Return sticky to
+ // avoid running any of the base class' code for this action.
+ return START_STICKY
+ }
+ else -> {}
+ }
+ return super.onStartCommand(intent, flags, startId)
+ }
+
+ private fun shareRecording(intent: Intent) {
+ val files = TraceUtils.traceDump(contentResolver, TRACE_FILE_NAME).get()
+ val traceUris: MutableList<Uri> = FileSender.getUriForFiles(this, files, AUTHORITY)
+
+ if (
+ intent.hasExtra(EXTRA_PATH) && intent.getStringExtra(EXTRA_PATH)?.isNotEmpty() == true
+ ) {
+ traceUris.add(Uri.parse(intent.getStringExtra(EXTRA_PATH)))
+ }
+
+ val sendIntent =
+ FileSender.buildSendIntent(this, traceUris).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
+ if (mNotificationId != NOTIF_BASE_ID) {
+ val currentUserId = mUserContextTracker.userContext.userId
+ mNotificationManager.cancelAsUser(null, mNotificationId, UserHandle(currentUserId))
+ }
+
+ // TODO: Debug why the notification shade isn't closing upon starting the BetterBug activity
+ mKeyguardDismissUtil.executeWhenUnlocked(
+ {
+ startActivity(sendIntent)
+ false
+ },
+ false,
+ false
+ )
+ }
+
companion object {
private const val TAG = "IssueRecordingService"
private const val CHANNEL_ID = "issue_record"
+ private const val EXTRA_SCREEN_RECORD = "extra_screenRecord"
+ private const val EXTRA_WINSCOPE_TRACING = "extra_winscopeTracing"
+
+ private val DEFAULT_TRACE_TAGS = listOf<String>()
+ private const val DEFAULT_BUFFER_SIZE = 16384
+ private const val DEFAULT_IS_INCLUDING_WINSCOPE = true
+ private const val DEFAULT_IS_LONG_TRACE = false
+ private const val DEFAULT_IS_INCLUDING_APP_TRACE = true
+ private const val DEFAULT_ATTACH_TO_BUGREPORT = true
+ private const val DEFAULT_MAX_TRACE_SIZE = 10240
+ private const val DEFAULT_MAX_TRACE_DURATION_IN_MINUTES = 30
+
+ private val TRACE_FILE_NAME = TraceUtils.getOutputFilename(TraceUtils.RecordingType.TRACE)
+ private const val AUTHORITY = "com.android.systemui.fileprovider"
/**
* Get an intent to stop the issue recording service.
@@ -71,7 +155,7 @@
* @return
*/
fun getStopIntent(context: Context): Intent =
- Intent(context, RecordingService::class.java)
+ Intent(context, IssueRecordingService::class.java)
.setAction(ACTION_STOP)
.putExtra(Intent.EXTRA_USER_HANDLE, context.userId)
@@ -80,8 +164,15 @@
*
* @param context Context from the requesting activity
*/
- fun getStartIntent(context: Context): Intent =
- Intent(context, RecordingService::class.java).setAction(ACTION_START)
+ fun getStartIntent(
+ context: Context,
+ screenRecord: Boolean,
+ winscopeTracing: Boolean
+ ): Intent =
+ Intent(context, IssueRecordingService::class.java)
+ .setAction(ACTION_START)
+ .putExtra(EXTRA_SCREEN_RECORD, screenRecord)
+ .putExtra(EXTRA_WINSCOPE_TRACING, winscopeTracing)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index 1c07d00..ff18a11 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -17,8 +17,6 @@
package com.android.systemui.recordissue
import android.annotation.SuppressLint
-import android.app.BroadcastOptions
-import android.app.PendingIntent
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
@@ -43,8 +41,6 @@
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate
import com.android.systemui.qs.tiles.RecordIssueTile
import com.android.systemui.res.R
-import com.android.systemui.screenrecord.RecordingService
-import com.android.systemui.settings.UserContextProvider
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -52,12 +48,12 @@
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.util.concurrent.Executor
+import java.util.function.Consumer
class RecordIssueDialogDelegate
@AssistedInject
constructor(
private val factory: SystemUIDialog.Factory,
- private val userContextProvider: UserContextProvider,
private val userTracker: UserTracker,
private val flags: FeatureFlagsClassic,
@Background private val bgExecutor: Executor,
@@ -66,14 +62,14 @@
private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
private val userFileManager: UserFileManager,
private val screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate,
- @Assisted private val onStarted: Runnable,
+ @Assisted private val onStarted: Consumer<IssueRecordingConfig>,
) : SystemUIDialog.Delegate {
/** To inject dependencies and allow for easier testing */
@AssistedFactory
interface Factory {
/** Create a dialog object */
- fun create(onStarted: Runnable): RecordIssueDialogDelegate
+ fun create(onStarted: Consumer<IssueRecordingConfig>): RecordIssueDialogDelegate
}
@SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch
@@ -87,10 +83,12 @@
setIcon(R.drawable.qs_record_issue_icon_off)
setNegativeButton(R.string.cancel) { _, _ -> dismiss() }
setPositiveButton(R.string.qs_record_issue_start) { _, _ ->
- onStarted.run()
- if (screenRecordSwitch.isChecked) {
- requestScreenCapture()
- }
+ onStarted.accept(
+ IssueRecordingConfig(
+ screenRecordSwitch.isChecked,
+ true /* TODO: Base this on issueType selected */
+ )
+ )
dismiss()
}
}
@@ -177,13 +175,4 @@
show()
}
}
-
- private fun requestScreenCapture() =
- PendingIntent.getForegroundService(
- userContextProvider.userContext,
- RecordingService.REQUEST_CODE,
- IssueRecordingService.getStartIntent(userContextProvider.userContext),
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
- .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index b355d2d..ac94f39 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -60,25 +60,27 @@
public static final int REQUEST_CODE = 2;
private static final int USER_ID_NOT_SPECIFIED = -1;
- private static final int NOTIF_BASE_ID = 4273;
+ protected static final int NOTIF_BASE_ID = 4273;
private static final String TAG = "RecordingService";
private static final String CHANNEL_ID = "screen_record";
private static final String GROUP_KEY = "screen_record_saved";
private static final String EXTRA_RESULT_CODE = "extra_resultCode";
- private static final String EXTRA_PATH = "extra_path";
+ protected static final String EXTRA_PATH = "extra_path";
private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio";
private static final String EXTRA_SHOW_TAPS = "extra_showTaps";
private static final String EXTRA_CAPTURE_TARGET = "extra_captureTarget";
protected static final String ACTION_START = "com.android.systemui.screenrecord.START";
+ protected static final String ACTION_SHOW_START_NOTIF =
+ "com.android.systemui.screenrecord.START_NOTIF";
protected static final String ACTION_STOP = "com.android.systemui.screenrecord.STOP";
- private static final String ACTION_STOP_NOTIF =
+ protected static final String ACTION_STOP_NOTIF =
"com.android.systemui.screenrecord.STOP_FROM_NOTIF";
- private static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE";
+ protected static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE";
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
private final RecordingController mController;
- private final KeyguardDismissUtil mKeyguardDismissUtil;
+ protected final KeyguardDismissUtil mKeyguardDismissUtil;
private final Handler mMainHandler;
private ScreenRecordingAudioSource mAudioSource;
private boolean mShowTaps;
@@ -86,9 +88,9 @@
private ScreenMediaRecorder mRecorder;
private final Executor mLongExecutor;
private final UiEventLogger mUiEventLogger;
- private final NotificationManager mNotificationManager;
- private final UserContextProvider mUserContextTracker;
- private int mNotificationId = NOTIF_BASE_ID;
+ protected final NotificationManager mNotificationManager;
+ protected final UserContextProvider mUserContextTracker;
+ protected int mNotificationId = NOTIF_BASE_ID;
private RecordingServiceStrings mStrings;
@Inject
@@ -185,7 +187,10 @@
return Service.START_NOT_STICKY;
}
break;
-
+ case ACTION_SHOW_START_NOTIF:
+ createRecordingNotification();
+ mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_START);
+ break;
case ACTION_STOP_NOTIF:
case ACTION_STOP:
// only difference for actions is the log event
@@ -232,6 +237,7 @@
super.onCreate();
}
+ @Nullable
@VisibleForTesting
protected ScreenMediaRecorder getRecorder() {
return mRecorder;
@@ -337,8 +343,9 @@
}
@VisibleForTesting
- protected Notification createSaveNotification(ScreenMediaRecorder.SavedRecording recording) {
- Uri uri = recording.getUri();
+ protected Notification createSaveNotification(
+ @Nullable ScreenMediaRecorder.SavedRecording recording) {
+ Uri uri = recording != null ? recording.getUri() : null;
Intent viewIntent = new Intent(Intent.ACTION_VIEW)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION)
.setDataAndType(uri, "video/mp4");
@@ -349,7 +356,7 @@
PendingIntent.getService(
this,
REQUEST_CODE,
- getShareIntent(this, uri.toString()),
+ getShareIntent(this, uri != null ? uri.toString() : null),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
.build();
@@ -371,9 +378,10 @@
.addExtras(extras);
// Add thumbnail if available
- if (recording.getThumbnail() != null) {
+ Icon thumbnail = recording != null ? recording.getThumbnail() : null;
+ if (thumbnail != null) {
Notification.BigPictureStyle pictureStyle = new Notification.BigPictureStyle()
- .bigPicture(recording.getThumbnail())
+ .bigPicture(thumbnail)
.showBigPictureWhenCollapsed(true);
builder.setStyle(pictureStyle);
}
@@ -408,27 +416,29 @@
}
Log.d(getTag(), "notifying for user " + userId);
setTapsVisible(mOriginalShowTaps);
- if (getRecorder() != null) {
- try {
+ try {
+ if (getRecorder() != null) {
getRecorder().end();
- saveRecording(userId);
- } catch (RuntimeException exception) {
+ }
+ saveRecording(userId);
+ } catch (RuntimeException exception) {
+ if (getRecorder() != null) {
// RuntimeException could happen if the recording stopped immediately after starting
// let's release the recorder and delete all temporary files in this case
getRecorder().release();
- showErrorToast(R.string.screenrecord_start_error);
- Log.e(getTag(), "stopRecording called, but there was an error when ending"
- + "recording");
- exception.printStackTrace();
- createErrorNotification();
- } catch (Throwable throwable) {
+ }
+ showErrorToast(R.string.screenrecord_start_error);
+ Log.e(getTag(), "stopRecording called, but there was an error when ending"
+ + "recording");
+ exception.printStackTrace();
+ createErrorNotification();
+ } catch (Throwable throwable) {
+ if (getRecorder() != null) {
// Something unexpected happen, SystemUI will crash but let's delete
// the temporary files anyway
getRecorder().release();
- throw new RuntimeException(throwable);
}
- } else {
- Log.e(getTag(), "stopRecording called, but recorder was null");
+ throw new RuntimeException(throwable);
}
updateState(false);
stopForeground(STOP_FOREGROUND_DETACH);
@@ -443,7 +453,8 @@
mLongExecutor.execute(() -> {
try {
Log.d(getTag(), "saving recording");
- Notification notification = createSaveNotification(getRecorder().save());
+ Notification notification = createSaveNotification(
+ getRecorder() != null ? getRecorder().save() : null);
postGroupNotification(currentUser);
mNotificationManager.notifyAsUser(null, mNotificationId, notification,
currentUser);
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 ada93db..2e8160b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -37,7 +37,6 @@
import com.android.systemui.model.SysUiState
import com.android.systemui.qs.tiles.RecordIssueTile
import com.android.systemui.res.R
-import com.android.systemui.settings.UserContextProvider
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -71,12 +70,11 @@
@Mock private lateinit var devicePolicyResolver: ScreenCaptureDevicePolicyResolver
@Mock private lateinit var dprLazy: dagger.Lazy<ScreenCaptureDevicePolicyResolver>
@Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger
- @Mock private lateinit var userContextProvider: UserContextProvider
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var userFileManager: UserFileManager
@Mock private lateinit var sharedPreferences: SharedPreferences
- @Mock private lateinit var screenCaptureDisabledDialogDelegate:
- ScreenCaptureDisabledDialogDelegate
+ @Mock
+ private lateinit var screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate
@Mock private lateinit var screenCaptureDisabledDialog: SystemUIDialog
@Mock private lateinit var sysuiState: SysUiState
@@ -96,9 +94,8 @@
MockitoAnnotations.initMocks(this)
whenever(dprLazy.get()).thenReturn(devicePolicyResolver)
whenever(sysuiState.setFlag(anyInt(), anyBoolean())).thenReturn(sysuiState)
- whenever(userContextProvider.userContext).thenReturn(mContext)
whenever(screenCaptureDisabledDialogDelegate.createDialog())
- .thenReturn(screenCaptureDisabledDialog)
+ .thenReturn(screenCaptureDisabledDialog)
whenever(
userFileManager.getSharedPreferences(
eq(RecordIssueTile.TILE_SPEC),
@@ -123,7 +120,6 @@
dialog =
RecordIssueDialogDelegate(
factory,
- userContextProvider,
userTracker,
flags,
bgExecutor,