diff options
9 files changed, 864 insertions, 23 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 3e1f72da8731..8ea68833e20d 100755 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2713,6 +2713,32 @@ public class AudioManager { } /** + * @hide + */ + public static String audioFocusToString(int focus) { + switch (focus) { + case AUDIOFOCUS_NONE: + return "AUDIOFOCUS_NONE"; + case AUDIOFOCUS_GAIN: + return "AUDIOFOCUS_GAIN"; + case AUDIOFOCUS_GAIN_TRANSIENT: + return "AUDIOFOCUS_GAIN_TRANSIENT"; + case AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: + return "AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK"; + case AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: + return "AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE"; + case AUDIOFOCUS_LOSS: + return "AUDIOFOCUS_LOSS"; + case AUDIOFOCUS_LOSS_TRANSIENT: + return "AUDIOFOCUS_LOSS_TRANSIENT"; + case AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: // Note CAN_DUCK not MAY_DUCK. + return "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"; + default: + return "AUDIO_FOCUS_UNKNOWN(" + focus + ")"; + } + } + + /** * Used to indicate no audio focus has been gained or lost, or requested. */ public static final int AUDIOFOCUS_NONE = 0; diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index c11762bcdb40..373f6e126924 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -214,6 +214,175 @@ public class AudioSystem } } + /** + * @hide + * Convert a native audio format integer constant to a string. + */ + public static String audioFormatToString(int audioFormat) { + switch (audioFormat) { + case /* AUDIO_FORMAT_INVALID */ 0xFFFFFFFF: + return "AUDIO_FORMAT_INVALID"; + case /* AUDIO_FORMAT_DEFAULT */ 0: + return "AUDIO_FORMAT_DEFAULT"; + case /* AUDIO_FORMAT_MP3 */ 0x01000000: + return "AUDIO_FORMAT_MP3"; + case /* AUDIO_FORMAT_AMR_NB */ 0x02000000: + return "AUDIO_FORMAT_AMR_NB"; + case /* AUDIO_FORMAT_AMR_WB */ 0x03000000: + return "AUDIO_FORMAT_AMR_WB"; + case /* AUDIO_FORMAT_AAC */ 0x04000000: + return "AUDIO_FORMAT_AAC"; + case /* AUDIO_FORMAT_HE_AAC_V1 */ 0x05000000: + return "AUDIO_FORMAT_HE_AAC_V1"; + case /* AUDIO_FORMAT_HE_AAC_V2 */ 0x06000000: + return "AUDIO_FORMAT_HE_AAC_V2"; + case /* AUDIO_FORMAT_VORBIS */ 0x07000000: + return "AUDIO_FORMAT_VORBIS"; + case /* AUDIO_FORMAT_OPUS */ 0x08000000: + return "AUDIO_FORMAT_OPUS"; + case /* AUDIO_FORMAT_AC3 */ 0x09000000: + return "AUDIO_FORMAT_AC3"; + case /* AUDIO_FORMAT_E_AC3 */ 0x0A000000: + return "AUDIO_FORMAT_E_AC3"; + case /* AUDIO_FORMAT_DTS */ 0x0B000000: + return "AUDIO_FORMAT_DTS"; + case /* AUDIO_FORMAT_DTS_HD */ 0x0C000000: + return "AUDIO_FORMAT_DTS_HD"; + case /* AUDIO_FORMAT_IEC61937 */ 0x0D000000: + return "AUDIO_FORMAT_IEC61937"; + case /* AUDIO_FORMAT_DOLBY_TRUEHD */ 0x0E000000: + return "AUDIO_FORMAT_DOLBY_TRUEHD"; + case /* AUDIO_FORMAT_EVRC */ 0x10000000: + return "AUDIO_FORMAT_EVRC"; + case /* AUDIO_FORMAT_EVRCB */ 0x11000000: + return "AUDIO_FORMAT_EVRCB"; + case /* AUDIO_FORMAT_EVRCWB */ 0x12000000: + return "AUDIO_FORMAT_EVRCWB"; + case /* AUDIO_FORMAT_EVRCNW */ 0x13000000: + return "AUDIO_FORMAT_EVRCNW"; + case /* AUDIO_FORMAT_AAC_ADIF */ 0x14000000: + return "AUDIO_FORMAT_AAC_ADIF"; + case /* AUDIO_FORMAT_WMA */ 0x15000000: + return "AUDIO_FORMAT_WMA"; + case /* AUDIO_FORMAT_WMA_PRO */ 0x16000000: + return "AUDIO_FORMAT_WMA_PRO"; + case /* AUDIO_FORMAT_AMR_WB_PLUS */ 0x17000000: + return "AUDIO_FORMAT_AMR_WB_PLUS"; + case /* AUDIO_FORMAT_MP2 */ 0x18000000: + return "AUDIO_FORMAT_MP2"; + case /* AUDIO_FORMAT_QCELP */ 0x19000000: + return "AUDIO_FORMAT_QCELP"; + case /* AUDIO_FORMAT_DSD */ 0x1A000000: + return "AUDIO_FORMAT_DSD"; + case /* AUDIO_FORMAT_FLAC */ 0x1B000000: + return "AUDIO_FORMAT_FLAC"; + case /* AUDIO_FORMAT_ALAC */ 0x1C000000: + return "AUDIO_FORMAT_ALAC"; + case /* AUDIO_FORMAT_APE */ 0x1D000000: + return "AUDIO_FORMAT_APE"; + case /* AUDIO_FORMAT_AAC_ADTS */ 0x1E000000: + return "AUDIO_FORMAT_AAC_ADTS"; + case /* AUDIO_FORMAT_SBC */ 0x1F000000: + return "AUDIO_FORMAT_SBC"; + case /* AUDIO_FORMAT_APTX */ 0x20000000: + return "AUDIO_FORMAT_APTX"; + case /* AUDIO_FORMAT_APTX_HD */ 0x21000000: + return "AUDIO_FORMAT_APTX_HD"; + case /* AUDIO_FORMAT_AC4 */ 0x22000000: + return "AUDIO_FORMAT_AC4"; + case /* AUDIO_FORMAT_LDAC */ 0x23000000: + return "AUDIO_FORMAT_LDAC"; + case /* AUDIO_FORMAT_MAT */ 0x24000000: + return "AUDIO_FORMAT_MAT"; + case /* AUDIO_FORMAT_AAC_LATM */ 0x25000000: + return "AUDIO_FORMAT_AAC_LATM"; + case /* AUDIO_FORMAT_CELT */ 0x26000000: + return "AUDIO_FORMAT_CELT"; + case /* AUDIO_FORMAT_APTX_ADAPTIVE */ 0x27000000: + return "AUDIO_FORMAT_APTX_ADAPTIVE"; + case /* AUDIO_FORMAT_LHDC */ 0x28000000: + return "AUDIO_FORMAT_LHDC"; + case /* AUDIO_FORMAT_LHDC_LL */ 0x29000000: + return "AUDIO_FORMAT_LHDC_LL"; + case /* AUDIO_FORMAT_APTX_TWSP */ 0x2A000000: + return "AUDIO_FORMAT_APTX_TWSP"; + + /* Aliases */ + case /* AUDIO_FORMAT_PCM_16_BIT */ 0x1: + return "AUDIO_FORMAT_PCM_16_BIT"; // (PCM | PCM_SUB_16_BIT) + case /* AUDIO_FORMAT_PCM_8_BIT */ 0x2: + return "AUDIO_FORMAT_PCM_8_BIT"; // (PCM | PCM_SUB_8_BIT) + case /* AUDIO_FORMAT_PCM_32_BIT */ 0x3: + return "AUDIO_FORMAT_PCM_32_BIT"; // (PCM | PCM_SUB_32_BIT) + case /* AUDIO_FORMAT_PCM_8_24_BIT */ 0x4: + return "AUDIO_FORMAT_PCM_8_24_BIT"; // (PCM | PCM_SUB_8_24_BIT) + case /* AUDIO_FORMAT_PCM_FLOAT */ 0x5: + return "AUDIO_FORMAT_PCM_FLOAT"; // (PCM | PCM_SUB_FLOAT) + case /* AUDIO_FORMAT_PCM_24_BIT_PACKED */ 0x6: + return "AUDIO_FORMAT_PCM_24_BIT_PACKED"; // (PCM | PCM_SUB_24_BIT_PACKED) + case /* AUDIO_FORMAT_AAC_MAIN */ 0x4000001: + return "AUDIO_FORMAT_AAC_MAIN"; // (AAC | AAC_SUB_MAIN) + case /* AUDIO_FORMAT_AAC_LC */ 0x4000002: + return "AUDIO_FORMAT_AAC_LC"; // (AAC | AAC_SUB_LC) + case /* AUDIO_FORMAT_AAC_SSR */ 0x4000004: + return "AUDIO_FORMAT_AAC_SSR"; // (AAC | AAC_SUB_SSR) + case /* AUDIO_FORMAT_AAC_LTP */ 0x4000008: + return "AUDIO_FORMAT_AAC_LTP"; // (AAC | AAC_SUB_LTP) + case /* AUDIO_FORMAT_AAC_HE_V1 */ 0x4000010: + return "AUDIO_FORMAT_AAC_HE_V1"; // (AAC | AAC_SUB_HE_V1) + case /* AUDIO_FORMAT_AAC_SCALABLE */ 0x4000020: + return "AUDIO_FORMAT_AAC_SCALABLE"; // (AAC | AAC_SUB_SCALABLE) + case /* AUDIO_FORMAT_AAC_ERLC */ 0x4000040: + return "AUDIO_FORMAT_AAC_ERLC"; // (AAC | AAC_SUB_ERLC) + case /* AUDIO_FORMAT_AAC_LD */ 0x4000080: + return "AUDIO_FORMAT_AAC_LD"; // (AAC | AAC_SUB_LD) + case /* AUDIO_FORMAT_AAC_HE_V2 */ 0x4000100: + return "AUDIO_FORMAT_AAC_HE_V2"; // (AAC | AAC_SUB_HE_V2) + case /* AUDIO_FORMAT_AAC_ELD */ 0x4000200: + return "AUDIO_FORMAT_AAC_ELD"; // (AAC | AAC_SUB_ELD) + case /* AUDIO_FORMAT_AAC_XHE */ 0x4000300: + return "AUDIO_FORMAT_AAC_XHE"; // (AAC | AAC_SUB_XHE) + case /* AUDIO_FORMAT_AAC_ADTS_MAIN */ 0x1e000001: + return "AUDIO_FORMAT_AAC_ADTS_MAIN"; // (AAC_ADTS | AAC_SUB_MAIN) + case /* AUDIO_FORMAT_AAC_ADTS_LC */ 0x1e000002: + return "AUDIO_FORMAT_AAC_ADTS_LC"; // (AAC_ADTS | AAC_SUB_LC) + case /* AUDIO_FORMAT_AAC_ADTS_SSR */ 0x1e000004: + return "AUDIO_FORMAT_AAC_ADTS_SSR"; // (AAC_ADTS | AAC_SUB_SSR) + case /* AUDIO_FORMAT_AAC_ADTS_LTP */ 0x1e000008: + return "AUDIO_FORMAT_AAC_ADTS_LTP"; // (AAC_ADTS | AAC_SUB_LTP) + case /* AUDIO_FORMAT_AAC_ADTS_HE_V1 */ 0x1e000010: + return "AUDIO_FORMAT_AAC_ADTS_HE_V1"; // (AAC_ADTS | AAC_SUB_HE_V1) + case /* AUDIO_FORMAT_AAC_ADTS_SCALABLE */ 0x1e000020: + return "AUDIO_FORMAT_AAC_ADTS_SCALABLE"; // (AAC_ADTS | AAC_SUB_SCALABLE) + case /* AUDIO_FORMAT_AAC_ADTS_ERLC */ 0x1e000040: + return "AUDIO_FORMAT_AAC_ADTS_ERLC"; // (AAC_ADTS | AAC_SUB_ERLC) + case /* AUDIO_FORMAT_AAC_ADTS_LD */ 0x1e000080: + return "AUDIO_FORMAT_AAC_ADTS_LD"; // (AAC_ADTS | AAC_SUB_LD) + case /* AUDIO_FORMAT_AAC_ADTS_HE_V2 */ 0x1e000100: + return "AUDIO_FORMAT_AAC_ADTS_HE_V2"; // (AAC_ADTS | AAC_SUB_HE_V2) + case /* AUDIO_FORMAT_AAC_ADTS_ELD */ 0x1e000200: + return "AUDIO_FORMAT_AAC_ADTS_ELD"; // (AAC_ADTS | AAC_SUB_ELD) + case /* AUDIO_FORMAT_AAC_ADTS_XHE */ 0x1e000300: + return "AUDIO_FORMAT_AAC_ADTS_XHE"; // (AAC_ADTS | AAC_SUB_XHE) + case /* AUDIO_FORMAT_AAC_LATM_LC */ 0x25000002: + return "AUDIO_FORMAT_AAC_LATM_LC"; // (AAC_LATM | AAC_SUB_LC) + case /* AUDIO_FORMAT_AAC_LATM_HE_V1 */ 0x25000010: + return "AUDIO_FORMAT_AAC_LATM_HE_V1"; // (AAC_LATM | AAC_SUB_HE_V1) + case /* AUDIO_FORMAT_AAC_LATM_HE_V2 */ 0x25000100: + return "AUDIO_FORMAT_AAC_LATM_HE_V2"; // (AAC_LATM | AAC_SUB_HE_V2) + case /* AUDIO_FORMAT_E_AC3_JOC */ 0xA000001: + return "AUDIO_FORMAT_E_AC3_JOC"; // (E_AC3 | E_AC3_SUB_JOC) + case /* AUDIO_FORMAT_MAT_1_0 */ 0x24000001: + return "AUDIO_FORMAT_MAT_1_0"; // (MAT | MAT_SUB_1_0) + case /* AUDIO_FORMAT_MAT_2_0 */ 0x24000002: + return "AUDIO_FORMAT_MAT_2_0"; // (MAT | MAT_SUB_2_0) + case /* AUDIO_FORMAT_MAT_2_1 */ 0x24000003: + return "AUDIO_FORMAT_MAT_2_1"; // (MAT | MAT_SUB_2_1) + default: + return "AUDIO_FORMAT_(" + audioFormat + ")"; + } + } + /* Routing bits for the former setRouting/getRouting API */ /** @hide @deprecated */ @Deprecated public static final int ROUTE_EARPIECE = (1 << 0); diff --git a/media/java/android/media/MediaMetrics.java b/media/java/android/media/MediaMetrics.java index 540955f3b393..f6f482dd0cd3 100644 --- a/media/java/android/media/MediaMetrics.java +++ b/media/java/android/media/MediaMetrics.java @@ -38,6 +38,117 @@ import java.util.Objects; public class MediaMetrics { public static final String TAG = "MediaMetrics"; + public static final String SEPARATOR = "."; + + /** + * A list of established MediaMetrics names that can be used for Items. + */ + public static class Name { + public static final String AUDIO = "audio"; + public static final String AUDIO_BLUETOOTH = AUDIO + SEPARATOR + "bluetooth"; + public static final String AUDIO_DEVICE = AUDIO + SEPARATOR + "device"; + public static final String AUDIO_FOCUS = AUDIO + SEPARATOR + "focus"; + public static final String AUDIO_FORCE_USE = AUDIO + SEPARATOR + "forceUse"; + public static final String AUDIO_MIC = AUDIO + SEPARATOR + "mic"; + public static final String AUDIO_SERVICE = AUDIO + SEPARATOR + "service"; + public static final String AUDIO_VOLUME = AUDIO + SEPARATOR + "volume"; + public static final String AUDIO_VOLUME_EVENT = AUDIO_VOLUME + SEPARATOR + "event"; + } + + /** + * A list of established string values. + */ + public static class Value { + public static final String CONNECT = "connect"; + public static final String CONNECTED = "connected"; + public static final String DISCONNECT = "disconnect"; + public static final String DISCONNECTED = "disconnected"; + public static final String DOWN = "down"; + public static final String MUTE = "mute"; + public static final String NO = "no"; + public static final String OFF = "off"; + public static final String ON = "on"; + public static final String UNMUTE = "unmute"; + public static final String UP = "up"; + public static final String YES = "yes"; + } + + /** + * A list of standard property keys for consistent use and type. + */ + public static class Property { + // A use for Bluetooth or USB device addresses + public static final Key<String> ADDRESS = createKey("address", String.class); + // A string representing the Audio Attributes + public static final Key<String> ATTRIBUTES = createKey("attributes", String.class); + + // The calling package responsible for the state change + public static final Key<String> CALLING_PACKAGE = + createKey("callingPackage", String.class); + + // The client name + public static final Key<String> CLIENT_NAME = createKey("clientName", String.class); + + // The device type + public static final Key<Integer> DELAY_MS = createKey("delayMs", Integer.class); + + // The device type + public static final Key<String> DEVICE = createKey("device", String.class); + + // For volume changes, up or down + public static final Key<String> DIRECTION = createKey("direction", String.class); + + // A reason for early return or error + public static final Key<String> EARLY_RETURN = + createKey("earlyReturn", String.class); + // ENCODING_ ... string to match AudioFormat encoding + public static final Key<String> ENCODING = createKey("encoding", String.class); + + public static final Key<String> EVENT = createKey("event#", String.class); + + // event generated is external (yes, no) + public static final Key<String> EXTERNAL = createKey("external", String.class); + + public static final Key<Integer> FLAGS = createKey("flags", Integer.class); + public static final Key<String> FOCUS_CHANGE_HINT = + createKey("focusChangeHint", String.class); + public static final Key<String> FORCE_USE_DUE_TO = + createKey("forceUseDueTo", String.class); + public static final Key<String> FORCE_USE_MODE = + createKey("forceUseMode", String.class); + public static final Key<Double> GAIN_DB = + createKey("gainDb", Double.class); + public static final Key<String> GROUP = + createKey("group", String.class); + // For volume + public static final Key<Integer> INDEX = createKey("index", Integer.class); + public static final Key<Integer> MAX_INDEX = createKey("maxIndex", Integer.class); + public static final Key<Integer> MIN_INDEX = createKey("minIndex", Integer.class); + public static final Key<String> MODE = + createKey("mode", String.class); // audio_mode + public static final Key<String> MUTE = + createKey("mute", String.class); // microphone, on or off. + + // Bluetooth or Usb device name + public static final Key<String> NAME = + createKey("name", String.class); + + // Number of observers + public static final Key<Integer> OBSERVERS = + createKey("observers", Integer.class); + + public static final Key<String> REQUEST = + createKey("request", String.class); + + // For Bluetooth + public static final Key<String> SCO_AUDIO_MODE = + createKey("scoAudioMode", String.class); + public static final Key<Integer> SDK = createKey("sdk", Integer.class); + public static final Key<String> STATE = createKey("state", String.class); + public static final Key<Integer> STATUS = createKey("status", Integer.class); + public static final Key<String> STREAM_TYPE = createKey("streamType", String.class); + } + /** * The TYPE constants below should match those in native MediaMetricsItem.h */ diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index c2c79d361996..032ad63a8570 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -30,6 +30,7 @@ import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioRoutesObserver; import android.media.IStrategyPreferredDeviceDispatcher; +import android.media.MediaMetrics; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -668,6 +669,13 @@ import java.io.PrintWriter; } AudioService.sForceUseLogger.log( new AudioServiceEvents.ForceUseEvent(useCase, config, eventSource)); + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_FORCE_USE + MediaMetrics.SEPARATOR + + AudioSystem.forceUseUsageToString(useCase)) + .set(MediaMetrics.Property.EVENT, "onSetForceUse") + .set(MediaMetrics.Property.FORCE_USE_DUE_TO, eventSource) + .set(MediaMetrics.Property.FORCE_USE_MODE, + AudioSystem.forceUseConfigToString(config)) + .record(); AudioSystem.setForceUse(useCase, config); } diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index c17ed3e292ef..3e97a1e136c6 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.AudioRoutesInfo; 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,10 +65,69 @@ public class AudioDeviceInventory { // lock to synchronize all access to mConnectedDevices and mApmConnectedDevices private final Object mDevicesLock = new Object(); + //Audio Analytics ids. + private static final String mMetricsId = "audio.device."; + // List of connected devices // Key for map created from DeviceInfo.makeDeviceListKey() @GuardedBy("mDevicesLock") - private final LinkedHashMap<String, DeviceInfo> mConnectedDevices = new LinkedHashMap<>(); + private final LinkedHashMap<String, DeviceInfo> mConnectedDevices = new LinkedHashMap<>() { + @Override + public DeviceInfo put(String key, DeviceInfo value) { + final DeviceInfo result = super.put(key, value); + record("put", true /* connected */, key, value); + return result; + } + + @Override + public DeviceInfo putIfAbsent(String key, DeviceInfo value) { + final DeviceInfo result = super.putIfAbsent(key, value); + if (result == null) { + record("putIfAbsent", true /* connected */, key, value); + } + return result; + } + + @Override + public DeviceInfo remove(Object key) { + final DeviceInfo result = super.remove(key); + if (result != null) { + record("remove", false /* connected */, (String) key, result); + } + return result; + } + + @Override + public boolean remove(Object key, Object value) { + final boolean result = super.remove(key, value); + if (result) { + record("remove", false /* connected */, (String) key, (DeviceInfo) value); + } + return result; + } + + // Not overridden + // clear + // compute + // computeIfAbsent + // computeIfPresent + // merge + // putAll + // replace + // replaceAll + private void record(String event, boolean connected, String key, DeviceInfo value) { + // DeviceInfo - int mDeviceType; + // DeviceInfo - int mDeviceCodecFormat; + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + + MediaMetrics.SEPARATOR + AudioSystem.getDeviceName(value.mDeviceType)) + .set(MediaMetrics.Property.ADDRESS, value.mDeviceAddress) + .set(MediaMetrics.Property.EVENT, event) + .set(MediaMetrics.Property.NAME, value.mDeviceName) + .set(MediaMetrics.Property.STATE, connected + ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) + .record(); + } + }; // List of devices actually connected to AudioPolicy (through AudioSystem), only one // by device type, which is used as the key, value is the DeviceInfo generated key. @@ -236,6 +296,16 @@ public class AudioDeviceInventory { + " codec=" + a2dpCodec + " vol=" + a2dpVolume)); + new MediaMetrics.Item(mMetricsId + "a2dp") + .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(a2dpCodec)) + .set(MediaMetrics.Property.EVENT, "onSetA2dpSinkConnectionState") + .set(MediaMetrics.Property.INDEX, a2dpVolume) + .set(MediaMetrics.Property.STATE, + state == BluetoothProfile.STATE_CONNECTED + ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) + .record(); + synchronized (mDevicesLock) { final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, btDevice.getAddress()); @@ -284,6 +354,15 @@ public class AudioDeviceInventory { final DeviceInfo di = mConnectedDevices.get(key); boolean isConnected = di != null; + new MediaMetrics.Item(mMetricsId + "onSetA2dpSourceConnectionState") + .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)) + .set(MediaMetrics.Property.STATE, + state == BluetoothProfile.STATE_CONNECTED + ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) + .record(); + if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { makeA2dpSrcUnavailable(address); } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { @@ -301,6 +380,17 @@ public class AudioDeviceInventory { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "onSetHearingAidConnectionState addr=" + address)); + new MediaMetrics.Item(mMetricsId + "onSetHearingAidConnectionState") + .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)) + .set(MediaMetrics.Property.STATE, + state == BluetoothProfile.STATE_CONNECTED + ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) + .set(MediaMetrics.Property.STREAM_TYPE, + AudioSystem.streamToString(streamType)) + .record(); + synchronized (mDevicesLock) { final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, btDevice.getAddress()); @@ -317,10 +407,15 @@ public class AudioDeviceInventory { } @GuardedBy("AudioDeviceBroker.mDeviceStateLock") - /*package*/ void onBluetoothA2dpActiveDeviceChange( + /*package*/ void onBluetoothA2dpActiveDeviceChange( @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) { + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + + "onBluetoothA2dpActiveDeviceChange") + .set(MediaMetrics.Property.EVENT, BtHelper.a2dpDeviceEventToString(event)); + final BluetoothDevice btDevice = btInfo.getBtDevice(); if (btDevice == null) { + mmi.set(MediaMetrics.Property.EARLY_RETURN, "btDevice null").record(); return; } if (AudioService.DEBUG_DEVICES) { @@ -341,6 +436,8 @@ public class AudioDeviceInventory { if (mDeviceBroker.hasScheduledA2dpSinkConnectionState(btDevice)) { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "A2dp config change ignored (scheduled connection change)")); + mmi.set(MediaMetrics.Property.EARLY_RETURN, "A2dp config change ignored") + .record(); return; } final String key = DeviceInfo.makeDeviceListKey( @@ -348,9 +445,16 @@ public class AudioDeviceInventory { final DeviceInfo di = mConnectedDevices.get(key); if (di == null) { Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpActiveDeviceChange"); + mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record(); return; } + mmi.set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.ENCODING, + AudioSystem.audioFormatToString(a2dpCodec)) + .set(MediaMetrics.Property.INDEX, a2dpVolume) + .set(MediaMetrics.Property.NAME, di.mDeviceName); + if (event == BtHelper.EVENT_ACTIVE_DEVICE_CHANGE) { // Device is connected if (a2dpVolume != -1) { @@ -388,6 +492,7 @@ public class AudioDeviceInventory { + address + " codec=" + a2dpCodec).printLog(TAG)); } } + mmi.record(); } /*package*/ void onMakeA2dpDeviceUnavailableNow(String address, int a2dpCodec) { @@ -399,6 +504,9 @@ public class AudioDeviceInventory { /*package*/ void onReportNewRoutes() { int n = mRoutesObservers.beginBroadcast(); if (n > 0) { + new MediaMetrics.Item(mMetricsId + "onReportNewRoutes") + .set(MediaMetrics.Property.OBSERVERS, n) + .record(); AudioRoutesInfo routes; synchronized (mCurAudioRoutes) { routes = new AudioRoutesInfo(mCurAudioRoutes); @@ -428,6 +536,13 @@ public class AudioDeviceInventory { AudioDeviceInventory.WiredDeviceConnectionState wdcs) { AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs)); + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + + "onSetWiredDeviceConnectionState") + .set(MediaMetrics.Property.ADDRESS, wdcs.mAddress) + .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(wdcs.mType)) + .set(MediaMetrics.Property.STATE, + wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED + ? MediaMetrics.Value.DISCONNECTED : MediaMetrics.Value.CONNECTED); synchronized (mDevicesLock) { if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED) && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) { @@ -438,6 +553,8 @@ public class AudioDeviceInventory { if (!handleDeviceConnection(wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mType, wdcs.mAddress, wdcs.mName)) { // change of connection state failed, bailout + mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed") + .record(); return; } if (wdcs.mState != AudioService.CONNECTION_STATE_DISCONNECTED) { @@ -453,15 +570,20 @@ public class AudioDeviceInventory { sendDeviceConnectionIntent(wdcs.mType, wdcs.mState, wdcs.mAddress, wdcs.mName); updateAudioRoutes(wdcs.mType, wdcs.mState); } + mmi.record(); } /*package*/ void onToggleHdmi() { + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "onToggleHdmi") + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HDMI)); synchronized (mDevicesLock) { // Is HDMI connected? final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HDMI, ""); final DeviceInfo di = mConnectedDevices.get(key); if (di == null) { Log.e(TAG, "invalid null DeviceInfo in onToggleHdmi"); + mmi.set(MediaMetrics.Property.EARLY_RETURN, "invalid null DeviceInfo").record(); return; } // Toggle HDMI to retrigger broadcast with proper formats. @@ -472,6 +594,7 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_STATE_AVAILABLE, "", "", "android"); // reconnect } + mmi.record(); } /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDeviceAttributes device) { @@ -535,6 +658,12 @@ public class AudioDeviceInventory { + Integer.toHexString(device) + " address:" + address + " name:" + deviceName + ")"); } + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "handleDeviceConnection") + .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(device)) + .set(MediaMetrics.Property.MODE, connect + ? MediaMetrics.Value.CONNECT : MediaMetrics.Value.DISCONNECT) + .set(MediaMetrics.Property.NAME, deviceName); synchronized (mDevicesLock) { final String deviceKey = DeviceInfo.makeDeviceListKey(device, address); if (AudioService.DEBUG_DEVICES) { @@ -550,13 +679,18 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName, AudioSystem.AUDIO_FORMAT_DEFAULT); if (res != AudioSystem.AUDIO_STATUS_OK) { - Slog.e(TAG, "not connecting device 0x" + Integer.toHexString(device) - + " due to command error " + res); + final String reason = "not connecting device 0x" + Integer.toHexString(device) + + " due to command error " + res; + Slog.e(TAG, reason); + mmi.set(MediaMetrics.Property.EARLY_RETURN, reason) + .set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED) + .record(); return false; } mConnectedDevices.put(deviceKey, new DeviceInfo( device, deviceName, address, AudioSystem.AUDIO_FORMAT_DEFAULT)); mDeviceBroker.postAccessoryPlugMediaUnmute(device); + mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); return true; } else if (!connect && isConnected) { mAudioSystem.setDeviceConnectionState(device, @@ -564,11 +698,13 @@ public class AudioDeviceInventory { AudioSystem.AUDIO_FORMAT_DEFAULT); // always remove even if disconnection failed mConnectedDevices.remove(deviceKey); + mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); return true; } Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey + ", deviceSpec=" + di + ", connect=" + connect); } + mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record(); return false; } @@ -582,6 +718,8 @@ public class AudioDeviceInventory { toRemove.add(deviceInfo.mDeviceAddress); } }); + new MediaMetrics.Item(mMetricsId + "disconnectA2dp") + .record(); if (toRemove.size() > 0) { final int delay = checkSendBecomingNoisyIntentInt( AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, @@ -602,6 +740,8 @@ public class AudioDeviceInventory { toRemove.add(deviceInfo.mDeviceAddress); } }); + new MediaMetrics.Item(mMetricsId + "disconnectA2dpSink") + .record(); toRemove.stream().forEach(deviceAddress -> makeA2dpSrcUnavailable(deviceAddress)); } } @@ -615,6 +755,8 @@ public class AudioDeviceInventory { toRemove.add(deviceInfo.mDeviceAddress); } }); + new MediaMetrics.Item(mMetricsId + "disconnectHearingAid") + .record(); if (toRemove.size() > 0) { final int delay = checkSendBecomingNoisyIntentInt( AudioSystem.DEVICE_OUT_HEARING_AID, 0, AudioSystem.DEVICE_NONE); @@ -743,6 +885,8 @@ public class AudioDeviceInventory { final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec); + // TODO: log in MediaMetrics once distinction between connection failure and + // double connection is made. if (res != AudioSystem.AUDIO_STATUS_OK) { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "APM failed to make available A2DP device addr=" + address @@ -771,7 +915,12 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) { + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "a2dp." + address) + .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(a2dpCodec)) + .set(MediaMetrics.Property.EVENT, "makeA2dpDeviceUnavailableNow"); + if (address == null) { + mmi.set(MediaMetrics.Property.EARLY_RETURN, "address null").record(); return; } final String deviceToRemoveKey = @@ -783,6 +932,9 @@ public class AudioDeviceInventory { // 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.set(MediaMetrics.Property.EARLY_RETURN, + "A2DP device made unavailable, was not used") + .record(); return; } @@ -804,6 +956,7 @@ public class AudioDeviceInventory { mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); // Remove A2DP routes as well setCurrentAudioRouteNameIfPossible(null); + mmi.record(); } @GuardedBy("mDevicesLock") @@ -862,6 +1015,14 @@ public class AudioDeviceInventory { mDeviceBroker.postApplyVolumeOnDevice(streamType, AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable"); setCurrentAudioRouteNameIfPossible(name); + new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceAvailable") + .set(MediaMetrics.Property.ADDRESS, address != null ? address : "") + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HEARING_AID)) + .set(MediaMetrics.Property.NAME, name) + .set(MediaMetrics.Property.STREAM_TYPE, + AudioSystem.streamToString(streamType)) + .record(); } @GuardedBy("mDevicesLock") @@ -873,6 +1034,11 @@ public class AudioDeviceInventory { DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address)); // Remove Hearing Aid routes as well setCurrentAudioRouteNameIfPossible(null); + new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceUnavailable") + .set(MediaMetrics.Property.ADDRESS, address != null ? address : "") + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HEARING_AID)) + .record(); } @GuardedBy("mDevicesLock") @@ -919,10 +1085,18 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private int checkSendBecomingNoisyIntentInt(int device, @AudioService.ConnectionState int state, int musicDevice) { + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + + "checkSendBecomingNoisyIntentInt") + .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(device)) + .set(MediaMetrics.Property.STATE, + state == AudioService.CONNECTION_STATE_CONNECTED + ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED); if (state != AudioService.CONNECTION_STATE_DISCONNECTED) { + mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return return 0; } if (!BECOMING_NOISY_INTENT_DEVICES_SET.contains(device)) { + mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return return 0; } int delay = 0; @@ -950,12 +1124,14 @@ public class AudioDeviceInventory { // the pausing of some apps that are playing remotely AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( "dropping ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG)); + mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return return 0; } mDeviceBroker.postBroadcastBecomingNoisy(); delay = AudioService.BECOMING_NOISY_DELAY_MS; } + mmi.set(MediaMetrics.Property.DELAY_MS, 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 f840f2d359d5..7cac376ea7ae 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.IStrategyPreferredDeviceDispatcher; 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; @@ -1880,6 +1881,16 @@ public class AudioService extends IAudioService.Stub synchronized (mExtVolumeControllerLock) { extVolCtlr = mExtVolumeController; } + new MediaMetrics.Item(mMetricsId + "adjustSuggestedStreamVolume") + .setUid(Binder.getCallingUid()) + .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackage) + .set(MediaMetrics.Property.CLIENT_NAME, caller) + .set(MediaMetrics.Property.DIRECTION, direction > 0 + ? MediaMetrics.Value.UP : MediaMetrics.Value.DOWN) + .set(MediaMetrics.Property.EXTERNAL, extVolCtlr != null + ? MediaMetrics.Value.YES : MediaMetrics.Value.NO) + .set(MediaMetrics.Property.FLAGS, flags) + .record(); if (extVolCtlr != null) { sendMsg(mAudioHandler, MSG_NOTIFY_VOL_EVENT, SENDMSG_QUEUE, direction, 0 /*ignored*/, @@ -3141,21 +3152,32 @@ public class AudioService extends IAudioService.Stub if (uid == android.os.Process.SYSTEM_UID) { uid = UserHandle.getUid(userId, UserHandle.getAppId(uid)); } + MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIC) + .setUid(uid) + .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackage) + .set(MediaMetrics.Property.EVENT, "setMicrophoneMute") + .set(MediaMetrics.Property.REQUEST, on + ? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE); + // If OP_MUTE_MICROPHONE is set, disallow unmuting. if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage) != AppOpsManager.MODE_ALLOWED) { + mmi.set(MediaMetrics.Property.EARLY_RETURN, "disallow unmuting").record(); return; } if (!checkAudioSettingsPermission("setMicrophoneMute()")) { + mmi.set(MediaMetrics.Property.EARLY_RETURN, "!checkAudioSettingsPermission").record(); return; } if (userId != UserHandle.getCallingUserId() && mContext.checkCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED) { + mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission").record(); return; } mMicMuteFromApi = on; + mmi.record(); // record now, the no caller check will set the mute state. setMicrophoneMuteNoCallerCheck(userId); } @@ -3167,6 +3189,12 @@ public class AudioService extends IAudioService.Stub return; } mMicMuteFromSwitch = on; + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIC) + .setUid(userId) + .set(MediaMetrics.Property.EVENT, "setMicrophoneMuteFromSwitch") + .set(MediaMetrics.Property.REQUEST, on + ? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE) + .record(); setMicrophoneMuteNoCallerCheck(userId); } @@ -3207,6 +3235,17 @@ public class AudioService extends IAudioService.Stub Log.e(TAG, "Error changing mic mute state to " + muted + " current:" + mMicMuteFromSystemCached); } + + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIC) + .setUid(userId) + .set(MediaMetrics.Property.EVENT, "setMicrophoneMuteNoCallerCheck") + .set(MediaMetrics.Property.MUTE, mMicMuteFromSystemCached + ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF) + .set(MediaMetrics.Property.REQUEST, muted + ? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE) + .set(MediaMetrics.Property.STATUS, ret) + .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 @@ -3941,10 +3980,20 @@ public class AudioService extends IAudioService.Stub } // 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(MediaMetrics.Name.AUDIO_DEVICE + + MediaMetrics.SEPARATOR + "setSpeakerphoneOn") + .setUid(uid) + .setPid(pid) + .set(MediaMetrics.Property.STATE, on + ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF) + .record(); + if (stateChanged) { final long ident = Binder.clearCallingIdentity(); try { @@ -3975,9 +4024,19 @@ public class AudioService extends IAudioService.Stub } // 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(MediaMetrics.Name.AUDIO_DEVICE + + MediaMetrics.SEPARATOR + "setBluetoothScoOn") + .setUid(uid) + .setPid(pid) + .set(MediaMetrics.Property.STATE, on + ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF) + .record(); mDeviceBroker.setBluetoothScoOn(on, eventSource); } @@ -3993,9 +4052,20 @@ public class AudioService extends IAudioService.Stub /** @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(MediaMetrics.Name.AUDIO_DEVICE + + MediaMetrics.SEPARATOR + "setBluetoothA2dpOn") + .setUid(uid) + .setPid(pid) + .set(MediaMetrics.Property.STATE, on + ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF) + .record(); + mDeviceBroker.setBluetoothA2dpOn_Async(on, eventSource); } @@ -4006,31 +4076,59 @@ public class AudioService extends IAudioService.Stub /** @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(MediaMetrics.Name.AUDIO_BLUETOOTH) + .setUid(uid) + .setPid(pid) + .set(MediaMetrics.Property.EVENT, "startBluetoothSco") + .set(MediaMetrics.Property.SCO_AUDIO_MODE, + BtHelper.scoAudioModeToString(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(MediaMetrics.Name.AUDIO_BLUETOOTH) + .setUid(uid) + .setPid(pid) + .set(MediaMetrics.Property.EVENT, "startBluetoothScoVirtualCall") + .set(MediaMetrics.Property.SCO_AUDIO_MODE, + BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL)) + .record(); startBluetoothScoInt(cb, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource); } void startBluetoothScoInt(IBinder cb, int scoAudioMode, @NonNull String eventSource) { + MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH) + .set(MediaMetrics.Property.EVENT, "startBluetoothScoInt") + .set(MediaMetrics.Property.SCO_AUDIO_MODE, + BtHelper.scoAudioModeToString(scoAudioMode)); + if (!checkAudioSettingsPermission("startBluetoothSco()") || !mSystemReady) { + mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record(); return; } synchronized (mDeviceBroker.mSetModeLock) { mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource); } + mmi.record(); } /** @see AudioManager#stopBluetoothSco() */ @@ -4039,12 +4137,21 @@ public class AudioService extends IAudioService.Stub !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(MediaMetrics.Name.AUDIO_BLUETOOTH) + .setUid(uid) + .setPid(pid) + .set(MediaMetrics.Property.EVENT, "stopBluetoothSco") + .set(MediaMetrics.Property.SCO_AUDIO_MODE, + BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_UNDEFINED)) + .record(); } @@ -4806,6 +4913,14 @@ public class AudioService extends IAudioService.Stub && state != CONNECTION_STATE_DISCONNECTED) { throw new IllegalArgumentException("Invalid state " + state); } + new MediaMetrics.Item(mMetricsId + "setWiredDeviceConnectionState") + .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.CLIENT_NAME, caller) + .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(type)) + .set(MediaMetrics.Property.NAME, name) + .set(MediaMetrics.Property.STATE, + state == CONNECTION_STATE_CONNECTED ? "connected" : "disconnected") + .record(); mDeviceBroker.setWiredDeviceConnectionState(type, state, address, name, caller); } @@ -5266,7 +5381,32 @@ public class AudioService extends IAudioService.Stub private String mVolumeIndexSettingName; private int mObservedDevices; - private final SparseIntArray mIndexMap = new SparseIntArray(8); + private final SparseIntArray mIndexMap = new SparseIntArray(8) { + @Override + public void put(int key, int value) { + super.put(key, value); + record("put", key, value); + } + @Override + public void setValueAt(int index, int value) { + super.setValueAt(index, value); + record("setValueAt", keyAt(index), value); + } + + // Record all changes in the VolumeStreamState + private void record(String event, int key, int value) { + final String device = key == AudioSystem.DEVICE_OUT_DEFAULT ? "default" + : AudioSystem.getOutputDeviceName(key); + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_VOLUME + MediaMetrics.SEPARATOR + + AudioSystem.streamToString(mStreamType) + + "." + device) + .set(MediaMetrics.Property.EVENT, event) + .set(MediaMetrics.Property.INDEX, value) + .set(MediaMetrics.Property.MIN_INDEX, mIndexMin) + .set(MediaMetrics.Property.MAX_INDEX, mIndexMax) + .record(); + } + }; private final Intent mVolumeChanged; private final Intent mStreamDevicesChanged; @@ -5949,6 +6089,13 @@ public class AudioService extends IAudioService.Stub + eventSource); break; } + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_FORCE_USE + + MediaMetrics.SEPARATOR + AudioSystem.forceUseUsageToString(useCase)) + .set(MediaMetrics.Property.EVENT, "setForceUse") + .set(MediaMetrics.Property.FORCE_USE_DUE_TO, eventSource) + .set(MediaMetrics.Property.FORCE_USE_MODE, + AudioSystem.forceUseConfigToString(config)) + .record(); sForceUseLogger.log( new AudioServiceEvents.ForceUseEvent(useCase, config, eventSource)); AudioSystem.setForceUse(useCase, config); @@ -6450,23 +6597,42 @@ public class AudioService extends IAudioService.Stub public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, IAudioPolicyCallback pcb, int sdk) { + final int uid = Binder.getCallingUid(); + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "focus") + .setUid(uid) + //.putInt("durationHint", durationHint) + .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) + .set(MediaMetrics.Property.CLIENT_NAME, clientId) + .set(MediaMetrics.Property.EVENT, "requestAudioFocus") + .set(MediaMetrics.Property.FLAGS, flags); + // permission checks if (aa != null && !isValidAudioAttributesUsage(aa)) { - Log.w(TAG, "Request using unsupported usage."); + final String reason = "Request using unsupported usage"; + Log.w(TAG, reason); + mmi.set(MediaMetrics.Property.EARLY_RETURN, reason) + .record(); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) { if (AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) { if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission( android.Manifest.permission.MODIFY_PHONE_STATE)) { - Log.e(TAG, "Invalid permission to (un)lock audio focus", new Exception()); + final String reason = "Invalid permission to (un)lock audio focus"; + Log.e(TAG, reason, new Exception()); + mmi.set(MediaMetrics.Property.EARLY_RETURN, reason) + .record(); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } } else { // only a registered audio policy can be used to lock focus synchronized (mAudioPolicies) { if (!mAudioPolicies.containsKey(pcb.asBinder())) { - Log.e(TAG, "Invalid unregistered AudioPolicy to (un)lock audio focus"); + final String reason = + "Invalid unregistered AudioPolicy to (un)lock audio focus"; + Log.e(TAG, reason); + mmi.set(MediaMetrics.Property.EARLY_RETURN, reason) + .record(); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } } @@ -6474,25 +6640,40 @@ public class AudioService extends IAudioService.Stub } if (callingPackageName == null || clientId == null || aa == null) { - Log.e(TAG, "Invalid null parameter to request audio focus"); + final String reason = "Invalid null parameter to request audio focus"; + Log.e(TAG, reason); + mmi.set(MediaMetrics.Property.EARLY_RETURN, reason) + .record(); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } - + mmi.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(mMetricsId + "focus") + .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) + .set(MediaMetrics.Property.CLIENT_NAME, clientId) + .set(MediaMetrics.Property.EVENT, "abandonAudioFocus"); + if (aa != null && !isValidAudioAttributesUsage(aa)) { Log.w(TAG, "Request using unsupported usage."); + mmi.set(MediaMetrics.Property.EARLY_RETURN, "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(mMetricsId + "focus") + .set(MediaMetrics.Property.CLIENT_NAME, clientId) + .set(MediaMetrics.Property.EVENT, "unregisterAudioFocusClient") + .record(); mMediaFocusControl.unregisterAudioFocusClient(clientId); } @@ -7066,6 +7247,12 @@ public class AudioService extends IAudioService.Stub } } + /** + * Audio Analytics ids. + */ + private static final String mMetricsId = MediaMetrics.Name.AUDIO_SERVICE + + MediaMetrics.SEPARATOR; + 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 add620e74df0..591356746e38 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 @@ package com.android.server.audio; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioSystem; +import android.media.MediaMetrics; import com.android.server.audio.AudioDeviceInventory.WiredDeviceConnectionState; @@ -120,6 +121,7 @@ public class AudioServiceEvents { mCaller = caller; mGroupName = null; mAudioAttributes = null; + logMetricEvent(); } /** used for VOL_SET_HEARING_AID_VOL*/ @@ -132,6 +134,7 @@ public class AudioServiceEvents { mCaller = null; mGroupName = null; mAudioAttributes = null; + logMetricEvent(); } /** used for VOL_SET_AVRCP_VOL */ @@ -144,6 +147,7 @@ public class AudioServiceEvents { mCaller = null; mGroupName = null; mAudioAttributes = null; + logMetricEvent(); } /** used for VOL_VOICE_ACTIVITY_HEARING_AID */ @@ -156,6 +160,7 @@ public class AudioServiceEvents { mCaller = null; mGroupName = null; mAudioAttributes = null; + logMetricEvent(); } /** used for VOL_MODE_CHANGE_HEARING_AID */ @@ -168,6 +173,7 @@ public class AudioServiceEvents { mCaller = null; mGroupName = null; mAudioAttributes = null; + logMetricEvent(); } /** used for VOL_SET_GROUP_VOL */ @@ -179,6 +185,102 @@ public class AudioServiceEvents { mCaller = caller; mGroupName = group; mAudioAttributes = aa; + logMetricEvent(); + } + + + /** + * Audio Analytics unique Id. + */ + private static final String mMetricsId = MediaMetrics.Name.AUDIO_VOLUME_EVENT; + + /** + * Log mediametrics event + */ + private void logMetricEvent() { + switch (mOp) { + case VOL_ADJUST_SUGG_VOL: + case VOL_ADJUST_VOL_UID: + case VOL_ADJUST_STREAM_VOL: { + String eventName; + switch (mOp) { + case VOL_ADJUST_SUGG_VOL: + eventName = "adjustSuggestedStreamVolume"; + break; + case VOL_ADJUST_STREAM_VOL: + eventName = "adjustStreamVolume"; + break; + case VOL_ADJUST_VOL_UID: + eventName = "adjustStreamVolumeForUid"; + break; + default: + return; // not possible, just return here + } + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller) + .set(MediaMetrics.Property.DIRECTION, mVal1 > 0 ? "up" : "down") + .set(MediaMetrics.Property.EVENT, eventName) + .set(MediaMetrics.Property.FLAGS, mVal2) + .set(MediaMetrics.Property.STREAM_TYPE, + AudioSystem.streamToString(mStream)) + .record(); + return; + } + case VOL_SET_STREAM_VOL: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller) + .set(MediaMetrics.Property.EVENT, "setStreamVolume") + .set(MediaMetrics.Property.FLAGS, mVal2) + .set(MediaMetrics.Property.INDEX, mVal1) + .set(MediaMetrics.Property.STREAM_TYPE, + AudioSystem.streamToString(mStream)) + .record(); + return; + case VOL_SET_HEARING_AID_VOL: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.EVENT, "setHearingAidVolume") + .set(MediaMetrics.Property.GAIN_DB, (double) mVal2) + .set(MediaMetrics.Property.INDEX, mVal1) + .record(); + return; + case VOL_SET_AVRCP_VOL: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.EVENT, "setAvrcpVolume") + .set(MediaMetrics.Property.INDEX, mVal1) + .record(); + return; + case VOL_VOICE_ACTIVITY_HEARING_AID: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.EVENT, "voiceActivityHearingAid") + .set(MediaMetrics.Property.INDEX, mVal1) + .set(MediaMetrics.Property.STATE, + mVal2 == 1 ? "active" : "inactive") + .set(MediaMetrics.Property.STREAM_TYPE, + AudioSystem.streamToString(mStream)) + .record(); + return; + case VOL_MODE_CHANGE_HEARING_AID: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.EVENT, "modeChangeHearingAid") + .set(MediaMetrics.Property.INDEX, mVal1) + .set(MediaMetrics.Property.MODE, AudioSystem.modeToString(mVal2)) + .set(MediaMetrics.Property.STREAM_TYPE, + AudioSystem.streamToString(mStream)) + .record(); + return; + case VOL_SET_GROUP_VOL: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.ATTRIBUTES, mAudioAttributes.toString()) + .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller) + .set(MediaMetrics.Property.EVENT, "setVolumeIndexForAttributes") + .set(MediaMetrics.Property.FLAGS, mVal2) + .set(MediaMetrics.Property.GROUP, mGroupName) + .set(MediaMetrics.Property.INDEX, mVal1) + .record(); + return; + default: + return; + } } @Override diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 93d1bede9de8..accb90cc3c0c 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -113,6 +113,24 @@ public class BtHelper { private static final int BT_HEARING_AID_GAIN_MIN = -128; + /** + * Returns a string representation of the scoAudioMode. + */ + public static String scoAudioModeToString(int scoAudioMode) { + switch (scoAudioMode) { + case SCO_MODE_UNDEFINED: + return "SCO_MODE_UNDEFINED"; + case SCO_MODE_VIRTUAL_CALL: + return "SCO_MODE_VIRTUAL_CALL"; + case SCO_MODE_RAW: + return "SCO_MODE_RAW"; + case SCO_MODE_VR: + return "SCO_MODE_VR"; + default: + return "SCO_MODE_(" + scoAudioMode + ")"; + } + } + //---------------------------------------------------------------------- /*package*/ static class BluetoothA2dpDeviceInfo { private final @NonNull BluetoothDevice mBtDevice; @@ -965,4 +983,27 @@ public class BtHelper { return AudioSystem.AUDIO_FORMAT_DEFAULT; } } + + /** + * Returns the String equivalent of the btCodecType. + * + * This uses an "ENCODING_" prefix for consistency with Audio; + * we could alternately use the "SOURCE_CODEC_TYPE_" prefix from Bluetooth. + */ + public static String bluetoothCodecToEncodingString(int btCodecType) { + switch (btCodecType) { + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC: + return "ENCODING_SBC"; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC: + return "ENCODING_AAC"; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX: + return "ENCODING_APTX"; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD: + return "ENCODING_APTX_HD"; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC: + return "ENCODING_LDAC"; + default: + return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")"; + } + } } diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index bfab9919258c..26281b739436 100755 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java @@ -25,6 +25,7 @@ import android.media.AudioFocusInfo; import android.media.AudioManager; import android.media.AudioSystem; import android.media.IAudioFocusDispatcher; +import android.media.MediaMetrics; import android.media.audiopolicy.IAudioPolicyCallback; import android.os.Binder; import android.os.Build; @@ -144,6 +145,8 @@ public class MediaFocusControl implements PlayerFocusEnforcer { private static final AudioEventLogger mEventLogger = new AudioEventLogger(50, "focus commands as seen by MediaFocusControl"); + private static final String mMetricsId = MediaMetrics.Name.AUDIO_FOCUS; + /*package*/ void noFocusForSuspendedApp(@NonNull String packageName, int uid) { synchronized (mAudioFocusLock) { final Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); @@ -818,6 +821,17 @@ public class MediaFocusControl implements PlayerFocusEnforcer { protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, int flags, int sdk, boolean forceDuck) { + new MediaMetrics.Item(mMetricsId) + .setUid(Binder.getCallingUid()) + .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) + .set(MediaMetrics.Property.CLIENT_NAME, clientId) + .set(MediaMetrics.Property.EVENT, "requestAudioFocus") + .set(MediaMetrics.Property.FLAGS, flags) + .set(MediaMetrics.Property.FOCUS_CHANGE_HINT, + AudioManager.audioFocusToString(focusChangeHint)) + //.set(MediaMetrics.Property.SDK, sdk) + .record(); + mEventLogger.log((new AudioEventLogger.StringEvent( "requestAudioFocus() from uid/pid " + Binder.getCallingUid() + "/" + Binder.getCallingPid() @@ -982,6 +996,13 @@ public class MediaFocusControl implements PlayerFocusEnforcer { * */ protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, String callingPackageName) { + new MediaMetrics.Item(mMetricsId) + .setUid(Binder.getCallingUid()) + .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) + .set(MediaMetrics.Property.CLIENT_NAME, clientId) + .set(MediaMetrics.Property.EVENT, "abandonAudioFocus") + .record(); + // AudioAttributes are currently ignored, to be used for zones / a11y mEventLogger.log((new AudioEventLogger.StringEvent( "abandonAudioFocus() from uid/pid " + Binder.getCallingUid() |