diff options
author | Robert Luo <robertluo@google.com> | 2020-11-02 08:46:03 +0800 |
---|---|---|
committer | Robert Luo <robertluo@google.com> | 2020-12-09 15:40:52 +0800 |
commit | 23ccad79a7852e7f5bb2001a41a8d5bcac8c5ff6 (patch) | |
tree | 198b9ff345f142a82b1b3a2a2abf81ca4ab03e36 /packages/SystemUI/src | |
parent | d852f17eeb6d597d346ad5cdd537201997e4faa6 (diff) |
Migrate media output switcher metrics - 1/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: I4094a510b5483a456522da9d0d8a6eee1526f687
Merged-In: I0fea0c59afcb44fc1d22920036107524f221a7be
Diffstat (limited to 'packages/SystemUI/src')
4 files changed, 264 insertions, 6 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index 451bd42bd053..eb86128a82a5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -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() { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java index c0138f0e200a..fedeceabe73a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java @@ -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 javax.inject.Singleton; @@ -32,10 +35,12 @@ import javax.inject.Singleton; */ @Singleton 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); @@ -46,6 +51,7 @@ public class MediaOutputDialog extends MediaOutputBaseDialog { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mUiEventLogger.log(MediaOutputEvent.MEDIA_OUTPUT_DIALOG_SHOW); } @Override @@ -79,4 +85,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; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt index 7d1a7ced7472..0f340a5cedaa 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt @@ -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. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java new file mode 100644 index 000000000000..ac0295ed6d14 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java @@ -0,0 +1,221 @@ +/* + * 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 ""; + } +} |