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

Commit ba8ec2dd authored by Steve Elliott's avatar Steve Elliott Committed by Automerger Merge Worker
Browse files

Merge "Add logging for unseen notif filtering" into udc-dev am: 209dc315

parents 2c6bdd4e 209dc315
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -136,6 +136,14 @@ public class LogModule {
        return factory.create("NotifRemoteInputLog", 50 /* maxSize */, false /* systrace */);
    }

    /** Provides a logging buffer for all logs related to unseen notifications. */
    @Provides
    @SysUISingleton
    @UnseenNotificationLog
    public static LogBuffer provideUnseenNotificationLogBuffer(LogBufferFactory factory) {
        return factory.create("UnseenNotifLog", 20 /* maxSize */, false /* systrace */);
    }

    /** Provides a logging buffer for all logs related to the data layer of notifications. */
    @Provides
    @SysUISingleton
+33 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.log.dagger;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

import com.android.systemui.plugins.log.LogBuffer;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;

import javax.inject.Qualifier;

/** A {@link LogBuffer} for unseen notification related messages. */
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface UnseenNotificationLog {
}
+45 −9
Original line number Diff line number Diff line
@@ -21,8 +21,10 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import android.os.UserHandle
import android.provider.Settings
import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -42,8 +44,14 @@ import com.android.systemui.statusbar.notification.collection.provider.SeenNotif
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.headsUpEvents
import com.android.systemui.util.asIndenting
import com.android.systemui.util.indentIfPossible
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import java.io.PrintWriter
import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -57,13 +65,11 @@ import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds

/**
 * Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section
@@ -74,17 +80,19 @@ class KeyguardCoordinator
@Inject
constructor(
    @Background private val bgDispatcher: CoroutineDispatcher,
    private val dumpManager: DumpManager,
    private val headsUpManager: HeadsUpManager,
    private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
    private val keyguardRepository: KeyguardRepository,
    private val keyguardTransitionRepository: KeyguardTransitionRepository,
    private val logger: KeyguardCoordinatorLogger,
    private val notifPipelineFlags: NotifPipelineFlags,
    @Application private val scope: CoroutineScope,
    private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
    private val secureSettings: SecureSettings,
    private val seenNotifsProvider: SeenNotificationsProviderImpl,
    private val statusBarStateController: StatusBarStateController,
) : Coordinator {
) : Coordinator, Dumpable {

    private val unseenNotifications = mutableSetOf<NotificationEntry>()
    private var unseenFilterEnabled = false
@@ -103,6 +111,7 @@ constructor(
        pipeline.addCollectionListener(collectionListener)
        scope.launch { trackUnseenNotificationsWhileUnlocked() }
        scope.launch { invalidateWhenUnseenSettingChanges() }
        dumpManager.registerDumpable(this)
    }

    private suspend fun trackUnseenNotificationsWhileUnlocked() {
@@ -122,14 +131,16 @@ constructor(
                        // If the screen is turning off, stop tracking, but if that transition is
                        // cancelled, then start again.
                        emitAll(
                            keyguardTransitionRepository.transitions
                                .map { step -> !step.isScreenTurningOff }
                            keyguardTransitionRepository.transitions.map { step ->
                                !step.isScreenTurningOff
                            }
                        )
                    }
                }
                // Prevent double emit of `false` caused by transition to AOD, followed by keyguard
                // showing
                .distinctUntilChanged()
                .onEach { trackingUnseen -> logger.logTrackingUnseen(trackingUnseen) }

        // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is
        // showing again
@@ -140,9 +151,11 @@ constructor(
                // set when unlocked
                awaitTimeSpentNotDozing(SEEN_TIMEOUT)
                clearUnseenOnBeginTracking = true
                logger.logSeenOnLockscreen()
            } else {
                if (clearUnseenOnBeginTracking) {
                    clearUnseenOnBeginTracking = false
                    logger.logAllMarkedSeenOnUnlock()
                    unseenNotifications.clear()
                }
                unseenNotifFilter.invalidateList("keyguard no longer showing")
@@ -166,6 +179,8 @@ constructor(
            .first()
    }

    // Track "unseen" notifications, marking them as seen when either shade is expanded or the
    // notification becomes heads up.
    private suspend fun trackUnseenNotifications() {
        coroutineScope {
            launch { clearUnseenNotificationsWhenShadeIsExpanded() }
@@ -179,6 +194,7 @@ constructor(
            // keyguard transition and not the user expanding the shade
            yield()
            if (isExpanded) {
                logger.logShadeExpanded()
                unseenNotifications.clear()
            }
        }
@@ -190,6 +206,7 @@ constructor(
            .forEach { unseenNotifications.remove(it) }
        headsUpManager.headsUpEvents.collect { (entry, isHun) ->
            if (isHun) {
                logger.logUnseenHun(entry.key)
                unseenNotifications.remove(entry)
            }
        }
@@ -231,6 +248,7 @@ constructor(
                if (
                    keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded
                ) {
                    logger.logUnseenAdded(entry.key)
                    unseenNotifications.add(entry)
                }
            }
@@ -239,12 +257,15 @@ constructor(
                if (
                    keyguardRepository.isKeyguardShowing() || !statusBarStateController.isExpanded
                ) {
                    logger.logUnseenUpdated(entry.key)
                    unseenNotifications.add(entry)
                }
            }

            override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
                unseenNotifications.remove(entry)
                if (unseenNotifications.remove(entry)) {
                    logger.logUnseenRemoved(entry.key)
                }
            }
        }

@@ -272,6 +293,7 @@ constructor(
                }.also { hasFiltered -> hasFilteredAnyNotifs = hasFilteredAnyNotifs || hasFiltered }

            override fun onCleanup() {
                logger.logProviderHasFilteredOutSeenNotifs(hasFilteredAnyNotifs)
                seenNotifsProvider.hasFilteredOutSeenNotifications = hasFilteredAnyNotifs
                hasFilteredAnyNotifs = false
            }
@@ -306,11 +328,25 @@ constructor(
        sectionHeaderVisibilityProvider.sectionHeadersVisible = showSections
    }

    override fun dump(pw: PrintWriter, args: Array<out String>) =
        with(pw.asIndenting()) {
            println(
                "seenNotifsProvider.hasFilteredOutSeenNotifications=" +
                    seenNotifsProvider.hasFilteredOutSeenNotifications
            )
            println("unseen notifications:")
            indentIfPossible {
                for (notification in unseenNotifications) {
                    println(notification.key)
                }
            }
        }

    companion object {
        private const val TAG = "KeyguardCoordinator"
        private val SEEN_TIMEOUT = 5.seconds
    }
}

private val TransitionStep.isScreenTurningOff: Boolean get() =
    transitionState == TransitionState.STARTED && to != KeyguardState.GONE
 No newline at end of file
private val TransitionStep.isScreenTurningOff: Boolean
    get() = transitionState == TransitionState.STARTED && to != KeyguardState.GONE
+99 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.statusbar.notification.collection.coordinator

import com.android.systemui.log.dagger.UnseenNotificationLog
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogLevel
import javax.inject.Inject

private const val TAG = "KeyguardCoordinator"

class KeyguardCoordinatorLogger
@Inject
constructor(
    @UnseenNotificationLog private val buffer: LogBuffer,
) {
    fun logSeenOnLockscreen() =
        buffer.log(
            TAG,
            LogLevel.DEBUG,
            "Notifications on lockscreen will be marked as seen when unlocked."
        )

    fun logTrackingUnseen(trackingUnseen: Boolean) =
        buffer.log(
            TAG,
            LogLevel.DEBUG,
            messageInitializer = { bool1 = trackingUnseen },
            messagePrinter = { "${if (bool1) "Start" else "Stop"} tracking unseen notifications." },
        )

    fun logAllMarkedSeenOnUnlock() =
        buffer.log(
            TAG,
            LogLevel.DEBUG,
            "Notifications have been marked as seen now that device is unlocked."
        )

    fun logShadeExpanded() =
        buffer.log(
            TAG,
            LogLevel.DEBUG,
            "Notifications have been marked as seen due to shade expansion."
        )

    fun logUnseenAdded(key: String) =
        buffer.log(
            TAG,
            LogLevel.DEBUG,
            messageInitializer = { str1 = key },
            messagePrinter = { "Unseen notif added: $str1" },
        )

    fun logUnseenUpdated(key: String) =
        buffer.log(
            TAG,
            LogLevel.DEBUG,
            messageInitializer = { str1 = key },
            messagePrinter = { "Unseen notif updated: $str1" },
        )

    fun logUnseenRemoved(key: String) =
        buffer.log(
            TAG,
            LogLevel.DEBUG,
            messageInitializer = { str1 = key },
            messagePrinter = { "Unseen notif removed: $str1" },
        )

    fun logProviderHasFilteredOutSeenNotifs(hasFilteredAnyNotifs: Boolean) =
        buffer.log(
            TAG,
            LogLevel.DEBUG,
            messageInitializer = { bool1 = hasFilteredAnyNotifs },
            messagePrinter = { "UI showing unseen filter treatment: $bool1" },
        )

    fun logUnseenHun(key: String) =
        buffer.log(
            TAG,
            LogLevel.DEBUG,
            messageInitializer = { str1 = key },
            messagePrinter = { "Unseen notif has become heads up: $str1" },
        )
}
+3 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.advanceTimeBy
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -380,10 +381,12 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
        val keyguardCoordinator =
            KeyguardCoordinator(
                testDispatcher,
                mock<DumpManager>(),
                headsUpManager,
                keyguardNotifVisibilityProvider,
                keyguardRepository,
                keyguardTransitionRepository,
                mock<KeyguardCoordinatorLogger>(),
                notifPipelineFlags,
                testScope.backgroundScope,
                sectionHeaderVisibilityProvider,