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

Commit 530e9c28 authored by Robert Luo's avatar Robert Luo
Browse files

Migrate media output switcher metrics - 2/n

Bug: 172181610
Test: (1) run statsd_testdrive 277, and check the
metric output correctness after content switching.
(2) run statsd_testdrive 90, and check the metric
output correctness after launching the output dialog
via Sound Setting / Volume Rocker / Media Object Chip.
(3) atest MediaOutputControllerTest
(4) atest MediaOutputDialogTest
(5) atest MediaOutputBaseDialogTest

Change-Id: I0fea0c59afcb44fc1d22920036107524f221a7be
parent 915c8155
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.IconCompat;

import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -84,11 +85,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
    @VisibleForTesting
    LocalMediaManager mLocalMediaManager;

    private MediaOutputMetricLogger mMetricLogger;
    private UiEventLogger mUiEventLogger;

    @Inject
    public MediaOutputController(@NonNull Context context, String packageName,
            boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
            lbm, ShadeController shadeController, ActivityStarter starter,
            NotificationEntryManager notificationEntryManager) {
            NotificationEntryManager notificationEntryManager, UiEventLogger uiEventLogger) {
        mContext = context;
        mPackageName = packageName;
        mMediaSessionManager = mediaSessionManager;
@@ -98,6 +102,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
        mNotificationEntryManager = notificationEntryManager;
        InfoMediaManager imm = new InfoMediaManager(mContext, packageName, null, lbm);
        mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
        mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
        mUiEventLogger = uiEventLogger;
    }

    void start(@NonNull Callback cb) {
@@ -151,6 +157,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
    public void onSelectedDeviceStateChanged(MediaDevice device,
            @LocalMediaManager.MediaDeviceState int state) {
        mCallback.onRouteChanged();
        mMetricLogger.logOutputSuccess(device.toString(), mMediaDevices);
    }

    @Override
@@ -161,6 +168,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
    @Override
    public void onRequestFailed(int reason) {
        mCallback.onRouteChanged();
        mMetricLogger.logOutputFailure(mMediaDevices, reason);
    }

    CharSequence getHeaderTitle() {
@@ -311,6 +319,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {
    }

    void connectDevice(MediaDevice device) {
        mMetricLogger.updateOutputEndPoints(getCurrentConnectedMediaDevice(), device);

        ThreadUtils.postOnBackgroundThread(() -> {
            mLocalMediaManager.connectDevice(device);
        });
@@ -439,7 +449,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback {

    void launchMediaOutputDialog() {
        mCallback.dismissDialog();
        new MediaOutputDialog(mContext, mAboveStatusbar, this);
        new MediaOutputDialog(mContext, mAboveStatusbar, this, mUiEventLogger);
    }

    void launchMediaOutputGroupDialog() {
+24 −1
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@ import android.view.WindowManager;

import androidx.core.graphics.drawable.IconCompat;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;

@@ -31,10 +34,12 @@ import com.android.systemui.dagger.SysUISingleton;
 */
@SysUISingleton
public class MediaOutputDialog extends MediaOutputBaseDialog {
    final UiEventLogger mUiEventLogger;

    MediaOutputDialog(Context context, boolean aboveStatusbar, MediaOutputController
            mediaOutputController) {
            mediaOutputController, UiEventLogger uiEventLogger) {
        super(context, mediaOutputController);
        mUiEventLogger = uiEventLogger;
        mAdapter = new MediaOutputAdapter(mMediaOutputController);
        if (!aboveStatusbar) {
            getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
@@ -45,6 +50,7 @@ public class MediaOutputDialog extends MediaOutputBaseDialog {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mUiEventLogger.log(MediaOutputEvent.MEDIA_OUTPUT_DIALOG_SHOW);
    }

    @Override
@@ -78,4 +84,21 @@ public class MediaOutputDialog extends MediaOutputBaseDialog {
        return mMediaOutputController.isActiveRemoteDevice(
                mMediaOutputController.getCurrentConnectedMediaDevice()) ? View.VISIBLE : View.GONE;
    }

    @VisibleForTesting
    public enum MediaOutputEvent implements UiEventLogger.UiEventEnum {
        @UiEvent(doc = "The MediaOutput dialog became visible on the screen.")
        MEDIA_OUTPUT_DIALOG_SHOW(655);

        private final int mId;

        MediaOutputEvent(int id) {
            mId = id;
        }

        @Override
        public int getId() {
            return mId;
        }
    }
}
+7 −3
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.media.dialog

import android.content.Context
import android.media.session.MediaSessionManager
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.notification.NotificationEntryManager
@@ -33,7 +34,8 @@ class MediaOutputDialogFactory @Inject constructor(
    private val lbm: LocalBluetoothManager?,
    private val shadeController: ShadeController,
    private val starter: ActivityStarter,
    private val notificationEntryManager: NotificationEntryManager
    private val notificationEntryManager: NotificationEntryManager,
    private val uiEventLogger: UiEventLogger
) {
    companion object {
        var mediaOutputDialog: MediaOutputDialog? = null
@@ -43,8 +45,10 @@ class MediaOutputDialogFactory @Inject constructor(
    fun create(packageName: String, aboveStatusBar: Boolean) {
        mediaOutputDialog?.dismiss()
        mediaOutputDialog = MediaOutputController(context, packageName, aboveStatusBar,
                mediaSessionManager, lbm, shadeController, starter, notificationEntryManager).run {
            MediaOutputDialog(context, aboveStatusBar, this) }
                mediaSessionManager, lbm, shadeController, starter, notificationEntryManager,
                uiEventLogger).run {
            MediaOutputDialog(context, aboveStatusBar, this, uiEventLogger)
        }
    }

    /** dismiss [MediaOutputDialog] if exist. */
+221 −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.media.dialog;

import static android.media.MediaRoute2ProviderService.REASON_INVALID_COMMAND;
import static android.media.MediaRoute2ProviderService.REASON_NETWORK_ERROR;
import static android.media.MediaRoute2ProviderService.REASON_REJECTED;
import static android.media.MediaRoute2ProviderService.REASON_ROUTE_NOT_AVAILABLE;
import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.util.Log;

import com.android.settingslib.media.MediaDevice;
import com.android.systemui.shared.system.SysUiStatsLog;

import java.util.List;

/**
 * Metric logger for media output features
 */
public class MediaOutputMetricLogger {

    private static final String TAG = "MediaOutputMetricLogger";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private final Context mContext;
    private final String mPackageName;
    private MediaDevice mSourceDevice, mTargetDevice;
    private int mWiredDeviceCount;
    private int mConnectedBluetoothDeviceCount;
    private int mRemoteDeviceCount;
    private int mAppliedDeviceCountWithinRemoteGroup;

    public MediaOutputMetricLogger(Context context, String packageName) {
        mContext = context;
        mPackageName = packageName;
    }

    /**
     * Update the endpoints of a content switching operation.
     * This method should be called before a switching operation, so the metric logger can track
     * source and target devices.
     * @param source the current connected media device
     * @param target the target media device for content switching to
     */
    public void updateOutputEndPoints(MediaDevice source, MediaDevice target) {
        mSourceDevice = source;
        mTargetDevice = target;

        if (DEBUG) {
            Log.d(TAG, "updateOutputEndPoints -"
                    + " source:" + mSourceDevice.toString()
                    + " target:" + mTargetDevice.toString());
        }
    }

    /**
     * Do the metric logging of content switching success.
     * @param selectedDeviceType string representation of the target media device
     * @param deviceList media device list for device count updating
     */
    public void logOutputSuccess(String selectedDeviceType, List<MediaDevice> deviceList) {
        if (DEBUG) {
            Log.d(TAG, "logOutputSuccess - selected device: " + selectedDeviceType);
        }

        updateLoggingDeviceCount(deviceList);

        SysUiStatsLog.write(
                SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED,
                getLoggingDeviceType(mSourceDevice, true),
                getLoggingDeviceType(mTargetDevice, false),
                SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__OK,
                SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NO_ERROR,
                getLoggingPackageName(),
                mWiredDeviceCount,
                mConnectedBluetoothDeviceCount,
                mRemoteDeviceCount,
                mAppliedDeviceCountWithinRemoteGroup);
    }

    /**
     * Do the metric logging of content switching failure.
     * @param deviceList media device list for device count updating
     * @param reason the reason of content switching failure
     */
    public void logOutputFailure(List<MediaDevice> deviceList, int reason) {
        if (DEBUG) {
            Log.e(TAG, "logRequestFailed - " + reason);
        }

        updateLoggingDeviceCount(deviceList);

        SysUiStatsLog.write(
                SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED,
                getLoggingDeviceType(mSourceDevice, true),
                getLoggingDeviceType(mTargetDevice, false),
                SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__ERROR,
                getLoggingSwitchOpSubResult(reason),
                getLoggingPackageName(),
                mWiredDeviceCount,
                mConnectedBluetoothDeviceCount,
                mRemoteDeviceCount,
                mAppliedDeviceCountWithinRemoteGroup);
    }

    private void updateLoggingDeviceCount(List<MediaDevice> deviceList) {
        mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0;
        mAppliedDeviceCountWithinRemoteGroup = 0;

        for (MediaDevice mediaDevice : deviceList) {
            if (mediaDevice.isConnected()) {
                switch (mediaDevice.getDeviceType()) {
                    case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE:
                    case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE:
                        mWiredDeviceCount++;
                        break;
                    case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE:
                        mConnectedBluetoothDeviceCount++;
                        break;
                    case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE:
                    case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE:
                        mRemoteDeviceCount++;
                        break;
                    default:
                }
            }
        }

        if (DEBUG) {
            Log.d(TAG, "connected devices:" + " wired: " + mWiredDeviceCount
                    + " bluetooth: " + mConnectedBluetoothDeviceCount
                    + " remote: " + mRemoteDeviceCount);
        }
    }

    private int getLoggingDeviceType(MediaDevice device, boolean isSourceDevice) {
        switch (device.getDeviceType()) {
            case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE:
                return isSourceDevice
                        ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__BUILTIN_SPEAKER
                        : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__BUILTIN_SPEAKER;
            case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE:
                return isSourceDevice
                        ? SysUiStatsLog
                        .MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__WIRED_3POINT5_MM_AUDIO
                        : SysUiStatsLog
                                .MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__WIRED_3POINT5_MM_AUDIO;
            case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE:
                return isSourceDevice
                        ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__USB_C_AUDIO
                        : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__USB_C_AUDIO;
            case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE:
                return isSourceDevice
                        ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__BLUETOOTH
                        : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__BLUETOOTH;
            case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE:
                return isSourceDevice
                        ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__REMOTE_SINGLE
                        : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__REMOTE_SINGLE;
            case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE:
                return isSourceDevice
                        ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__REMOTE_GROUP
                        : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__REMOTE_GROUP;
            default:
                return isSourceDevice
                        ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE
                        : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__UNKNOWN_TYPE;
        }
    }

    private int getLoggingSwitchOpSubResult(int reason) {
        switch (reason) {
            case REASON_REJECTED:
                return SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__REJECTED;
            case REASON_NETWORK_ERROR:
                return SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NETWORK_ERROR;
            case REASON_ROUTE_NOT_AVAILABLE:
                return SysUiStatsLog
                        .MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__ROUTE_NOT_AVAILABLE;
            case REASON_INVALID_COMMAND:
                return SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__INVALID_COMMAND;
            case REASON_UNKNOWN_ERROR:
            default:
                return SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__UNKNOWN_ERROR;
        }
    }

    private String getLoggingPackageName() {
        if (mPackageName != null && !mPackageName.isEmpty()) {
            try {
                final ApplicationInfo applicationInfo = mContext.getPackageManager()
                        .getApplicationInfo(mPackageName, /* default flag */ 0);
                if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
                        || (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
                    return mPackageName;
                }
            } catch (Exception ex) {
                Log.e(TAG, mPackageName + " is invalid.");
            }
        }

        return "";
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.widget.TextView;
import androidx.core.graphics.drawable.IconCompat;
import androidx.test.filters.SmallTest;

import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -61,6 +62,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
    private ActivityStarter mStarter = mock(ActivityStarter.class);
    private NotificationEntryManager mNotificationEntryManager =
            mock(NotificationEntryManager.class);
    private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);

    private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
    private MediaOutputController mMediaOutputController;
@@ -73,7 +75,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
    public void setUp() {
        mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
                mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
                mNotificationEntryManager);
                mNotificationEntryManager, mUiEventLogger);
        mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
                mMediaOutputController);
        mMediaOutputBaseDialogImpl.onCreate(new Bundle());
Loading