From 0e4242f9cb332e5aa7d271f67ded75aa59f3023f Mon Sep 17 00:00:00 2001 From: Paul McLean Date: Thu, 5 Feb 2015 12:52:35 -0800 Subject: Audio Device Enumeration API. Change-Id: Ic78fda8b2a6f6ac0f37a1f665b29f3359cfc5ecc --- media/java/android/media/AudioDevicesManager.java | 272 +++++++++++++++++++++ .../media/OnAudioDeviceConnectionListener.java | 28 +++ 2 files changed, 300 insertions(+) create mode 100644 media/java/android/media/AudioDevicesManager.java create mode 100644 media/java/android/media/OnAudioDeviceConnectionListener.java (limited to 'media') diff --git a/media/java/android/media/AudioDevicesManager.java b/media/java/android/media/AudioDevicesManager.java new file mode 100644 index 0000000..4e52953 --- /dev/null +++ b/media/java/android/media/AudioDevicesManager.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.content.Context; +import android.util.Slog; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import android.content.Context; + +/** @hide + * API candidate + */ +public class AudioDevicesManager { + private static String TAG = "AudioDevicesManager"; + private static boolean DEBUG = true; + + private AudioManager mAudioManager = null; + private OnAmPortUpdateListener mPortListener = null; + + /* + * Enum/Selection API + */ + public AudioDevicesManager(Context context) { + mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); + mPortListener = new OnAmPortUpdateListener(); + mAudioManager.registerAudioPortUpdateListener(mPortListener); + } + + /** @hide + * API candidate + */ + //TODO Merge this class into android.media.AudioDevice + public class AudioDeviceInfo { + private AudioDevicePort mPort = null; + + /** @hide */ + /* package */ AudioDeviceInfo(AudioDevicePort port) { + mPort = port; + } + + public int getId() { return mPort.handle().id(); } + + public String getName() { return mPort.name(); } + + public int getType() { + return mPort.type(); + } + + public String getAddress() { + return mPort.address(); + } + + public int getRole() { return mPort.role(); } + + public int[] getSampleRates() { return mPort.samplingRates(); } + + public int[] getChannelMasks() { return mPort.channelMasks(); } + + public int[] getChannelCounts() { + int[] masks = getChannelMasks(); + int[] counts = new int[masks.length]; + for (int mask_index = 0; mask_index < masks.length; mask_index++) { + counts[mask_index] = getRole() == AudioPort.ROLE_SINK + ? AudioFormat.channelCountFromOutChannelMask(masks[mask_index]) + : AudioFormat.channelCountFromInChannelMask(masks[mask_index]); + } + return counts; + } + + /* The format IDs are in AudioFormat.java */ + public int[] getFormats() { return mPort.formats(); } + + public String toString() { return "" + getId() + " - " + getName(); } + } + + /** @hide */ + public static final int LIST_DEVICES_OUTPUTS = 0x0001; + /** @hide */ + public static final int LIST_DEVICES_INPUTS = 0x0002; + /** @hide */ + public static final int LIST_DEVICES_BUILTIN = 0x0004; + /** @hide */ + public static final int LIST_DEVICES_USB = 0x0008; + // TODO implement the semantics for these. + /** @hide */ + public static final int LIST_DEVICES_WIRED = 0x0010; + /** @hide */ + public static final int LIST_DEVICES_UNWIRED = 0x0020; + + /** @hide */ + public static final int LIST_DEVICES_ALL = LIST_DEVICES_OUTPUTS | LIST_DEVICES_INPUTS; + + private boolean checkFlags(AudioDevicePort port, int flags) { + // Inputs / Outputs + boolean passed = + port.role() == AudioPort.ROLE_SINK && (flags & LIST_DEVICES_OUTPUTS) != 0 || + port.role() == AudioPort.ROLE_SOURCE && (flags & LIST_DEVICES_INPUTS) != 0; + + // USB + if (passed && (flags & LIST_DEVICES_USB) != 0) { + int role = port.role(); + int type = port.type(); + Slog.i(TAG, " role:" + role + " type:0x" + Integer.toHexString(type)); + passed = + (role == AudioPort.ROLE_SINK && (type & AudioSystem.DEVICE_OUT_ALL_USB) != 0) || + (role == AudioPort.ROLE_SOURCE && (type & AudioSystem.DEVICE_IN_ALL_USB) != 0); + } + + return passed; + } + + /** @hide */ + public ArrayList listDevices(int flags) { + Slog.i(TAG, "AudioManager.listDevices(" + Integer.toHexString(flags) + ")"); + + //FIXME - Use ArrayList when mAudioManager.listAudioDevicePorts() is fixed. + ArrayList ports = new ArrayList(); + int status = mAudioManager.listAudioDevicePorts(ports); + + Slog.i(TAG, " status:" + status + " numPorts:" + ports.size()); + + ArrayList deviceList = new ArrayList(); + + if (status == AudioManager.SUCCESS) { + deviceList = new ArrayList(); + for (AudioPort port : ports) { + if (/*port instanceof AudioDevicePort &&*/ checkFlags((AudioDevicePort)port, flags)) { + deviceList.add(new AudioDeviceInfo((AudioDevicePort)port)); + } + } + } + return deviceList; + } + + private ArrayList mDeviceConnectionListeners = + new ArrayList(); + + private HashMap mCurrentPortlist = + new HashMap(); + + private ArrayList calcAddedDevices(AudioPort[] portList) { + ArrayList addedDevices = new ArrayList(); + synchronized(mCurrentPortlist) { + for(int portIndex = 0; portIndex < portList.length; portIndex++) { + if (portList[portIndex] instanceof AudioDevicePort) { + if (!mCurrentPortlist.containsKey(portList[portIndex].handle().id())) { + addedDevices.add(new AudioDeviceInfo((AudioDevicePort)portList[portIndex])); + } + } + } + } + return addedDevices; + } + + private boolean hasPortId(AudioPort[] portList, int id) { + for(int portIndex = 0; portIndex < portList.length; portIndex++) { + if (portList[portIndex].handle().id() == id) { + return true; + } + } + return false; + } + + private ArrayList calcRemovedDevices(AudioPort[] portList) { + ArrayList removedDevices = new ArrayList(); + + synchronized (mCurrentPortlist) { + Iterator it = mCurrentPortlist.entrySet().iterator(); + while (it.hasNext()) { + HashMap.Entry pairs = (HashMap.Entry)it.next(); + if (pairs.getValue() instanceof AudioDevicePort) { + if (!hasPortId(portList, ((Integer)pairs.getKey()).intValue())) { + removedDevices.add(new AudioDeviceInfo((AudioDevicePort)pairs.getValue())); + } + } + } + } + return removedDevices; + } + + private void buildCurrentDevicesList(AudioPort[] portList) { + synchronized (mCurrentPortlist) { + mCurrentPortlist.clear(); + for (int portIndex = 0; portIndex < portList.length; portIndex++) { + if (portList[portIndex] instanceof AudioDevicePort) { + mCurrentPortlist.put(portList[portIndex].handle().id(), + (AudioDevicePort)portList[portIndex]); + } + } + } + } + + /** @hide */ + public void addDeviceConnectionListener(OnAudioDeviceConnectionListener listener) { + synchronized (mDeviceConnectionListeners) { + mDeviceConnectionListeners.add(listener); + } + } + + /** @hide */ + public void removeDeviceConnectionListener(OnAudioDeviceConnectionListener listener) { + synchronized (mDeviceConnectionListeners) { + mDeviceConnectionListeners.remove(listener); + } + } + + /** + * @hide + */ + private class OnAmPortUpdateListener implements AudioManager.OnAudioPortUpdateListener { + static final String TAG = "OnAmPortUpdateListener"; + public void onAudioPortListUpdate(AudioPort[] portList) { + Slog.i(TAG, "onAudioPortListUpdate() " + portList.length + " ports."); + ArrayList addedDevices = calcAddedDevices(portList); + ArrayList removedDevices = calcRemovedDevices(portList); + + ArrayList listeners = null; + synchronized (mDeviceConnectionListeners) { + listeners = + new ArrayList(mDeviceConnectionListeners); + } + + // Connect + if (addedDevices.size() != 0) { + for (OnAudioDeviceConnectionListener listener : listeners) { + listener.onConnect(addedDevices); + } + } + + // Disconnect? + if (removedDevices.size() != 0) { + for (OnAudioDeviceConnectionListener listener : listeners) { + listener.onDisconnect(removedDevices); + } + } + + buildCurrentDevicesList(portList); + } + + /** + * Callback method called upon audio patch list update. + * @param patchList the updated list of audio patches + */ + public void onAudioPatchListUpdate(AudioPatch[] patchList) { + Slog.i(TAG, "onAudioPatchListUpdate() " + patchList.length + " patches."); + } + + /** + * Callback method called when the mediaserver dies + */ + public void onServiceDied() { + Slog.i(TAG, "onServiceDied()"); + } + } +} diff --git a/media/java/android/media/OnAudioDeviceConnectionListener.java b/media/java/android/media/OnAudioDeviceConnectionListener.java new file mode 100644 index 0000000..4bdd4d0 --- /dev/null +++ b/media/java/android/media/OnAudioDeviceConnectionListener.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import java.util.ArrayList; + +/** + * @hide + * API candidate + */ +public abstract class OnAudioDeviceConnectionListener { + public void onConnect(ArrayList devices) {} + public void onDisconnect(ArrayList devices) {} +} -- cgit v1.1