MediaMetrics instrumentation

Instrumenting MediaMetrics logging into multiple services:
AudioService, AudioServiceEvents, AudioDeviceInventory

Test: manual testing, mediametrics dumpsys
Bug: 149850236
Merged-In: I7fc5413a473d9526ddffc2a6e6583d0b2a7eaa2e
Change-Id: I8cc6f5cec66d74c9d118691e1953aea879769d46
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index c17ed3e..c72674a 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -33,6 +33,7 @@
 import android.media.AudioSystem;
 import android.media.IAudioRoutesObserver;
 import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.MediaMetrics;
 import android.os.Binder;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -64,6 +65,10 @@
     // lock to synchronize all access to mConnectedDevices and mApmConnectedDevices
     private final Object mDevicesLock = new Object();
 
+    //Audio Analytics ids.
+    private static final String mAnalyticsId = "audio.deviceInventory.";
+    private static final String mAnalyticsPropEarlyReturn = "earlyReturn";
+
     // List of connected devices
     // Key for map created from DeviceInfo.makeDeviceListKey()
     @GuardedBy("mDevicesLock")
@@ -242,6 +247,15 @@
             final DeviceInfo di = mConnectedDevices.get(key);
             boolean isConnected = di != null;
 
+            new MediaMetrics.Item(mAnalyticsId + "onSetA2dpSinkConnectionState")
+                    .putInt("state", state)
+                    .putString("address", address)
+                    .putInt("a2dpCodec", a2dpCodec)
+                    .putInt("a2dpVolume", a2dpVolume)
+                    .putString("key", key)
+                    .putInt("isConnected", isConnected ? 1 : 0)
+                    .record();
+
             if (isConnected) {
                 if (state == BluetoothProfile.STATE_CONNECTED) {
                     // device is already connected, but we are receiving a connection again,
@@ -284,6 +298,13 @@
             final DeviceInfo di = mConnectedDevices.get(key);
             boolean isConnected = di != null;
 
+            new MediaMetrics.Item(mAnalyticsId + "onSetA2dpSourceConnectionState")
+                    .putInt("state", state)
+                    .putString("address", address)
+                    .putString("key", key)
+                    .putInt("isConnected", isConnected ? 1 : 0)
+                    .record();
+
             if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                 makeA2dpSrcUnavailable(address);
             } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
@@ -307,6 +328,14 @@
             final DeviceInfo di = mConnectedDevices.get(key);
             boolean isConnected = di != null;
 
+            new MediaMetrics.Item(mAnalyticsId + "onSetHearingAidConnectionState")
+                    .putInt("state", state)
+                    .putInt("streamType", streamType)
+                    .putString("address", address)
+                    .putString("key", key)
+                    .putInt("isConnected", isConnected ? 1 : 0)
+                    .record();
+
             if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                 makeHearingAidDeviceUnavailable(address);
             } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
@@ -319,8 +348,13 @@
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ void onBluetoothA2dpActiveDeviceChange(
             @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) {
+        MediaMetrics.Item mmi = new MediaMetrics.Item(mAnalyticsId
+                + "onBluetoothA2dpActiveDeviceChange")
+                .putInt("event", event);
+
         final BluetoothDevice btDevice = btInfo.getBtDevice();
         if (btDevice == null) {
+            mmi.putString(mAnalyticsPropEarlyReturn, "btDevice null").record();
             return;
         }
         if (AudioService.DEBUG_DEVICES) {
@@ -341,6 +375,7 @@
             if (mDeviceBroker.hasScheduledA2dpSinkConnectionState(btDevice)) {
                 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
                         "A2dp config change ignored (scheduled connection change)"));
+                mmi.putString(mAnalyticsPropEarlyReturn, "A2dp config change ignored").record();
                 return;
             }
             final String key = DeviceInfo.makeDeviceListKey(
@@ -348,9 +383,15 @@
             final DeviceInfo di = mConnectedDevices.get(key);
             if (di == null) {
                 Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpActiveDeviceChange");
+                mmi.putString(mAnalyticsPropEarlyReturn, "null DeviceInfo").record();
                 return;
             }
 
+            mmi.putString("address", address)
+                    .putInt("a2dpCodec", a2dpCodec)
+                    .putInt("a2dpVolume", a2dpVolume)
+                    .putString("key", key);
+
             if (event == BtHelper.EVENT_ACTIVE_DEVICE_CHANGE) {
                 // Device is connected
                 if (a2dpVolume != -1) {
@@ -382,12 +423,14 @@
                         btDevice, BluetoothA2dp.STATE_DISCONNECTED, BluetoothProfile.A2DP,
                         false /* suppressNoisyIntent */, musicDevice,
                         -1 /* a2dpVolume */);
+                mmi.putInt("musicDevice", musicDevice);
             } else {
                 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
                         "APM handleDeviceConfigChange success for A2DP device addr="
                                 + address + " codec=" + a2dpCodec).printLog(TAG));
             }
         }
+        mmi.record();
     }
 
     /*package*/ void onMakeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
@@ -399,6 +442,8 @@
     /*package*/ void onReportNewRoutes() {
         int n = mRoutesObservers.beginBroadcast();
         if (n > 0) {
+            new MediaMetrics.Item(mAnalyticsId + "onReportNewRoutes")
+                    .putInt("routesObservers", n).record();
             AudioRoutesInfo routes;
             synchronized (mCurAudioRoutes) {
                 routes = new AudioRoutesInfo(mCurAudioRoutes);
@@ -428,16 +473,24 @@
                             AudioDeviceInventory.WiredDeviceConnectionState wdcs) {
         AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs));
 
+        MediaMetrics.Item mmi = new MediaMetrics.Item(mAnalyticsId
+                + "onSetWiredDeviceConnectionState")
+                .putInt("wdcs.mState", wdcs.mState)
+                .putInt("wdcs.mType", wdcs.mType);
+
         synchronized (mDevicesLock) {
             if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED)
                     && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) {
                 mDeviceBroker.setBluetoothA2dpOnInt(true,
                         "onSetWiredDeviceConnectionState state DISCONNECTED");
+                mmi.putInt("setBluetoothA2dpOnInt", 1);
             }
 
             if (!handleDeviceConnection(wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED,
                     wdcs.mType, wdcs.mAddress, wdcs.mName)) {
                 // change of connection state failed, bailout
+                mmi.putString(mAnalyticsPropEarlyReturn, "change of connection state failed")
+                        .record();
                 return;
             }
             if (wdcs.mState != AudioService.CONNECTION_STATE_DISCONNECTED) {
@@ -446,22 +499,30 @@
                             "onSetWiredDeviceConnectionState state not DISCONNECTED");
                 }
                 mDeviceBroker.checkMusicActive(wdcs.mType, wdcs.mCaller);
+                mmi.putInt("setBluetoothA2dpOnInt", 0);
             }
             if (wdcs.mType == AudioSystem.DEVICE_OUT_HDMI) {
                 mDeviceBroker.checkVolumeCecOnHdmiConnection(wdcs.mState, wdcs.mCaller);
             }
             sendDeviceConnectionIntent(wdcs.mType, wdcs.mState, wdcs.mAddress, wdcs.mName);
             updateAudioRoutes(wdcs.mType, wdcs.mState);
+
+            mmi.putString("wdcs.mAddress", wdcs.mAddress != null ? wdcs.mAddress : "")
+                    .putString("wdcs.mName", wdcs.mName != null ? wdcs.mName : "")
+                    .record();
         }
     }
 
     /*package*/ void onToggleHdmi() {
+        MediaMetrics.Item mmi = new MediaMetrics.Item(mAnalyticsId + "onToggleHdmi");
         synchronized (mDevicesLock) {
             // Is HDMI connected?
             final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HDMI, "");
             final DeviceInfo di = mConnectedDevices.get(key);
+            mmi.putString("key", key);
             if (di == null) {
                 Log.e(TAG, "invalid null DeviceInfo in onToggleHdmi");
+                mmi.putString(mAnalyticsPropEarlyReturn, "invalid null DeviceInfo").record();
                 return;
             }
             // Toggle HDMI to retrigger broadcast with proper formats.
@@ -472,6 +533,7 @@
                     AudioSystem.DEVICE_STATE_AVAILABLE, "", "",
                     "android"); // reconnect
         }
+        mmi.record();
     }
 
     /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDeviceAttributes device) {
@@ -535,6 +597,11 @@
                     + Integer.toHexString(device) + " address:" + address
                     + " name:" + deviceName + ")");
         }
+        MediaMetrics.Item mmi = new MediaMetrics.Item(mAnalyticsId + "handleDeviceConnection")
+                .putInt("connect", connect ? 1 : 0)
+                .putInt("device", device)
+                .putString("address", address != null ? address : "")
+                .putString("deviceName", deviceName != null ? deviceName : "");
         synchronized (mDevicesLock) {
             final String deviceKey = DeviceInfo.makeDeviceListKey(device, address);
             if (AudioService.DEBUG_DEVICES) {
@@ -545,6 +612,8 @@
             if (AudioService.DEBUG_DEVICES) {
                 Slog.i(TAG, "deviceInfo:" + di + " is(already)Connected:" + isConnected);
             }
+            mmi.putString("deviceKey", deviceKey)
+                    .putInt("isConnected", isConnected ? 1 : 0);
             if (connect && !isConnected) {
                 final int res = mAudioSystem.setDeviceConnectionState(device,
                         AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName,
@@ -552,11 +621,14 @@
                 if (res != AudioSystem.AUDIO_STATUS_OK) {
                     Slog.e(TAG, "not connecting device 0x" + Integer.toHexString(device)
                             + " due to command error " + res);
+                    mmi.putInt("command error", res)
+                            .putInt("result", 0).record();
                     return false;
                 }
                 mConnectedDevices.put(deviceKey, new DeviceInfo(
                         device, deviceName, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
                 mDeviceBroker.postAccessoryPlugMediaUnmute(device);
+                mmi.putInt("result", 1).record();
                 return true;
             } else if (!connect && isConnected) {
                 mAudioSystem.setDeviceConnectionState(device,
@@ -564,11 +636,13 @@
                         AudioSystem.AUDIO_FORMAT_DEFAULT);
                 // always remove even if disconnection failed
                 mConnectedDevices.remove(deviceKey);
+                mmi.putInt("result", 1).record();
                 return true;
             }
             Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey
                     + ", deviceSpec=" + di + ", connect=" + connect);
         }
+        mmi.putInt("result", 0).record();
         return false;
     }
 
@@ -582,6 +656,8 @@
                     toRemove.add(deviceInfo.mDeviceAddress);
                 }
             });
+            new MediaMetrics.Item(mAnalyticsId + "disconnectA2dp")
+                    .putInt("toRemove.size", toRemove.size()).record();
             if (toRemove.size() > 0) {
                 final int delay = checkSendBecomingNoisyIntentInt(
                         AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
@@ -602,6 +678,8 @@
                     toRemove.add(deviceInfo.mDeviceAddress);
                 }
             });
+            new MediaMetrics.Item(mAnalyticsId + "disconnectA2dpSink")
+                    .putInt("toRemove.size", toRemove.size()).record();
             toRemove.stream().forEach(deviceAddress -> makeA2dpSrcUnavailable(deviceAddress));
         }
     }
@@ -615,6 +693,8 @@
                     toRemove.add(deviceInfo.mDeviceAddress);
                 }
             });
+            new MediaMetrics.Item(mAnalyticsId + "disconnectHearingAid")
+                    .putInt("toRemove.size", toRemove.size()).record();
             if (toRemove.size() > 0) {
                 final int delay = checkSendBecomingNoisyIntentInt(
                         AudioSystem.DEVICE_OUT_HEARING_AID, 0, AudioSystem.DEVICE_NONE);
@@ -771,18 +851,27 @@
 
     @GuardedBy("mDevicesLock")
     private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
+        MediaMetrics.Item mmi = new MediaMetrics.Item(mAnalyticsId
+                + "makeA2dpDeviceUnavailableNow")
+                .putInt("a2dpCodec", a2dpCodec);
         if (address == null) {
+            mmi.putString(mAnalyticsPropEarlyReturn, "address null").record();
             return;
         }
         final String deviceToRemoveKey =
                 DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
 
+        mmi.putString("address", address)
+                .putString("deviceToRemoveKey", deviceToRemoveKey);
+
         mConnectedDevices.remove(deviceToRemoveKey);
         if (!deviceToRemoveKey
                 .equals(mApmConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) {
             // removing A2DP device not currently used by AudioPolicy, log but don't act on it
             AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
                     "A2DP device " + address + " made unavailable, was not used")).printLog(TAG));
+            mmi.putString(mAnalyticsPropEarlyReturn, "A2DP device made unavailable, was not used")
+                    .record();
             return;
         }
 
@@ -804,6 +893,7 @@
         mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
         // Remove A2DP routes as well
         setCurrentAudioRouteNameIfPossible(null);
+        mmi.record();
     }
 
     @GuardedBy("mDevicesLock")
@@ -873,6 +963,8 @@
                 DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address));
         // Remove Hearing Aid routes as well
         setCurrentAudioRouteNameIfPossible(null);
+        new MediaMetrics.Item(mAnalyticsId + "makeHearingAidDeviceUnavailable")
+                .putString("address", address != null ? address : "").record();
     }
 
     @GuardedBy("mDevicesLock")
@@ -919,10 +1011,17 @@
     @GuardedBy("mDevicesLock")
     private int checkSendBecomingNoisyIntentInt(int device,
             @AudioService.ConnectionState int state, int musicDevice) {
+        MediaMetrics.Item mmi = new MediaMetrics.Item(mAnalyticsId
+                + "checkSendBecomingNoisyIntentInt")
+                .putInt("device", device)
+                .putInt("state", state)
+                .putInt("musicDevice", musicDevice);
         if (state != AudioService.CONNECTION_STATE_DISCONNECTED) {
+            mmi.putString(mAnalyticsPropEarlyReturn, "CONNECTION_STATE_DISCONNECTED").record();
             return 0;
         }
         if (!BECOMING_NOISY_INTENT_DEVICES_SET.contains(device)) {
+            mmi.putString(mAnalyticsPropEarlyReturn, "doesn't contains device").record();
             return 0;
         }
         int delay = 0;
@@ -950,12 +1049,14 @@
                 // the pausing of some apps that are playing remotely
                 AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
                         "dropping ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG));
+                mmi.putString(mAnalyticsPropEarlyReturn, "no media playback").record();
                 return 0;
             }
             mDeviceBroker.postBroadcastBecomingNoisy();
             delay = AudioService.BECOMING_NOISY_DELAY_MS;
         }
 
+        mmi.putInt("delay", delay).record();
         return delay;
     }
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f840f2d..043e338 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -88,6 +88,7 @@
 import android.media.IVolumeController;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
+import android.media.MediaMetrics;
 import android.media.PlayerBase;
 import android.media.VolumePolicy;
 import android.media.audiofx.AudioEffect;
@@ -1884,6 +1885,15 @@
             sendMsg(mAudioHandler, MSG_NOTIFY_VOL_EVENT, SENDMSG_QUEUE,
                     direction, 0 /*ignored*/,
                     extVolCtlr, 0 /*delay*/);
+
+            new MediaMetrics.Item(mAnalyticsId + "adjustSuggestedStreamVolume")
+                    .setUid(Binder.getCallingUid())
+                    .putInt("extVolCtlr", extVolCtlr != null ? 1 : 0)
+                    .putInt("direction", direction)
+                    .putInt("flags", flags)
+                    .putString("callingPackage", callingPackage)
+                    .putString("caller", caller)
+                    .record();
         } else {
             adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
                     caller, Binder.getCallingUid());
@@ -1970,12 +1980,22 @@
         if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream=" + streamType + ", dir=" + direction
                 + ", flags=" + flags + ", caller=" + caller);
 
+        MediaMetrics.Item mmi = new MediaMetrics.Item(mAnalyticsId + "adjustStreamVolume")
+                .setUid(uid)
+                .setPid(Binder.getCallingPid())
+                .putInt("streamType", streamType)
+                .putInt("direction", direction)
+                .putInt("flags", flags)
+                .putString("callingPackage", callingPackage)
+                .putString("caller", caller);
+
         ensureValidDirection(direction);
         ensureValidStreamType(streamType);
 
         boolean isMuteAdjust = isMuteAdjust(direction);
 
         if (isMuteAdjust && !isStreamAffectedByMute(streamType)) {
+            mmi.putString(mAnalyticsPropEarlyReturn, "isMuteAdjust").record();
             return;
         }
 
@@ -1989,6 +2009,7 @@
                     != PackageManager.PERMISSION_GRANTED) {
             Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid="
                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+            mmi.putString(mAnalyticsPropEarlyReturn, "Permission Denial").record();
             return;
         }
 
@@ -2020,6 +2041,7 @@
         // is not an a2dp device
         if (!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
                 && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
+            mmi.putString(mAnalyticsPropEarlyReturn, "skip a2dp").record();
             return;
         }
 
@@ -2030,6 +2052,7 @@
         }
         if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
                 != AppOpsManager.MODE_ALLOWED) {
+            mmi.putString(mAnalyticsPropEarlyReturn, "mode not allowed").record();
             return;
         }
 
@@ -2140,6 +2163,15 @@
                         0,
                         streamState,
                         0);
+
+                mmi.putInt("device", device)
+                        .putInt("streamType2", streamState.mStreamType)
+                        .putInt("isMuted", streamState.mIsMuted ? 1 : 0)
+                        .putInt("indexMin", streamState.mIndexMin)
+                        .putInt("indexMax", streamState.mIndexMax)
+                        .putInt("observedDevices", streamState.mObservedDevices)
+                        .putInt("flags2", flags)
+                        .putInt("streamTypeAlias", streamTypeAlias);
             }
 
             int newIndex = mStreamStates[streamType].getIndex(device);
@@ -2153,6 +2185,7 @@
                             + newIndex + "stream=" + streamType);
                 }
                 mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10);
+                mmi.putInt("postSetAvrcpAbsoluteVolumeIndex", newIndex / 10);
             }
 
             // Check if volume update should be send to Hearing Aid
@@ -2198,6 +2231,7 @@
                             try {
                                 mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, true);
                                 mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, false);
+                                mmi.putInt("HDMI.sendVolumeKeyEvent", keyCode);
                             } finally {
                                 Binder.restoreCallingIdentity(ident);
                             }
@@ -2211,6 +2245,7 @@
                 }
             }
         }
+        mmi.record();
         int index = mStreamStates[streamType].getIndex(device);
         sendVolumeUpdate(streamType, oldIndex, index, flags, device);
     }
@@ -2850,6 +2885,14 @@
             }
         }
         mVolumeController.postVolumeChanged(streamType, flags);
+
+        new MediaMetrics.Item(mAnalyticsId + "sendVolumeUpdate")
+                .putInt("streamType", streamType)
+                .putInt("oldIndex", oldIndex)
+                .putInt("index", index)
+                .putInt("flags", flags)
+                .putInt("device", device)
+                .record();
     }
 
     // If Hdmi-CEC system audio mode is on and we are a TV panel, never show volume bar.
@@ -2891,12 +2934,21 @@
                                     int device,
                                     boolean force,
                                     String caller) {
+        MediaMetrics.Item mmi = new MediaMetrics.Item(mAnalyticsId + "setStreamVolumeInt")
+                .putInt("streamType", streamType)
+                .putInt("index", index)
+                .putInt("device", device)
+                .putInt("force", force ? 1 : 0)
+                .putString("caller", caller);
+
         if (isFullVolumeDevice(device)) {
+            mmi.putString(mAnalyticsPropEarlyReturn, "mFullVolumeDevices");
             return;
         }
         VolumeStreamState streamState = mStreamStates[streamType];
 
         if (streamState.setIndex(index, device, caller) || force) {
+            mmi.putInt("sendMsg", 1);
             // Post message to set system volume (it in turn will post a message
             // to persist).
             sendMsg(mAudioHandler,
@@ -2907,6 +2959,7 @@
                     streamState,
                     0);
         }
+        mmi.record();
     }
 
     private void setSystemAudioMute(boolean state) {
@@ -3141,22 +3194,34 @@
         if (uid == android.os.Process.SYSTEM_UID) {
             uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
         }
+        MediaMetrics.Item mmi = new MediaMetrics.Item(mAnalyticsId + "setMicrophoneMute")
+                .setUid(uid)
+                .putInt("userId", userId)
+                .putString("callingPackage", callingPackage);
+
         // If OP_MUTE_MICROPHONE is set, disallow unmuting.
         if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage)
                 != AppOpsManager.MODE_ALLOWED) {
+            mmi.putString(mAnalyticsPropEarlyReturn, "disallow unmuting").record();
             return;
         }
         if (!checkAudioSettingsPermission("setMicrophoneMute()")) {
+            mmi.putString(mAnalyticsPropEarlyReturn, "!checkAudioSettingsPermission").record();
             return;
         }
         if (userId != UserHandle.getCallingUserId() &&
                 mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
                 != PackageManager.PERMISSION_GRANTED) {
+            mmi.putString(mAnalyticsPropEarlyReturn, "permission").record();
             return;
         }
         mMicMuteFromApi = on;
+
         setMicrophoneMuteNoCallerCheck(userId);
+
+        mmi.putInt("mMicMuteFromApi", mMicMuteFromApi ? 1 : 0)
+                .record();
     }
 
     /** @see AudioManager#setMicrophoneMuteFromSwitch(boolean) */
@@ -3167,6 +3232,10 @@
             return;
         }
         mMicMuteFromSwitch = on;
+        new MediaMetrics.Item(mAnalyticsId + "setMicrophoneMuteFromSwitch")
+                .setUid(userId)
+                .putInt("mMicMuteFromSwitch", mMicMuteFromSwitch ? 1 : 0)
+                .record();
         setMicrophoneMuteNoCallerCheck(userId);
     }
 
@@ -3174,6 +3243,9 @@
         InputManager im = mContext.getSystemService(InputManager.class);
         final int isMicMuted = im.isMicMuted();
         if (isMicMuted != InputManager.SWITCH_STATE_UNKNOWN) {
+            new MediaMetrics.Item(mAnalyticsId + "setMicMuteFromSwitchInput")
+                    .putInt("isMicMuted", isMicMuted)
+                    .record();
             setMicrophoneMuteFromSwitch(im.isMicMuted() != InputManager.SWITCH_STATE_OFF);
         }
     }
@@ -3207,6 +3279,14 @@
                 Log.e(TAG, "Error changing mic mute state to " + muted + " current:"
                         + mMicMuteFromSystemCached);
             }
+
+            new MediaMetrics.Item(mAnalyticsId + "setMicrophoneMuteNoCallerCheck")
+                    .setUid(userId)
+                    .putInt("muted", muted ? 1 : 0)
+                    .putInt("currentMute", mMicMuteFromSystemCached ? 1 : 0)
+                    .putLong("identity", identity)
+                    .record();
+
             try {
                 // send the intent even if there was a failure to change the actual mute state:
                 // the AudioManager.setMicrophoneMute API doesn't have a return value to
@@ -3326,6 +3406,13 @@
             synchronized (mSettingsLock) {
                 final int ringerModeInternal = getRingerModeInternal();
                 final int ringerModeExternal = getRingerModeExternal();
+                new MediaMetrics.Item(mAnalyticsId + "setRingerMode")
+                        .putInt("ringerMode", ringerMode)
+                        .putInt("external", external ? 1 : 0)
+                        .putString("caller", caller)
+                        .putInt("ringerModeInternal", ringerModeInternal)
+                        .putInt("ringerModeExternal", ringerModeExternal)
+                        .record();
                 if (external) {
                     setRingerModeExt(ringerMode);
                     if (mRingerModeDelegate != null) {
@@ -3839,6 +3926,10 @@
             Log.w(TAG, "AudioService effectType value " + effectType + " out of range");
             return;
         }
+        new MediaMetrics.Item(mAnalyticsId + "playSoundEffectVolume")
+                .putInt("effectType", effectType)
+                .putDouble("volume", volume)
+                .record();
 
         sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_QUEUE,
                 effectType, (int) (volume * 1000), null, 0);
@@ -3851,7 +3942,12 @@
     public boolean loadSoundEffects() {
         LoadSoundEffectReply reply = new LoadSoundEffectReply();
         sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, reply, 0);
-        return reply.waitForLoaded(3 /*attempts*/);
+
+        boolean loaded = reply.waitForLoaded(3 /*attempts*/);
+        new MediaMetrics.Item(mAnalyticsId + "loadSoundEffects")
+                .putInt("loaded", loaded ? 1 : 0)
+                .record();
+        return loaded;
     }
 
     /**
@@ -3860,6 +3956,8 @@
      */
     protected void scheduleLoadSoundEffects() {
         sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, null, 0);
+        new MediaMetrics.Item(mAnalyticsId + "scheduleLoadSoundEffects")
+                .record();
     }
 
     /**
@@ -3869,6 +3967,8 @@
      */
     public void unloadSoundEffects() {
         sendMsg(mAudioHandler, MSG_UNLOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, null, 0);
+        new MediaMetrics.Item(mAnalyticsId + "unloadSoundEffects")
+                .record();
     }
 
     /** @see AudioManager#reloadAudioSettings() */
@@ -3941,10 +4041,19 @@
         }
 
         // for logging only
+        final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
         final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
-                .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
-                .append(Binder.getCallingPid()).toString();
+                .append(") from u/pid:").append(uid).append("/")
+                .append(pid).toString();
         final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(on, eventSource);
+        new MediaMetrics.Item(mAnalyticsId + "setSpeakerphoneOn")
+                .setUid(uid)
+                .setPid(pid)
+                .putInt("on", on ? 1 : 0)
+                .putInt("stateChanged", stateChanged ? 1 : 0)
+                .record();
+
         if (stateChanged) {
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -3975,9 +4084,17 @@
         }
 
         // for logging only
+        final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
         final String eventSource = new StringBuilder("setBluetoothScoOn(").append(on)
-                .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
-                .append(Binder.getCallingPid()).toString();
+                .append(") from u/pid:").append(uid).append("/").append(pid).toString();
+
+        //bt sco
+        new MediaMetrics.Item(mAnalyticsId + "setBluetoothScoOn")
+                .setUid(uid)
+                .setPid(pid)
+                .putInt("on", on ? 1 : 0)
+                .record();
 
         mDeviceBroker.setBluetoothScoOn(on, eventSource);
     }
@@ -3993,9 +4110,18 @@
     /** @see AudioManager#setBluetoothA2dpOn(boolean) */
     public void setBluetoothA2dpOn(boolean on) {
         // for logging only
+        final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
         final String eventSource = new StringBuilder("setBluetoothA2dpOn(").append(on)
-                .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
-                .append(Binder.getCallingPid()).toString();
+                .append(") from u/pid:").append(uid).append("/")
+                .append(pid).toString();
+
+        new MediaMetrics.Item(mAnalyticsId + "setBluetoothA2dpOn")
+                .setUid(uid)
+                .setPid(pid)
+                .putInt("on", on ? 1 : 0)
+                .record();
+
         mDeviceBroker.setBluetoothA2dpOn_Async(on, eventSource);
     }
 
@@ -4006,31 +4132,52 @@
 
     /** @see AudioManager#startBluetoothSco() */
     public void startBluetoothSco(IBinder cb, int targetSdkVersion) {
+        final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
         final int scoAudioMode =
                 (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
                         BtHelper.SCO_MODE_VIRTUAL_CALL : BtHelper.SCO_MODE_UNDEFINED;
         final String eventSource = new StringBuilder("startBluetoothSco()")
-                .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
-                .append(Binder.getCallingPid()).toString();
+                .append(") from u/pid:").append(uid).append("/")
+                .append(pid).toString();
+
+        new MediaMetrics.Item(mAnalyticsId + "startBluetoothSco")
+                .setUid(uid)
+                .setPid(pid)
+                .putInt("scoAudioMode", scoAudioMode)
+                .record();
         startBluetoothScoInt(cb, scoAudioMode, eventSource);
+
     }
 
     /** @see AudioManager#startBluetoothScoVirtualCall() */
     public void startBluetoothScoVirtualCall(IBinder cb) {
+        final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
         final String eventSource = new StringBuilder("startBluetoothScoVirtualCall()")
-                .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
-                .append(Binder.getCallingPid()).toString();
+                .append(") from u/pid:").append(uid).append("/")
+                .append(pid).toString();
+
+        new MediaMetrics.Item(mAnalyticsId + "startBluetoothScoVirtualCall")
+                .setUid(uid)
+                .setPid(pid)
+                .record();
         startBluetoothScoInt(cb, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
     }
 
     void startBluetoothScoInt(IBinder cb, int scoAudioMode, @NonNull String eventSource) {
+        MediaMetrics.Item mmi = new MediaMetrics.Item(mAnalyticsId + "startBluetoothScoInt")
+                .putInt("scoAudioMode", scoAudioMode);
+
         if (!checkAudioSettingsPermission("startBluetoothSco()") ||
                 !mSystemReady) {
+            mmi.putString(mAnalyticsPropEarlyReturn, "permission or systemReady").record();
             return;
         }
         synchronized (mDeviceBroker.mSetModeLock) {
             mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
         }
+        mmi.record();
     }
 
     /** @see AudioManager#stopBluetoothSco() */
@@ -4039,12 +4186,18 @@
                 !mSystemReady) {
             return;
         }
+        final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
         final String eventSource =  new StringBuilder("stopBluetoothSco()")
-                .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
-                .append(Binder.getCallingPid()).toString();
+                .append(") from u/pid:").append(uid).append("/")
+                .append(pid).toString();
         synchronized (mDeviceBroker.mSetModeLock) {
             mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
         }
+        new MediaMetrics.Item(mAnalyticsId + "stopBluetoothSco")
+                .setUid(uid)
+                .setPid(pid)
+                .record();
     }
 
 
@@ -4806,6 +4959,13 @@
                 && state != CONNECTION_STATE_DISCONNECTED) {
             throw new IllegalArgumentException("Invalid state " + state);
         }
+        new MediaMetrics.Item(mAnalyticsId + "setWiredDeviceConnectionState")
+                .putInt("type", type)
+                .putInt("state", state)
+                .putString("address", address)
+                .putString("name", name)
+                .putString("caller", caller)
+                .record();
         mDeviceBroker.setWiredDeviceConnectionState(type, state, address, name, caller);
     }
 
@@ -6477,22 +6637,41 @@
             Log.e(TAG, "Invalid null parameter to request audio focus");
             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
         }
+        int uid = Binder.getCallingUid();
+        new MediaMetrics.Item(mAnalyticsId + "requestAudioFocus")
+                .setUid(uid)
+                .putInt("durationHint", durationHint)
+                .putString("clientId", clientId)
+                .putString("callingPackage", callingPackageName)
+                .putInt("flags", flags)
+                .putInt("sdk", sdk)
+                .record();
 
         return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
                 clientId, callingPackageName, flags, sdk,
-                forceFocusDuckingForAccessibility(aa, durationHint, Binder.getCallingUid()));
+                forceFocusDuckingForAccessibility(aa, durationHint, uid));
     }
 
     public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa,
             String callingPackageName) {
+        MediaMetrics.Item mmi = new MediaMetrics.Item(mAnalyticsId + "abandonAudioFocus")
+                .putString("clientId", clientId)
+                .putString("callingPackage", callingPackageName);
+
         if (aa != null && !isValidAudioAttributesUsage(aa)) {
             Log.w(TAG, "Request using unsupported usage.");
+            mmi.putString(mAnalyticsPropEarlyReturn, "unsupported usage").record();
+
             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
         }
+        mmi.record();
         return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName);
     }
 
     public void unregisterAudioFocusClient(String clientId) {
+        new MediaMetrics.Item(mAnalyticsId + "unregisterAudioFocusClient")
+                .putString("clientId", clientId)
+                .record();
         mMediaFocusControl.unregisterAudioFocusClient(clientId);
     }
 
@@ -7066,6 +7245,12 @@
         }
     }
 
+    /**
+     * Audio Analytics ids.
+     */
+    private static final String mAnalyticsId = "audio.service.";
+    private static final String mAnalyticsPropEarlyReturn = "earlyReturn";
+
     private static String safeMediaVolumeStateToString(int state) {
         switch(state) {
             case SAFE_MEDIA_VOLUME_NOT_CONFIGURED: return "SAFE_MEDIA_VOLUME_NOT_CONFIGURED";
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index add620e..5fdc3e4 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -19,6 +19,7 @@
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioSystem;
+import android.media.MediaMetrics;
 
 import com.android.server.audio.AudioDeviceInventory.WiredDeviceConnectionState;
 
@@ -108,6 +109,11 @@
         final String mGroupName;
         final AudioAttributes mAudioAttributes;
 
+        /**
+         * Audio Analytics unique Id.
+         */
+        private static final String mAnalyticsIdRoot = "audio.volumeEvent.";
+
         /** used for VOL_ADJUST_VOL_UID,
          *           VOL_ADJUST_SUGG_VOL,
          *           VOL_ADJUST_STREAM_VOL,
@@ -120,6 +126,13 @@
             mCaller = caller;
             mGroupName = null;
             mAudioAttributes = null;
+
+            new MediaMetrics.Item(mAnalyticsIdRoot + mStream)
+                    .putInt("op", mOp)
+                    .putInt("dir", mVal1)
+                    .putInt("flags", mVal2)
+                    .putString("from", mCaller)
+                    .record();
         }
 
         /** used for VOL_SET_HEARING_AID_VOL*/
@@ -132,6 +145,12 @@
             mCaller = null;
             mGroupName = null;
             mAudioAttributes = null;
+
+            new MediaMetrics.Item(mAnalyticsIdRoot + "HA")
+                    .putInt("op", mOp)
+                    .putInt("index", mVal1)
+                    .putInt("gainDb", mVal2)
+                    .record();
         }
 
         /** used for VOL_SET_AVRCP_VOL */
@@ -144,6 +163,11 @@
             mCaller = null;
             mGroupName = null;
             mAudioAttributes = null;
+
+            new MediaMetrics.Item(mAnalyticsIdRoot + "AVRCP")
+                    .putInt("op", mOp)
+                    .putInt("index", mVal1)
+                    .record();
         }
 
         /** used for VOL_VOICE_ACTIVITY_HEARING_AID */
@@ -156,6 +180,12 @@
             mCaller = null;
             mGroupName = null;
             mAudioAttributes = null;
+
+            new MediaMetrics.Item(mAnalyticsIdRoot + mStream)
+                    .putInt("op", mOp)
+                    .putInt("index", mVal1)
+                    .putInt("voiceActive", mVal2)
+                    .record();
         }
 
         /** used for VOL_MODE_CHANGE_HEARING_AID */
@@ -179,6 +209,12 @@
             mCaller = caller;
             mGroupName = group;
             mAudioAttributes = aa;
+
+            new MediaMetrics.Item(mAnalyticsIdRoot + mStream)
+                    .putInt("op", mOp)
+                    .putInt("index", mVal1)
+                    .putInt("mode", mVal2)
+                    .record();
         }
 
         @Override