summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Stefan Andonian <andonian@google.com> 2024-09-23 23:37:15 +0000
committer Stefan Andonian <andonian@google.com> 2024-10-03 18:07:11 +0000
commit70b54e38d928b308bea19fdd9bf5d55d1125ca1a (patch)
treedd65bfd2a8fe5dc6aabe2698ad94c4c560e5d3f1
parentc0a488ea49fc9f1528b9fd2c9386cfb1a5e87303 (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
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/TraceurConnectionTest.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceConnection.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt12
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,