1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.media.dialog; 18 19 import static android.media.MediaRoute2ProviderService.REASON_INVALID_COMMAND; 20 import static android.media.MediaRoute2ProviderService.REASON_NETWORK_ERROR; 21 import static android.media.MediaRoute2ProviderService.REASON_REJECTED; 22 import static android.media.MediaRoute2ProviderService.REASON_ROUTE_NOT_AVAILABLE; 23 import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR; 24 25 import android.content.Context; 26 import android.content.pm.ApplicationInfo; 27 import android.util.Log; 28 29 import com.android.settingslib.media.MediaDevice; 30 import com.android.systemui.shared.system.SysUiStatsLog; 31 32 import java.util.List; 33 34 /** 35 * Metric logger for media output features 36 */ 37 public class MediaOutputMetricLogger { 38 39 private static final String TAG = "MediaOutputMetricLogger"; 40 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 41 42 private final Context mContext; 43 private final String mPackageName; 44 private MediaDevice mSourceDevice, mTargetDevice; 45 private int mWiredDeviceCount; 46 private int mConnectedBluetoothDeviceCount; 47 private int mRemoteDeviceCount; 48 private int mAppliedDeviceCountWithinRemoteGroup; 49 MediaOutputMetricLogger(Context context, String packageName)50 public MediaOutputMetricLogger(Context context, String packageName) { 51 mContext = context; 52 mPackageName = packageName; 53 } 54 55 /** 56 * Update the endpoints of a content switching operation. 57 * This method should be called before a switching operation, so the metric logger can track 58 * source and target devices. 59 * 60 * @param source the current connected media device 61 * @param target the target media device for content switching to 62 */ updateOutputEndPoints(MediaDevice source, MediaDevice target)63 public void updateOutputEndPoints(MediaDevice source, MediaDevice target) { 64 mSourceDevice = source; 65 mTargetDevice = target; 66 67 if (DEBUG) { 68 Log.d(TAG, "updateOutputEndPoints -" 69 + " source:" + mSourceDevice.toString() 70 + " target:" + mTargetDevice.toString()); 71 } 72 } 73 74 /** 75 * Do the metric logging of content switching success. 76 * 77 * @param selectedDeviceType string representation of the target media device 78 * @param deviceItemList media item list for device count updating 79 */ logOutputItemSuccess(String selectedDeviceType, List<MediaItem> deviceItemList)80 public void logOutputItemSuccess(String selectedDeviceType, List<MediaItem> deviceItemList) { 81 if (DEBUG) { 82 Log.d(TAG, "logOutputSuccess - selected device: " + selectedDeviceType); 83 } 84 85 if (mSourceDevice == null && mTargetDevice == null) { 86 return; 87 } 88 89 updateLoggingMediaItemCount(deviceItemList); 90 91 SysUiStatsLog.write( 92 SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED, 93 getLoggingDeviceType(mSourceDevice, true), 94 getLoggingDeviceType(mTargetDevice, false), 95 SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__OK, 96 SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NO_ERROR, 97 getLoggingPackageName(), 98 mWiredDeviceCount, 99 mConnectedBluetoothDeviceCount, 100 mRemoteDeviceCount, 101 mAppliedDeviceCountWithinRemoteGroup, 102 mTargetDevice.isSuggestedDevice(), 103 mTargetDevice.hasOngoingSession()); 104 } 105 106 /** 107 * Do the metric logging of volume adjustment. 108 * 109 * @param source the device been adjusted 110 */ logInteractionAdjustVolume(MediaDevice source)111 public void logInteractionAdjustVolume(MediaDevice source) { 112 if (DEBUG) { 113 Log.d(TAG, "logInteraction - AdjustVolume"); 114 } 115 116 SysUiStatsLog.write( 117 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, 118 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__ADJUST_VOLUME, 119 getInteractionDeviceType(source), 120 getLoggingPackageName(), 121 source.isSuggestedDevice()); 122 } 123 124 /** 125 * Do the metric logging of stop casting. 126 */ logInteractionStopCasting()127 public void logInteractionStopCasting() { 128 if (DEBUG) { 129 Log.d(TAG, "logInteraction - Stop casting"); 130 } 131 132 SysUiStatsLog.write( 133 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, 134 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__STOP_CASTING, 135 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE, 136 getLoggingPackageName(), 137 /*isSuggestedDevice = */false); 138 } 139 140 /** 141 * Do the metric logging of device expansion. 142 */ logInteractionExpansion(MediaDevice source)143 public void logInteractionExpansion(MediaDevice source) { 144 if (DEBUG) { 145 Log.d(TAG, "logInteraction - Expansion"); 146 } 147 148 SysUiStatsLog.write( 149 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, 150 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__EXPANSION, 151 getInteractionDeviceType(source), 152 getLoggingPackageName(), 153 source.isSuggestedDevice()); 154 } 155 156 /** 157 * Do the metric logging of muting device. 158 */ logInteractionMute(MediaDevice source)159 public void logInteractionMute(MediaDevice source) { 160 if (DEBUG) { 161 Log.d(TAG, "logInteraction - Mute"); 162 } 163 164 SysUiStatsLog.write( 165 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, 166 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__MUTE, 167 getInteractionDeviceType(source), 168 getLoggingPackageName(), 169 source.isSuggestedDevice()); 170 } 171 172 /** 173 * Do the metric logging of unmuting device. 174 */ logInteractionUnmute(MediaDevice source)175 public void logInteractionUnmute(MediaDevice source) { 176 if (DEBUG) { 177 Log.d(TAG, "logInteraction - Unmute"); 178 } 179 180 SysUiStatsLog.write( 181 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, 182 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__UNMUTE, 183 getInteractionDeviceType(source), 184 getLoggingPackageName(), 185 source.isSuggestedDevice()); 186 } 187 188 /** 189 * Do the metric logging of content switching failure. 190 * 191 * @param deviceItemList media item list for device count updating 192 * @param reason the reason of content switching failure 193 */ logOutputItemFailure(List<MediaItem> deviceItemList, int reason)194 public void logOutputItemFailure(List<MediaItem> deviceItemList, int reason) { 195 if (DEBUG) { 196 Log.e(TAG, "logRequestFailed - " + reason); 197 } 198 199 if (mSourceDevice == null && mTargetDevice == null) { 200 return; 201 } 202 203 updateLoggingMediaItemCount(deviceItemList); 204 205 SysUiStatsLog.write( 206 SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED, 207 getLoggingDeviceType(mSourceDevice, true), 208 getLoggingDeviceType(mTargetDevice, false), 209 SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__ERROR, 210 getLoggingSwitchOpSubResult(reason), 211 getLoggingPackageName(), 212 mWiredDeviceCount, 213 mConnectedBluetoothDeviceCount, 214 mRemoteDeviceCount, 215 mAppliedDeviceCountWithinRemoteGroup, 216 mTargetDevice.isSuggestedDevice(), 217 mTargetDevice.hasOngoingSession()); 218 } 219 updateLoggingDeviceCount(List<MediaDevice> deviceList)220 private void updateLoggingDeviceCount(List<MediaDevice> deviceList) { 221 mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0; 222 mAppliedDeviceCountWithinRemoteGroup = 0; 223 224 for (MediaDevice mediaDevice : deviceList) { 225 if (mediaDevice.isConnected()) { 226 switch (mediaDevice.getDeviceType()) { 227 case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE: 228 case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE: 229 mWiredDeviceCount++; 230 break; 231 case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE: 232 mConnectedBluetoothDeviceCount++; 233 break; 234 case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE: 235 case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE: 236 mRemoteDeviceCount++; 237 break; 238 default: 239 } 240 } 241 } 242 243 if (DEBUG) { 244 Log.d(TAG, "connected devices:" + " wired: " + mWiredDeviceCount 245 + " bluetooth: " + mConnectedBluetoothDeviceCount 246 + " remote: " + mRemoteDeviceCount); 247 } 248 } 249 updateLoggingMediaItemCount(List<MediaItem> deviceItemList)250 private void updateLoggingMediaItemCount(List<MediaItem> deviceItemList) { 251 mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0; 252 mAppliedDeviceCountWithinRemoteGroup = 0; 253 254 for (MediaItem mediaItem : deviceItemList) { 255 if (mediaItem.getMediaDevice().isPresent() 256 && mediaItem.getMediaDevice().get().isConnected()) { 257 switch (mediaItem.getMediaDevice().get().getDeviceType()) { 258 case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE: 259 case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE: 260 mWiredDeviceCount++; 261 break; 262 case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE: 263 mConnectedBluetoothDeviceCount++; 264 break; 265 case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE: 266 case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE: 267 mRemoteDeviceCount++; 268 break; 269 default: 270 } 271 } 272 } 273 274 if (DEBUG) { 275 Log.d(TAG, "connected devices:" + " wired: " + mWiredDeviceCount 276 + " bluetooth: " + mConnectedBluetoothDeviceCount 277 + " remote: " + mRemoteDeviceCount); 278 } 279 } 280 getLoggingDeviceType(MediaDevice device, boolean isSourceDevice)281 private int getLoggingDeviceType(MediaDevice device, boolean isSourceDevice) { 282 if (device == null) { 283 return isSourceDevice 284 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE 285 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__UNKNOWN_TYPE; 286 } 287 switch (device.getDeviceType()) { 288 case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE: 289 return isSourceDevice 290 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__BUILTIN_SPEAKER 291 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__BUILTIN_SPEAKER; 292 case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE: 293 return isSourceDevice 294 ? SysUiStatsLog 295 .MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__WIRED_3POINT5_MM_AUDIO 296 : SysUiStatsLog 297 .MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__WIRED_3POINT5_MM_AUDIO; 298 case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE: 299 return isSourceDevice 300 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__USB_C_AUDIO 301 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__USB_C_AUDIO; 302 case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE: 303 return isSourceDevice 304 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__BLUETOOTH 305 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__BLUETOOTH; 306 case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE: 307 return isSourceDevice 308 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__REMOTE_SINGLE 309 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__REMOTE_SINGLE; 310 case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE: 311 return isSourceDevice 312 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__REMOTE_GROUP 313 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__REMOTE_GROUP; 314 case MediaDevice.MediaDeviceType.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER: 315 return isSourceDevice 316 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__AVR 317 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__AVR; 318 default: 319 return isSourceDevice 320 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE 321 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__UNKNOWN_TYPE; 322 } 323 } 324 getInteractionDeviceType(MediaDevice device)325 private int getInteractionDeviceType(MediaDevice device) { 326 if (device == null) { 327 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE; 328 } 329 switch (device.getDeviceType()) { 330 case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE: 331 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__BUILTIN_SPEAKER; 332 case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE: 333 return SysUiStatsLog 334 .MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__WIRED_3POINT5_MM_AUDIO; 335 case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE: 336 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__USB_C_AUDIO; 337 case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE: 338 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__BLUETOOTH; 339 case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE: 340 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__REMOTE_SINGLE; 341 case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE: 342 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__REMOTE_GROUP; 343 default: 344 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE; 345 } 346 } 347 348 getLoggingSwitchOpSubResult(int reason)349 private int getLoggingSwitchOpSubResult(int reason) { 350 switch (reason) { 351 case REASON_REJECTED: 352 return SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__REJECTED; 353 case REASON_NETWORK_ERROR: 354 return SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NETWORK_ERROR; 355 case REASON_ROUTE_NOT_AVAILABLE: 356 return SysUiStatsLog 357 .MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__ROUTE_NOT_AVAILABLE; 358 case REASON_INVALID_COMMAND: 359 return SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__INVALID_COMMAND; 360 case REASON_UNKNOWN_ERROR: 361 default: 362 return SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__UNKNOWN_ERROR; 363 } 364 } 365 getLoggingPackageName()366 private String getLoggingPackageName() { 367 if (mPackageName != null && !mPackageName.isEmpty()) { 368 try { 369 final ApplicationInfo applicationInfo = mContext.getPackageManager() 370 .getApplicationInfo(mPackageName, /* default flag */ 0); 371 if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 372 || (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { 373 return mPackageName; 374 } 375 } catch (Exception ex) { 376 Log.e(TAG, mPackageName + " is invalid."); 377 } 378 } 379 380 return ""; 381 } 382 } 383