16aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal/*
26aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal * Copyright (C) 2014 The Android Open Source Project
36aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal *
46aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal * Licensed under the Apache License, Version 2.0 (the "License");
56aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal * you may not use this file except in compliance with the License.
66aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal * You may obtain a copy of the License at
76aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal *
86aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal *      http://www.apache.org/licenses/LICENSE-2.0
96aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal *
106aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal * Unless required by applicable law or agreed to in writing, software
116aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal * distributed under the License is distributed on an "AS IS" BASIS,
126aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal * See the License for the specific language governing permissions and
146aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal * limitations under the License.
156aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal */
166aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal
177cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnpackage com.android.server.telecom;
186aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal
196aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepalimport android.content.Context;
20ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liuimport android.media.AudioDeviceCallback;
21ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liuimport android.media.AudioDeviceInfo;
226aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepalimport android.media.AudioManager;
23a3eccfee788c3ac3c831a443b085b141b39bb63dBrad Ebingerimport android.telecom.Log;
246aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal
25f62630a57de0d52be2bdbc92a9bf8f305cc0892dHall Liuimport com.android.internal.annotations.VisibleForTesting;
269787e0e80d8960cf8b0ca74c7cdc4c4aac97187aTyler Gunnimport com.android.internal.util.IndentingPrintWriter;
279787e0e80d8960cf8b0ca74c7cdc4c4aac97187aTyler Gunn
28a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shraunerimport java.util.Collections;
29a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shraunerimport java.util.Set;
30a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shraunerimport java.util.concurrent.ConcurrentHashMap;
31b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal
32b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal/** Listens for and caches headset state. */
33f62630a57de0d52be2bdbc92a9bf8f305cc0892dHall Liu@VisibleForTesting
34f62630a57de0d52be2bdbc92a9bf8f305cc0892dHall Liupublic class WiredHeadsetManager {
35f62630a57de0d52be2bdbc92a9bf8f305cc0892dHall Liu    @VisibleForTesting
36f62630a57de0d52be2bdbc92a9bf8f305cc0892dHall Liu    public interface Listener {
37b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal        void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn);
38b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal    }
39b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal
406aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal    /** Receiver for wired headset plugged and unplugged events. */
41ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu    private class WiredHeadsetCallback extends AudioDeviceCallback {
42ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu        @Override
43ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu        public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
44ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu            Log.startSession("WHC.oADA");
45ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu            try {
46ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu                updateHeadsetStatus();
47ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu            } finally {
48ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu                Log.endSession();
49ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu            }
50ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu        }
51ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu
526aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal        @Override
53ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu        public void onAudioDevicesRemoved(AudioDeviceInfo[] devices) {
54ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu            Log.startSession("WHC.oADR");
5558f365891ab631d8c45ad69d7024519384e9b75aBrad Ebinger            try {
56ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu                updateHeadsetStatus();
5758f365891ab631d8c45ad69d7024519384e9b75aBrad Ebinger            } finally {
5858f365891ab631d8c45ad69d7024519384e9b75aBrad Ebinger                Log.endSession();
596aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal            }
606aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal        }
61ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu
62ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu        private void updateHeadsetStatus() {
6322c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin            final boolean isPluggedIn = isWiredHeadsetPluggedIn();
64ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu
6531ed9becf01386eb721ffc5803d5a8a6c64cd4cfHall Liu            Log.i(WiredHeadsetManager.this, "ACTION_HEADSET_PLUG event, plugged in: %b, ",
6631ed9becf01386eb721ffc5803d5a8a6c64cd4cfHall Liu                    isPluggedIn);
67ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu            onHeadsetPluggedInChanged(isPluggedIn);
68ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu        }
696aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal    }
706aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal
71c395e49e7b9aef59e5b4092945a8648f5adba3a0Santos Cordon    private final AudioManager mAudioManager;
726aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal    private boolean mIsPluggedIn;
73a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner    /**
74a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
75a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner     * load factor before resizing, 1 means we only expect a single thread to
76a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner     * access the map so make only a single shard
77a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner     */
78a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner    private final Set<Listener> mListeners = Collections.newSetFromMap(
79ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu            new ConcurrentHashMap<>(8, 0.9f, 1));
806aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal
81f62630a57de0d52be2bdbc92a9bf8f305cc0892dHall Liu    public WiredHeadsetManager(Context context) {
82c395e49e7b9aef59e5b4092945a8648f5adba3a0Santos Cordon        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
8322c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin        mIsPluggedIn = isWiredHeadsetPluggedIn();
846aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal
85ca9988ba4e8aed7d04835eca57b62f397c1a08f4Hall Liu        mAudioManager.registerAudioDeviceCallback(new WiredHeadsetCallback(), null);
866aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal    }
876aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal
88f62630a57de0d52be2bdbc92a9bf8f305cc0892dHall Liu    @VisibleForTesting
89f62630a57de0d52be2bdbc92a9bf8f305cc0892dHall Liu    public void addListener(Listener listener) {
90b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal        mListeners.add(listener);
91b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal    }
92b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal
93b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal    void removeListener(Listener listener) {
94a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner        if (listener != null) {
95a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner            mListeners.remove(listener);
96a82c8f794a0a1a9eaa1329a6361abe28043d139aJay Shrauner        }
97b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal    }
98b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal
99f62630a57de0d52be2bdbc92a9bf8f305cc0892dHall Liu    @VisibleForTesting
100f62630a57de0d52be2bdbc92a9bf8f305cc0892dHall Liu    public boolean isPluggedIn() {
1016aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal        return mIsPluggedIn;
1026aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal    }
1036aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal
10422c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin    private boolean isWiredHeadsetPluggedIn() {
10522c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin        AudioDeviceInfo[] devices = mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL);
10622c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin        boolean isPluggedIn = false;
10722c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin        for (AudioDeviceInfo device : devices) {
10822c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin            switch (device.getType()) {
10922c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin                case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
11022c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin                case AudioDeviceInfo.TYPE_WIRED_HEADSET:
11122c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin                case AudioDeviceInfo.TYPE_USB_HEADSET:
11222c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin                case AudioDeviceInfo.TYPE_USB_DEVICE:
11322c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin                    isPluggedIn = true;
11422c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin            }
11522c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin            if (isPluggedIn) {
11622c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin                break;
11722c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin            }
11822c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin        }
11922c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin        return isPluggedIn;
12022c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin    }
12122c2ce9cfe71b1d905b9af3bc28513a8eba180c4David Lin
1226aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal    private void onHeadsetPluggedInChanged(boolean isPluggedIn) {
1236aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal        if (mIsPluggedIn != isPluggedIn) {
1246aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal            Log.v(this, "onHeadsetPluggedInChanged, mIsPluggedIn: %b -> %b", mIsPluggedIn,
1256aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal                    isPluggedIn);
1266aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal            boolean oldIsPluggedIn = mIsPluggedIn;
1276aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal            mIsPluggedIn = isPluggedIn;
128b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal            for (Listener listener : mListeners) {
129b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal                listener.onWiredHeadsetPluggedInChanged(oldIsPluggedIn, mIsPluggedIn);
130b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal            }
1316aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal        }
1326aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal    }
1339787e0e80d8960cf8b0ca74c7cdc4c4aac97187aTyler Gunn
1349787e0e80d8960cf8b0ca74c7cdc4c4aac97187aTyler Gunn    /**
1359787e0e80d8960cf8b0ca74c7cdc4c4aac97187aTyler Gunn     * Dumps the state of the {@link WiredHeadsetManager}.
1369787e0e80d8960cf8b0ca74c7cdc4c4aac97187aTyler Gunn     *
1379787e0e80d8960cf8b0ca74c7cdc4c4aac97187aTyler Gunn     * @param pw The {@code IndentingPrintWriter} to write the state to.
1389787e0e80d8960cf8b0ca74c7cdc4c4aac97187aTyler Gunn     */
1399787e0e80d8960cf8b0ca74c7cdc4c4aac97187aTyler Gunn    public void dump(IndentingPrintWriter pw) {
1409787e0e80d8960cf8b0ca74c7cdc4c4aac97187aTyler Gunn        pw.println("mIsPluggedIn: " + mIsPluggedIn);
1419787e0e80d8960cf8b0ca74c7cdc4c4aac97187aTyler Gunn    }
1426aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal}
143