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

Commit 936840b0 authored by Ned Burns's avatar Ned Burns
Browse files

Log clicks on notification actions

Add buffer logging for the various stages of clicking on a notification
action.

Bug: 112656837
Test: atest, manual
Change-Id: I2f4803d2e3973a92f9ca24773eedc3ee6a3b1d59
parent 84e2f0c5
Loading
Loading
Loading
Loading
+88 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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

import android.app.PendingIntent
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.NotifInteractionLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import javax.inject.Inject

/**
 * Logger class for events related to the user clicking on notification actions
 */
class ActionClickLogger @Inject constructor(
    @NotifInteractionLog private val buffer: LogBuffer
) {
    fun logInitialClick(
        entry: NotificationEntry?,
        pendingIntent: PendingIntent
    ) {
        buffer.log(TAG, LogLevel.DEBUG, {
            str1 = entry?.key
            str2 = entry?.ranking?.channel?.id
            str3 = pendingIntent.intent.toString()
        }, {
            "ACTION CLICK $str1 (channel=$str2) for pending intent $str3"
        })
    }

    fun logRemoteInputWasHandled(
        entry: NotificationEntry?
    ) {
        buffer.log(TAG, LogLevel.DEBUG, {
            str1 = entry?.key
        }, {
            "  [Action click] Triggered remote input (for $str1))"
        })
    }

    fun logStartingIntentWithDefaultHandler(
        entry: NotificationEntry?,
        pendingIntent: PendingIntent
    ) {
        buffer.log(TAG, LogLevel.DEBUG, {
            str1 = entry?.key
            str2 = pendingIntent.intent.toString()
        }, {
            "  [Action click] Launching intent $str2 via default handler (for $str1)"
        })
    }

    fun logWaitingToCloseKeyguard(
        pendingIntent: PendingIntent
    ) {
        buffer.log(TAG, LogLevel.DEBUG, {
            str1 = pendingIntent.intent.toString()
        }, {
            "  [Action click] Intent $str1 launches an activity, dismissing keyguard first..."
        })
    }

    fun logKeyguardGone(
        pendingIntent: PendingIntent
    ) {
        buffer.log(TAG, LogLevel.DEBUG, {
            str1 = pendingIntent.intent.toString()
        }, {
            "  [Action click] Keyguard dismissed, calling default handler for intent $str1"
        })
    }
}

private const val TAG = "ActionClickLogger"
 No newline at end of file
+19 −8
Original line number Diff line number Diff line
@@ -54,7 +54,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -114,6 +114,7 @@ public class NotificationRemoteInputManager implements Dumpable {
    private final SmartReplyController mSmartReplyController;
    private final NotificationEntryManager mEntryManager;
    private final Handler mMainHandler;
    private final ActionClickLogger mLogger;

    private final Lazy<StatusBar> mStatusBarLazy;

@@ -138,14 +139,18 @@ public class NotificationRemoteInputManager implements Dumpable {
            mStatusBarLazy.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view,
                    "NOTIFICATION_CLICK");

            final NotificationEntry entry = getNotificationForParent(view.getParent());
            mLogger.logInitialClick(entry, pendingIntent);

            if (handleRemoteInput(view, pendingIntent)) {
                mLogger.logRemoteInputWasHandled(entry);
                return true;
            }

            if (DEBUG) {
                Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
            }
            logActionClick(view, pendingIntent);
            logActionClick(view, entry, pendingIntent);
            // The intent we are sending is for the application, which
            // won't have permission to immediately start an activity after
            // the user switches to home.  We know it is safe to do at this
@@ -158,11 +163,15 @@ public class NotificationRemoteInputManager implements Dumpable {
                Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
                options.second.setLaunchWindowingMode(
                        WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
                mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent);
                return RemoteViews.startPendingIntent(view, pendingIntent, options);
            });
        }

        private void logActionClick(View view, PendingIntent actionIntent) {
        private void logActionClick(
                View view,
                NotificationEntry entry,
                PendingIntent actionIntent) {
            Integer actionIndex = (Integer)
                    view.getTag(com.android.internal.R.id.notification_action_index_tag);
            if (actionIndex == null) {
@@ -170,7 +179,7 @@ public class NotificationRemoteInputManager implements Dumpable {
                return;
            }
            ViewParent parent = view.getParent();
            StatusBarNotification statusBarNotification = getNotificationForParent(parent);
            StatusBarNotification statusBarNotification = entry.getSbn();
            if (statusBarNotification == null) {
                Log.w(TAG, "Couldn't determine notification for click.");
                return;
@@ -212,10 +221,10 @@ public class NotificationRemoteInputManager implements Dumpable {
            }
        }

        private StatusBarNotification getNotificationForParent(ViewParent parent) {
        private NotificationEntry getNotificationForParent(ViewParent parent) {
            while (parent != null) {
                if (parent instanceof ExpandableNotificationRow) {
                    return ((ExpandableNotificationRow) parent).getEntry().getSbn();
                    return ((ExpandableNotificationRow) parent).getEntry();
                }
                parent = parent.getParent();
            }
@@ -255,7 +264,7 @@ public class NotificationRemoteInputManager implements Dumpable {
    };

    /**
     * Injected constructor. See {@link StatusBarModule}.
     * Injected constructor. See {@link StatusBarDependenciesModule}.
     */
    public NotificationRemoteInputManager(
            Context context,
@@ -265,13 +274,15 @@ public class NotificationRemoteInputManager implements Dumpable {
            Lazy<StatusBar> statusBarLazy,
            StatusBarStateController statusBarStateController,
            @Main Handler mainHandler,
            RemoteInputUriController remoteInputUriController) {
            RemoteInputUriController remoteInputUriController,
            ActionClickLogger logger) {
        mContext = context;
        mLockscreenUserManager = lockscreenUserManager;
        mSmartReplyController = smartReplyController;
        mEntryManager = notificationEntryManager;
        mStatusBarLazy = statusBarLazy;
        mMainHandler = mainHandler;
        mLogger = logger;
        mBarService = IStatusBarService.Stub.asInterface(
                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+5 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.MediaArtworkProcessor;
import com.android.systemui.statusbar.NotificationListener;
@@ -73,7 +74,8 @@ public interface StatusBarDependenciesModule {
            Lazy<StatusBar> statusBarLazy,
            StatusBarStateController statusBarStateController,
            Handler mainHandler,
            RemoteInputUriController remoteInputUriController) {
            RemoteInputUriController remoteInputUriController,
            ActionClickLogger actionClickLogger) {
        return new NotificationRemoteInputManager(
                context,
                lockscreenUserManager,
@@ -82,7 +84,8 @@ public interface StatusBarDependenciesModule {
                statusBarLazy,
                statusBarStateController,
                mainHandler,
                remoteInputUriController);
                remoteInputUriController,
                actionClickLogger);
    }

    /** */
+8 −1
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.view.ViewParent;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -73,6 +74,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
    private View mPendingRemoteInputView;
    private KeyguardManager mKeyguardManager;
    private final CommandQueue mCommandQueue;
    private final ActionClickLogger mActionClickLogger;
    private int mDisabled2;
    protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver();
    private Handler mMainHandler = new Handler();
@@ -87,7 +89,8 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
            StatusBarStateController statusBarStateController,
            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
            ActivityStarter activityStarter, ShadeController shadeController,
            CommandQueue commandQueue) {
            CommandQueue commandQueue,
            ActionClickLogger clickLogger) {
        mContext = context;
        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
        mShadeController = shadeController;
@@ -101,6 +104,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
        mKeyguardManager = context.getSystemService(KeyguardManager.class);
        mCommandQueue = commandQueue;
        mCommandQueue.addCallback(this);
        mActionClickLogger = clickLogger;
        mActivityIntentHelper = new ActivityIntentHelper(mContext);
        mGroupManager = groupManager;
        // Listen to onKeyguardShowingChanged in case a managed profile needs to be unlocked
@@ -304,9 +308,12 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
            NotificationRemoteInputManager.ClickHandler defaultHandler) {
        final boolean isActivity = pendingIntent.isActivity();
        if (isActivity) {
            mActionClickLogger.logWaitingToCloseKeyguard(pendingIntent);
            final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity(
                    pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId());
            mActivityStarter.dismissKeyguardThenExecute(() -> {
                mActionClickLogger.logKeyguardGone(pendingIntent);

                try {
                    ActivityManager.getService().resumeAppSwitches();
                } catch (RemoteException e) {
+16 −6
Original line number Diff line number Diff line
@@ -82,7 +82,8 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
                () -> mock(StatusBar.class),
                mStateController,
                Handler.createAsync(Looper.myLooper()),
                mRemoteInputUriController);
                mRemoteInputUriController,
                mock(ActionClickLogger.class));
        mEntry = new NotificationEntryBuilder()
                .setPkg(TEST_PACKAGE_NAME)
                .setOpPkg(TEST_PACKAGE_NAME)
@@ -256,17 +257,26 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {

    private class TestableNotificationRemoteInputManager extends NotificationRemoteInputManager {

        TestableNotificationRemoteInputManager(Context context,
        TestableNotificationRemoteInputManager(
                Context context,
                NotificationLockscreenUserManager lockscreenUserManager,
                SmartReplyController smartReplyController,
                NotificationEntryManager notificationEntryManager,
                Lazy<StatusBar> statusBarLazy,
                StatusBarStateController statusBarStateController,
                Handler mainHandler,
                RemoteInputUriController remoteInputUriController) {
            super(context, lockscreenUserManager, smartReplyController, notificationEntryManager,
                    statusBarLazy, statusBarStateController, mainHandler,
                    remoteInputUriController);
                RemoteInputUriController remoteInputUriController,
                ActionClickLogger actionClickLogger) {
            super(
                    context,
                    lockscreenUserManager,
                    smartReplyController,
                    notificationEntryManager,
                    statusBarLazy,
                    statusBarStateController,
                    mainHandler,
                    remoteInputUriController,
                    actionClickLogger);
        }

        public void setUpWithPresenterForTest(Callback callback,
Loading