Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad3f9ac5 authored by Stefan Andonian's avatar Stefan Andonian
Browse files

Record an issue from the Record Issue QS tile.

This change integrates the Perfetto/Winscope Traces, as well as the
overall notification flow in order to start traces, stop traces, and
then share them with betterbug.

Bug: 305049544
Flag: ACONFIG record_issue_qs_tile DEVELOPMENT
Test: Manually tested on device. This feature is not 100% production
ready, but it is close.

Change-Id: I701b4e5876c6ad4c3eeb085ea93279fcb611d49a
parent 1c1bd35f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -155,6 +155,7 @@ android_library {
        "jsr330",
        "lottie",
        "LowLightDreamLib",
        "TraceurCommon",
        "motion_tool_lib",
        "notification_flags_lib",
        "PlatformComposeCore",
@@ -301,6 +302,7 @@ android_library {
        "androidx.compose.material_material-icons-extended",
        "androidx.activity_activity-compose",
        "androidx.compose.animation_animation-graphics",
        "TraceurCommon",
    ],
}

+2 −1
Original line number Diff line number Diff line
@@ -19,4 +19,5 @@
    <cache-path name="leak" path="leak/"/>
    <external-path name="screenrecord" path="."/>
    <cache-path name="multi_user" path="multi_user/" />
    <root-path name="traces" path="/data/local/traces"/>
</paths>
+16 −2
Original line number Diff line number Diff line
@@ -103,14 +103,27 @@ constructor(
    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 @@ constructor(
            delegateFactory
                .create {
                    isRecording = true
                    startIssueRecordingService(it.screenRecord, it.winscopeTracing)
                    refreshState()
                }
                .createDialog()
+19 −0
Original line number Diff line number Diff line
/*
 * 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)
+94 −3
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@ import android.app.NotificationManager
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.RecordingService
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 @@ constructor(

    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 @@ constructor(
         * @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 @@ constructor(
         *
         * @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)
    }
}

Loading