1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.incallui.audiomode;
18 
19 import android.content.Context;
20 import android.media.AudioDeviceInfo;
21 import android.media.AudioManager;
22 import android.telecom.CallAudioState;
23 import com.android.dialer.common.LogUtil;
24 import java.util.ArrayList;
25 import java.util.List;
26 
27 /** Proxy class for getting and setting the audio mode. */
28 public class AudioModeProvider {
29   private static final int SUPPORTED_AUDIO_ROUTE_ALL =
30       CallAudioState.ROUTE_EARPIECE
31           | CallAudioState.ROUTE_BLUETOOTH
32           | CallAudioState.ROUTE_WIRED_HEADSET
33           | CallAudioState.ROUTE_SPEAKER;
34 
35   private static final AudioModeProvider instance = new AudioModeProvider();
36   private final List<AudioModeListener> listeners = new ArrayList<>();
37   private CallAudioState audioState =
38       new CallAudioState(false, CallAudioState.ROUTE_EARPIECE, SUPPORTED_AUDIO_ROUTE_ALL);
39 
getInstance()40   public static AudioModeProvider getInstance() {
41     return instance;
42   }
43 
onAudioStateChanged(CallAudioState audioState)44   public void onAudioStateChanged(CallAudioState audioState) {
45     if (!this.audioState.equals(audioState)) {
46       this.audioState = audioState;
47       for (AudioModeListener listener : listeners) {
48         listener.onAudioStateChanged(audioState);
49       }
50     }
51   }
52 
addListener(AudioModeListener listener)53   public void addListener(AudioModeListener listener) {
54     if (!listeners.contains(listener)) {
55       listeners.add(listener);
56       listener.onAudioStateChanged(audioState);
57     }
58   }
59 
removeListener(AudioModeListener listener)60   public void removeListener(AudioModeListener listener) {
61     listeners.remove(listener);
62   }
63 
getAudioState()64   public CallAudioState getAudioState() {
65     return audioState;
66   }
67 
68   /**
69    * Sets a approximated audio state before {@link #onAudioStateChanged} is called. Classes such as
70    * {@link com.android.incallui.ProximitySensor} fetches the audio state before it is updated by
71    * telecom. This method attempts to guess the correct routing based on connected audio devices.
72    * The audio state may still be wrong on a second call due to a bug, telecom setting the
73    * route back to earpiece when a call ends.
74    */
initializeAudioState(Context context)75   public void initializeAudioState(Context context) {
76     onAudioStateChanged(
77         new CallAudioState(false, getApproximatedAudioRoute(context), SUPPORTED_AUDIO_ROUTE_ALL));
78   }
79 
getApproximatedAudioRoute(Context context)80   private static int getApproximatedAudioRoute(Context context) {
81     AudioManager audioManager = context.getSystemService(AudioManager.class);
82     boolean hasBluetooth = false;
83     boolean hasHeadset = false;
84     for (AudioDeviceInfo info : audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
85       switch (info.getType()) {
86         case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
87         case AudioDeviceInfo.TYPE_BLUETOOTH_SCO:
88           hasBluetooth = true;
89           continue;
90         case AudioDeviceInfo.TYPE_WIRED_HEADSET:
91           hasHeadset = true;
92           continue;
93         default:
94           continue;
95       }
96     }
97     if (hasBluetooth) {
98       LogUtil.i("AudioModeProvider.getApproximatedAudioRoute", "Routing to bluetooth");
99       return CallAudioState.ROUTE_BLUETOOTH;
100     }
101     if (hasHeadset) {
102       LogUtil.i("AudioModeProvider.getApproximatedAudioRoute", "Routing to headset");
103       return CallAudioState.ROUTE_WIRED_HEADSET;
104     }
105     LogUtil.i("AudioModeProvider.getApproximatedAudioRoute", "Routing to earpiece");
106     return CallAudioState.ROUTE_EARPIECE;
107   }
108 
109   /** Notified on changes to audio mode. */
110   public interface AudioModeListener {
111 
onAudioStateChanged(CallAudioState audioState)112     void onAudioStateChanged(CallAudioState audioState);
113   }
114 }
115