blob: 0d58f863b476e8d6dd8f6b0738b0c3a3a26da3fa [file] [log] [blame]
Sailesh Nepal810735e2014-03-18 18:15:46 -07001/*
2 * Copyright (C) 2014 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
17package com.android.telecomm;
18
19import android.content.Context;
20import android.media.AudioManager;
21import android.telecomm.CallState;
22
Santos Cordon1ae2b852014-03-19 03:03:10 -070023import com.google.common.collect.Lists;
24
25import java.util.List;
26
Sailesh Nepal810735e2014-03-18 18:15:46 -070027/**
28 * This class manages audio modes, streams and other properties.
29 */
30final class CallAudioManager extends CallsManagerListenerBase {
Santos Cordon1ae2b852014-03-19 03:03:10 -070031 private AsyncRingtonePlayer mRinger = new AsyncRingtonePlayer();
32
33 private boolean mHasAudioFocus = false;
34
35 /**
36 * Used to keep ordering of unanswered incoming calls. The existence of multiple call services
37 * means that there can easily exist multiple incoming calls and explicit ordering is useful for
38 * maintaining the proper state of the ringer.
39 */
40 private final List<String> mUnansweredCallIds = Lists.newLinkedList();
41
42 /**
43 * Denotes when the ringer is disabled. This is useful in temporarily disabling the ringer when
44 * the a call is answered/rejected by the user, but the call hasn't actually moved out of the
45 * ringing state.
46 */
47 private boolean mIsRingingDisabled = false;
48
49 @Override
50 public void onCallAdded(Call call) {
51 if (call.getState() == CallState.RINGING) {
52 mUnansweredCallIds.add(call.getId());
53 }
54 updateAudio();
55 }
56
57 @Override
58 public void onCallRemoved(Call call) {
59 removeFromUnansweredCallIds(call.getId());
60 updateAudio();
61 }
62
Sailesh Nepal810735e2014-03-18 18:15:46 -070063 @Override
64 public void onCallStateChanged(Call call, CallState oldState, CallState newState) {
Santos Cordon1ae2b852014-03-19 03:03:10 -070065 if (oldState == CallState.RINGING) {
66 removeFromUnansweredCallIds(call.getId());
67 }
68
69 updateAudio();
70 }
71
72 @Override
73 public void onIncomingCallAnswered(Call call) {
74 mIsRingingDisabled = true;
75 updateAudio();
76 }
77
78 @Override
79 public void onIncomingCallRejected(Call call) {
80 mIsRingingDisabled = true;
81 updateAudio();
82 }
83
84 /**
85 * Reads the current state of all calls from CallsManager and sets the appropriate audio modes
86 * as well as triggers the start/stop of the ringer.
87 */
88 private void updateAudio() {
89 CallsManager callsManager = CallsManager.getInstance();
90
91 boolean hasRingingCall = !mIsRingingDisabled && !mUnansweredCallIds.isEmpty();
92 boolean hasLiveCall = callsManager.hasCallWithState(CallState.ACTIVE, CallState.DIALING);
93
94 int mode = hasRingingCall ? AudioManager.MODE_RINGTONE :
95 hasLiveCall ? AudioManager.MODE_IN_CALL :
96 AudioManager.MODE_NORMAL;
97
98 boolean needsFocus = (mode != AudioManager.MODE_NORMAL);
99
100 // Acquiring focus needs to be first, unlike releasing focus, which happens at the end.
101 if (needsFocus) {
102 acquireFocus(hasRingingCall);
103 setMode(mode);
104 }
105
106 if (hasRingingCall) {
107 mRinger.play();
108 } else {
109 mRinger.stop();
110 }
111
112 if (!needsFocus) {
113 setMode(AudioManager.MODE_NORMAL);
114 releaseFocus();
Sailesh Nepal810735e2014-03-18 18:15:46 -0700115 }
116 }
117
Santos Cordon1ae2b852014-03-19 03:03:10 -0700118 /**
119 * Acquires audio focus.
120 *
121 * @param isForRinging True if this focus is for playing the ringer.
122 */
123 private void acquireFocus(boolean isForRinging) {
124 if (!mHasAudioFocus) {
125 int stream = isForRinging ? AudioManager.STREAM_RING : AudioManager.STREAM_VOICE_CALL;
126
127 AudioManager audioManager = getAudioManager();
128 audioManager.requestAudioFocusForCall(stream, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
129 audioManager.setMicrophoneMute(false);
130 audioManager.setSpeakerphoneOn(false);
131 mHasAudioFocus = true;
132 }
Sailesh Nepal810735e2014-03-18 18:15:46 -0700133 }
134
Santos Cordon1ae2b852014-03-19 03:03:10 -0700135 /**
136 * Releases focus.
137 */
138 void releaseFocus() {
139 if (mHasAudioFocus) {
140 AudioManager audioManager = getAudioManager();
141
142 // Reset speakerphone and mute in case they were changed by telecomm.
143 audioManager.setMicrophoneMute(false);
144 audioManager.setSpeakerphoneOn(false);
145 audioManager.abandonAudioFocusForCall();
146
147 mHasAudioFocus = false;
148 Log.v(this, "Focus released");
149 }
150
151 }
152
153 /**
154 * Sets the audio mode.
155 *
156 * @param mode Mode constant from AudioManager.MODE_*.
157 */
158 void setMode(int mode) {
159 if (mHasAudioFocus) {
160 AudioManager audioManager = getAudioManager();
161 if (mode != audioManager.getMode()) {
162 Log.v(this, "Audio mode set to %d.", mode);
163 audioManager.setMode(mode);
164 Log.v(this, "Audio mode actually set to %d.", audioManager.getMode());
165 }
166 } else {
167 Log.wtf(this, "Trying to set audio mode to %d without focus.", mode);
168 }
169 }
170
171 /**
172 * Removes the specified call from the list of unanswered incoming calls.
173 *
174 * @param callId The ID of the call.
175 */
176 private void removeFromUnansweredCallIds(String callId) {
177 if (!mUnansweredCallIds.isEmpty()) {
178 // If the call is the top-most call, then no longer disable the ringer.
179 if (callId.equals(mUnansweredCallIds.get(0))) {
180 mIsRingingDisabled = false;
181 }
182
183 mUnansweredCallIds.remove(callId);
184 }
185 }
186
187 /**
188 * Returns the system audio manager.
189 */
190 private AudioManager getAudioManager() {
191 return (AudioManager) TelecommApp.getInstance().getSystemService(Context.AUDIO_SERVICE);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700192 }
193}