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 * @param source the current connected media device 60 * @param target the target media device for content switching to 61 */ updateOutputEndPoints(MediaDevice source, MediaDevice target)62 public void updateOutputEndPoints(MediaDevice source, MediaDevice target) { 63 mSourceDevice = source; 64 mTargetDevice = target; 65 66 if (DEBUG) { 67 Log.d(TAG, "updateOutputEndPoints -" 68 + " source:" + mSourceDevice.toString() 69 + " target:" + mTargetDevice.toString()); 70 } 71 } 72 73 /** 74 * Do the metric logging of content switching success. 75 * @param selectedDeviceType string representation of the target media device 76 * @param deviceList media device list for device count updating 77 */ logOutputSuccess(String selectedDeviceType, List<MediaDevice> deviceList)78 public void logOutputSuccess(String selectedDeviceType, List<MediaDevice> deviceList) { 79 if (DEBUG) { 80 Log.d(TAG, "logOutputSuccess - selected device: " + selectedDeviceType); 81 } 82 83 updateLoggingDeviceCount(deviceList); 84 85 SysUiStatsLog.write( 86 SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED, 87 getLoggingDeviceType(mSourceDevice, true), 88 getLoggingDeviceType(mTargetDevice, false), 89 SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__OK, 90 SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NO_ERROR, 91 getLoggingPackageName(), 92 mWiredDeviceCount, 93 mConnectedBluetoothDeviceCount, 94 mRemoteDeviceCount, 95 mAppliedDeviceCountWithinRemoteGroup); 96 } 97 98 /** 99 * Do the metric logging of content switching success. 100 * @param selectedDeviceType string representation of the target media device 101 * @param deviceItemList media item list for device count updating 102 */ logOutputItemSuccess(String selectedDeviceType, List<MediaItem> deviceItemList)103 public void logOutputItemSuccess(String selectedDeviceType, List<MediaItem> deviceItemList) { 104 if (DEBUG) { 105 Log.d(TAG, "logOutputSuccess - selected device: " + selectedDeviceType); 106 } 107 108 updateLoggingMediaItemCount(deviceItemList); 109 110 SysUiStatsLog.write( 111 SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED, 112 getLoggingDeviceType(mSourceDevice, true), 113 getLoggingDeviceType(mTargetDevice, false), 114 SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__OK, 115 SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NO_ERROR, 116 getLoggingPackageName(), 117 mWiredDeviceCount, 118 mConnectedBluetoothDeviceCount, 119 mRemoteDeviceCount, 120 mAppliedDeviceCountWithinRemoteGroup); 121 } 122 123 /** 124 * Do the metric logging of volume adjustment. 125 * @param source the device been adjusted 126 */ logInteractionAdjustVolume(MediaDevice source)127 public void logInteractionAdjustVolume(MediaDevice source) { 128 if (DEBUG) { 129 Log.d(TAG, "logInteraction - AdjustVolume"); 130 } 131 132 SysUiStatsLog.write( 133 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, 134 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__ADJUST_VOLUME, 135 getInteractionDeviceType(source), 136 getLoggingPackageName()); 137 } 138 139 /** 140 * Do the metric logging of stop casting. 141 */ logInteractionStopCasting()142 public void logInteractionStopCasting() { 143 if (DEBUG) { 144 Log.d(TAG, "logInteraction - Stop casting"); 145 } 146 147 SysUiStatsLog.write( 148 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, 149 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__STOP_CASTING, 150 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE, 151 getLoggingPackageName()); 152 } 153 154 /** 155 * Do the metric logging of device expansion. 156 */ logInteractionExpansion(MediaDevice source)157 public void logInteractionExpansion(MediaDevice source) { 158 if (DEBUG) { 159 Log.d(TAG, "logInteraction - Expansion"); 160 } 161 162 SysUiStatsLog.write( 163 SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT, 164 SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__EXPANSION, 165 getInteractionDeviceType(source), 166 getLoggingPackageName()); 167 } 168 169 /** 170 * Do the metric logging of content switching failure. 171 * @param deviceList media device list for device count updating 172 * @param reason the reason of content switching failure 173 */ logOutputFailure(List<MediaDevice> deviceList, int reason)174 public void logOutputFailure(List<MediaDevice> deviceList, int reason) { 175 if (DEBUG) { 176 Log.e(TAG, "logRequestFailed - " + reason); 177 } 178 179 updateLoggingDeviceCount(deviceList); 180 181 SysUiStatsLog.write( 182 SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED, 183 getLoggingDeviceType(mSourceDevice, true), 184 getLoggingDeviceType(mTargetDevice, false), 185 SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__ERROR, 186 getLoggingSwitchOpSubResult(reason), 187 getLoggingPackageName(), 188 mWiredDeviceCount, 189 mConnectedBluetoothDeviceCount, 190 mRemoteDeviceCount, 191 mAppliedDeviceCountWithinRemoteGroup); 192 } 193 194 /** 195 * Do the metric logging of content switching failure. 196 * @param deviceItemList media item list for device count updating 197 * @param reason the reason of content switching failure 198 */ logOutputItemFailure(List<MediaItem> deviceItemList, int reason)199 public void logOutputItemFailure(List<MediaItem> deviceItemList, int reason) { 200 if (DEBUG) { 201 Log.e(TAG, "logRequestFailed - " + reason); 202 } 203 204 updateLoggingMediaItemCount(deviceItemList); 205 206 SysUiStatsLog.write( 207 SysUiStatsLog.MEDIAOUTPUT_OP_SWITCH_REPORTED, 208 getLoggingDeviceType(mSourceDevice, true), 209 getLoggingDeviceType(mTargetDevice, false), 210 SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__RESULT__ERROR, 211 getLoggingSwitchOpSubResult(reason), 212 getLoggingPackageName(), 213 mWiredDeviceCount, 214 mConnectedBluetoothDeviceCount, 215 mRemoteDeviceCount, 216 mAppliedDeviceCountWithinRemoteGroup); 217 } 218 updateLoggingDeviceCount(List<MediaDevice> deviceList)219 private void updateLoggingDeviceCount(List<MediaDevice> deviceList) { 220 mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0; 221 mAppliedDeviceCountWithinRemoteGroup = 0; 222 223 for (MediaDevice mediaDevice : deviceList) { 224 if (mediaDevice.isConnected()) { 225 switch (mediaDevice.getDeviceType()) { 226 case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE: 227 case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE: 228 mWiredDeviceCount++; 229 break; 230 case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE: 231 mConnectedBluetoothDeviceCount++; 232 break; 233 case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE: 234 case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE: 235 mRemoteDeviceCount++; 236 break; 237 default: 238 } 239 } 240 } 241 242 if (DEBUG) { 243 Log.d(TAG, "connected devices:" + " wired: " + mWiredDeviceCount 244 + " bluetooth: " + mConnectedBluetoothDeviceCount 245 + " remote: " + mRemoteDeviceCount); 246 } 247 } 248 updateLoggingMediaItemCount(List<MediaItem> deviceItemList)249 private void updateLoggingMediaItemCount(List<MediaItem> deviceItemList) { 250 mWiredDeviceCount = mConnectedBluetoothDeviceCount = mRemoteDeviceCount = 0; 251 mAppliedDeviceCountWithinRemoteGroup = 0; 252 253 for (MediaItem mediaItem: deviceItemList) { 254 if (mediaItem.getMediaDevice().isPresent() 255 && mediaItem.getMediaDevice().get().isConnected()) { 256 switch (mediaItem.getMediaDevice().get().getDeviceType()) { 257 case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE: 258 case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE: 259 mWiredDeviceCount++; 260 break; 261 case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE: 262 mConnectedBluetoothDeviceCount++; 263 break; 264 case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE: 265 case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE: 266 mRemoteDeviceCount++; 267 break; 268 default: 269 } 270 } 271 } 272 273 if (DEBUG) { 274 Log.d(TAG, "connected devices:" + " wired: " + mWiredDeviceCount 275 + " bluetooth: " + mConnectedBluetoothDeviceCount 276 + " remote: " + mRemoteDeviceCount); 277 } 278 } 279 getLoggingDeviceType(MediaDevice device, boolean isSourceDevice)280 private int getLoggingDeviceType(MediaDevice device, boolean isSourceDevice) { 281 if (device == null) { 282 return isSourceDevice 283 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE 284 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__UNKNOWN_TYPE; 285 } 286 switch (device.getDeviceType()) { 287 case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE: 288 return isSourceDevice 289 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__BUILTIN_SPEAKER 290 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__BUILTIN_SPEAKER; 291 case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE: 292 return isSourceDevice 293 ? SysUiStatsLog 294 .MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__WIRED_3POINT5_MM_AUDIO 295 : SysUiStatsLog 296 .MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__WIRED_3POINT5_MM_AUDIO; 297 case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE: 298 return isSourceDevice 299 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__USB_C_AUDIO 300 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__USB_C_AUDIO; 301 case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE: 302 return isSourceDevice 303 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__BLUETOOTH 304 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__BLUETOOTH; 305 case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE: 306 return isSourceDevice 307 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__REMOTE_SINGLE 308 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__REMOTE_SINGLE; 309 case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE: 310 return isSourceDevice 311 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__REMOTE_GROUP 312 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__REMOTE_GROUP; 313 default: 314 return isSourceDevice 315 ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE 316 : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__UNKNOWN_TYPE; 317 } 318 } 319 getInteractionDeviceType(MediaDevice device)320 private int getInteractionDeviceType(MediaDevice device) { 321 if (device == null) { 322 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE; 323 } 324 switch (device.getDeviceType()) { 325 case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE: 326 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__BUILTIN_SPEAKER; 327 case MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE: 328 return SysUiStatsLog 329 .MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__WIRED_3POINT5_MM_AUDIO; 330 case MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE: 331 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__USB_C_AUDIO; 332 case MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE: 333 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__BLUETOOTH; 334 case MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE: 335 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__REMOTE_SINGLE; 336 case MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE: 337 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__REMOTE_GROUP; 338 default: 339 return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE; 340 } 341 } 342 343 getLoggingSwitchOpSubResult(int reason)344 private int getLoggingSwitchOpSubResult(int reason) { 345 switch (reason) { 346 case REASON_REJECTED: 347 return SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__REJECTED; 348 case REASON_NETWORK_ERROR: 349 return SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__NETWORK_ERROR; 350 case REASON_ROUTE_NOT_AVAILABLE: 351 return SysUiStatsLog 352 .MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__ROUTE_NOT_AVAILABLE; 353 case REASON_INVALID_COMMAND: 354 return SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__INVALID_COMMAND; 355 case REASON_UNKNOWN_ERROR: 356 default: 357 return SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SUBRESULT__UNKNOWN_ERROR; 358 } 359 } 360 getLoggingPackageName()361 private String getLoggingPackageName() { 362 if (mPackageName != null && !mPackageName.isEmpty()) { 363 try { 364 final ApplicationInfo applicationInfo = mContext.getPackageManager() 365 .getApplicationInfo(mPackageName, /* default flag */ 0); 366 if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 367 || (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { 368 return mPackageName; 369 } 370 } catch (Exception ex) { 371 Log.e(TAG, mPackageName + " is invalid."); 372 } 373 } 374 375 return ""; 376 } 377 } 378