/* * 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 deviceItemList media item list for device count updating */ public void logOutputItemSuccess(String selectedDeviceType, List deviceItemList) { if (DEBUG) { Log.d(TAG, "logOutputSuccess - selected device: " + selectedDeviceType); } if (mSourceDevice == null && mTargetDevice == null) { return; } updateLoggingMediaItemCount(deviceItemList); 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, mTargetDevice.isSuggestedDevice(), mTargetDevice.hasOngoingSession()); } /** * Do the metric logging of volume adjustment. * * @param source the device been adjusted */ public void logInteractionAdjustVolume(MediaDevice source) { if (DEBUG) { Log.d(TAG, "logInteraction - AdjustVolume"); } SysUiStatsLog.write( SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__ADJUST_VOLUME, getInteractionDeviceType(source), getLoggingPackageName(), source.isSuggestedDevice()); } /** * Do the metric logging of stop casting. */ public void logInteractionStopCasting() { if (DEBUG) { Log.d(TAG, "logInteraction - Stop casting"); } SysUiStatsLog.write( SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__STOP_CASTING, SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE, getLoggingPackageName(), /*isSuggestedDevice = */false); } /** * Do the metric logging of device expansion. */ public void logInteractionExpansion(MediaDevice source) { if (DEBUG) { Log.d(TAG, "logInteraction - Expansion"); } SysUiStatsLog.write( SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__EXPANSION, getInteractionDeviceType(source), getLoggingPackageName(), source.isSuggestedDevice()); } /** * Do the metric logging of muting device. */ public void logInteractionMute(MediaDevice source) { if (DEBUG) { Log.d(TAG, "logInteraction - Mute"); } SysUiStatsLog.write( SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__MUTE, getInteractionDeviceType(source), getLoggingPackageName(), source.isSuggestedDevice()); } /** * Do the metric logging of unmuting device. */ public void logInteractionUnmute(MediaDevice source) { if (DEBUG) { Log.d(TAG, "logInteraction - Unmute"); } SysUiStatsLog.write( SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__UNMUTE, getInteractionDeviceType(source), getLoggingPackageName(), source.isSuggestedDevice()); } /** * Do the metric logging of content switching failure. * * @param deviceItemList media item list for device count updating * @param reason the reason of content switching failure */ public void logOutputItemFailure(List deviceItemList, int reason) { if (DEBUG) { Log.e(TAG, "logRequestFailed - " + reason); } if (mSourceDevice == null && mTargetDevice == null) { return; } updateLoggingMediaItemCount(deviceItemList); 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, mTargetDevice.isSuggestedDevice(), mTargetDevice.hasOngoingSession()); } private void updateLoggingDeviceCount(List 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 void updateLoggingMediaItemCount(List deviceItemList) { mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0; mAppliedDeviceCountWithinRemoteGroup = 0; for (MediaItem mediaItem : deviceItemList) { if (mediaItem.getMediaDevice().isPresent() && mediaItem.getMediaDevice().get().isConnected()) { switch (mediaItem.getMediaDevice().get().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) { if (device == null) { return isSourceDevice ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__UNKNOWN_TYPE; } 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; case MediaDevice.MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER: return isSourceDevice ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__AVR : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__AVR; default: return isSourceDevice ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__UNKNOWN_TYPE; } } private int getInteractionDeviceType(MediaDevice device) { if (device == null) { return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE; } switch (device.getDeviceType()) { case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE: return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__BUILTIN_SPEAKER; case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE: return SysUiStatsLog .MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__WIRED_3POINT5_MM_AUDIO; case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE: return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__USB_C_AUDIO; case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE: return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__BLUETOOTH; case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE: return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__REMOTE_SINGLE; case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE: return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__REMOTE_GROUP; default: return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_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 ""; } }