diff options
8 files changed, 110 insertions, 21 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt index 9ca8f447d374..963973588236 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt @@ -78,7 +78,6 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() { @Mock private lateinit var sysuiState: SysUiState @Mock private lateinit var systemUIDialogManager: SystemUIDialogManager @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher - @Mock private lateinit var traceurConnection: TraceurConnection private val systemClock = FakeSystemClock() private val bgExecutor = FakeExecutor(systemClock) private val mainExecutor = FakeExecutor(systemClock) @@ -120,7 +119,6 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() { mediaProjectionMetricsLogger, screenCaptureDisabledDialogDelegate, state, - traceurConnection, ) { latch.countDown() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/TraceurConnectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/TraceurConnectionTest.kt index 85a8237ff955..d90cca9b23fe 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/TraceurConnectionTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/TraceurConnectionTest.kt @@ -50,7 +50,7 @@ class TraceurConnectionTest : SysuiTestCase() { fun setup() { MockitoAnnotations.initMocks(this) whenever(userContextProvider.userContext).thenReturn(mContext) - underTest = TraceurConnection(userContextProvider, Looper.getMainLooper()) + underTest = TraceurConnection.Provider(userContextProvider, Looper.getMainLooper()).create() } @Test 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 c316c8626e8e..fb406d47d7a6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt @@ -45,6 +45,7 @@ import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.recordissue.IssueRecordingService.Companion.getStartIntent import com.android.systemui.recordissue.IssueRecordingService.Companion.getStopIntent +import com.android.systemui.recordissue.IssueRecordingServiceConnection import com.android.systemui.recordissue.IssueRecordingState import com.android.systemui.recordissue.RecordIssueDialogDelegate import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC @@ -66,7 +67,7 @@ class RecordIssueTile constructor( host: QSHost, uiEventLogger: QsEventLogger, - @Background backgroundLooper: Looper, + @Background private val backgroundLooper: Looper, @Main mainHandler: Handler, falsingManager: FalsingManager, metricsLogger: MetricsLogger, @@ -78,7 +79,8 @@ constructor( private val dialogTransitionAnimator: DialogTransitionAnimator, private val panelInteractor: PanelInteractor, private val userContextProvider: UserContextProvider, - private val traceurConnection: TraceurConnection, + irsConnProvider: IssueRecordingServiceConnection.Provider, + traceurConnProvider: TraceurConnection.Provider, @Background private val bgExecutor: Executor, private val issueRecordingState: IssueRecordingState, private val delegateFactory: RecordIssueDialogDelegate.Factory, @@ -98,6 +100,15 @@ constructor( private val onRecordingChangeListener = Runnable { refreshState() } + private val irsConnection: IssueRecordingServiceConnection = irsConnProvider.create() + private val traceurConnection = + traceurConnProvider.create().apply { + onBound.add { + getTags(issueRecordingState) + doUnBind() + } + } + override fun handleSetListening(listening: Boolean) { super.handleSetListening(listening) if (listening) { @@ -109,7 +120,7 @@ constructor( override fun handleDestroy() { super.handleDestroy() - bgExecutor.execute { traceurConnection.doUnBind() } + bgExecutor.execute { irsConnection.doUnBind() } } override fun getTileLabel(): CharSequence = mContext.getString(R.string.qs_record_issue_label) @@ -158,6 +169,15 @@ constructor( ) private fun showPrompt(expandable: Expandable?) { + bgExecutor.execute { + // We only want to get the tags once per session, as this is not likely to change, if at + // all on a month to month basis. Using onBound's size is a way to verify if the tag + // retrieval has already happened or not. + if (traceurConnection.onBound.isNotEmpty()) { + traceurConnection.doBind() + } + irsConnection.doBind() + } val dialog: AlertDialog = delegateFactory .create { diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt index d1fa94e0a65e..3f875bcc288b 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt @@ -23,9 +23,12 @@ import android.content.Intent import android.content.res.Resources import android.net.Uri import android.os.Handler +import android.os.IBinder +import android.os.Looper import android.util.Log import com.android.internal.logging.UiEventLogger import com.android.systemui.animation.DialogTransitionAnimator +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.LongRunning import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor @@ -42,6 +45,7 @@ class IssueRecordingService @Inject constructor( controller: RecordingController, + @Background private val bgLooper: Looper, @LongRunning private val bgExecutor: Executor, @Main handler: Handler, uiEventLogger: UiEventLogger, @@ -50,8 +54,8 @@ constructor( keyguardDismissUtil: KeyguardDismissUtil, dialogTransitionAnimator: DialogTransitionAnimator, panelInteractor: PanelInteractor, - traceurConnection: TraceurConnection, private val issueRecordingState: IssueRecordingState, + traceurConnectionProvider: TraceurConnection.Provider, iActivityManager: IActivityManager, ) : RecordingService( @@ -64,6 +68,8 @@ constructor( keyguardDismissUtil, ) { + private val traceurConnection: TraceurConnection = traceurConnectionProvider.create() + private val session = IssueRecordingServiceSession( bgExecutor, @@ -76,6 +82,23 @@ constructor( userContextProvider, ) + /** + * It is necessary to bind to IssueRecordingService from the Record Issue Tile because there are + * instances where this service is not created in the same user profile as the record issue tile + * aka, headless system user mode. In those instances, the TraceurConnection will be considered + * a leak in between notification actions unless the tile is bound to this service to keep it + * alive. + */ + override fun onBind(intent: Intent): IBinder? { + traceurConnection.doBind() + return super.onBind(intent) + } + + override fun onUnbind(intent: Intent?): Boolean { + traceurConnection.doUnBind() + return super.onUnbind(intent) + } + override fun getTag(): String = TAG override fun getChannelId(): String = CHANNEL_ID diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceConnection.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceConnection.kt new file mode 100644 index 000000000000..85a580558125 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceConnection.kt @@ -0,0 +1,40 @@ +/* + * 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 + +import android.content.Intent +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.settings.UserContextProvider +import com.android.traceur.MessageConstants.SYSTEM_UI_PACKAGE_NAME +import javax.inject.Inject + +/** + * It is necessary to bind to IssueRecordingService from the Record Issue Tile because there are + * instances where this service is not created in the same user profile as the record issue tile + * aka, headless system user mode. In those instances, the TraceurConnection will be considered a + * leak in between notification actions unless the tile is bound to this service to keep it alive. + */ +class IssueRecordingServiceConnection(userContextProvider: UserContextProvider) : + UserAwareConnection( + userContextProvider, + Intent().setClassName(SYSTEM_UI_PACKAGE_NAME, IssueRecordingService::class.java.name), + ) { + @SysUISingleton + class Provider @Inject constructor(private val userContextProvider: UserContextProvider) { + fun create() = IssueRecordingServiceConnection(userContextProvider) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt index e38fe9aafe32..6758c3ba0767 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt @@ -64,7 +64,6 @@ constructor( private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger, private val screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate, private val state: IssueRecordingState, - private val traceurConnection: TraceurConnection, @Assisted private val onStarted: Runnable, ) : SystemUIDialog.Delegate { @@ -87,10 +86,6 @@ constructor( setNegativeButton(R.string.cancel) { _, _ -> } setPositiveButton(R.string.qs_record_issue_start) { _, _ -> onStarted.run() } } - bgExecutor.execute { - traceurConnection.onBound.add { traceurConnection.getTags(state) } - traceurConnection.doBind() - } } override fun createDialog(): SystemUIDialog = factory.create(this) diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt index 9494da91afe0..81529b357527 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt @@ -41,15 +41,23 @@ import javax.inject.Inject private const val TAG = "TraceurConnection" -@SysUISingleton class TraceurConnection -@Inject -constructor(userContextProvider: UserContextProvider, @Background private val bgLooper: Looper) : +private constructor(userContextProvider: UserContextProvider, private val bgLooper: Looper) : UserAwareConnection( userContextProvider, Intent().setClassName(TRACING_APP_PACKAGE_NAME, TRACING_APP_ACTIVITY), ) { + @SysUISingleton + class Provider + @Inject + constructor( + private val userContextProvider: UserContextProvider, + @Background private val bgLooper: Looper, + ) { + fun create() = TraceurConnection(userContextProvider, bgLooper) + } + val onBound: MutableList<Runnable> = CopyOnWriteArrayList(mutableListOf()) override fun onServiceConnected(className: ComponentName, service: IBinder) { @@ -119,10 +127,7 @@ private class ShareFilesHandler( val fileSharingIntent = FileSender.buildSendIntent(userContextProvider.userContext, uris) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) - userContextProvider.userContext.startActivityAsUser( - fileSharingIntent, - userContextProvider.userContext.user, - ) + userContextProvider.userContext.startActivity(fileSharingIntent) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt index 833cf424ef93..c7da03dbbf30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles import android.os.Handler +import android.os.Looper import android.service.quicksettings.Tile import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -31,6 +32,7 @@ import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor +import com.android.systemui.recordissue.IssueRecordingServiceConnection import com.android.systemui.recordissue.IssueRecordingState import com.android.systemui.recordissue.RecordIssueDialogDelegate import com.android.systemui.recordissue.TraceurConnection @@ -75,13 +77,14 @@ class RecordIssueTileTest : SysuiTestCase() { @Mock private lateinit var panelInteractor: PanelInteractor @Mock private lateinit var userContextProvider: UserContextProvider @Mock private lateinit var issueRecordingState: IssueRecordingState - @Mock private lateinit var traceurConnection: TraceurConnection @Mock private lateinit var delegateFactory: RecordIssueDialogDelegate.Factory @Mock private lateinit var dialogDelegate: RecordIssueDialogDelegate @Mock private lateinit var dialog: SystemUIDialog private lateinit var testableLooper: TestableLooper private lateinit var tile: RecordIssueTile + private lateinit var irsConnProvider: IssueRecordingServiceConnection.Provider + private lateinit var traceurConnProvider: TraceurConnection.Provider @Before fun setUp() { @@ -90,6 +93,10 @@ class RecordIssueTileTest : SysuiTestCase() { whenever(delegateFactory.create(any())).thenReturn(dialogDelegate) whenever(dialogDelegate.createDialog()).thenReturn(dialog) + irsConnProvider = IssueRecordingServiceConnection.Provider(userContextProvider) + traceurConnProvider = + TraceurConnection.Provider(userContextProvider, Looper.getMainLooper()) + testableLooper = TestableLooper.get(this) tile = RecordIssueTile( @@ -107,7 +114,8 @@ class RecordIssueTileTest : SysuiTestCase() { dialogLauncherAnimator, panelInteractor, userContextProvider, - traceurConnection, + irsConnProvider, + traceurConnProvider, Executors.newSingleThreadExecutor(), issueRecordingState, delegateFactory, |