diff options
| author | 2024-09-23 23:37:15 +0000 | |
|---|---|---|
| committer | 2024-10-03 18:07:11 +0000 | |
| commit | 70b54e38d928b308bea19fdd9bf5d55d1125ca1a (patch) | |
| tree | dd65bfd2a8fe5dc6aabe2698ad94c4c560e5d3f1 | |
| parent | c0a488ea49fc9f1528b9fd2c9386cfb1a5e87303 (diff) | |
Make RecordIssueTile's ServiceConnections not injected, but created.
This fixes the issue of sometimes having the dagger injection giving the
same instance in the tile and the service, and sometimes not. We can
avoid this by ensuring the service connections are always different.
Also, by Binding to IssueRecordingService, we can avoid "leaking" the
traceurConnection which would otherwise need to be created / destroyed
with every notification action.
Bug: 364824477
Test: Verified locally that System Tracing via the Record Issue Tile
works great in Headless System User Mode.
Flag: EXEMPT bug fix
Change-Id: I076f7a9c058fbc725296a3c2bb34cbc75675d30c
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, |