blob: fee0a63f356e4113814ed1a2833be0359020e3e0 [file] [log] [blame]
Sailesh Nepal6aca10a2014-03-24 16:11:02 -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;
Evan Charlton198fde82014-04-07 10:53:11 -070021import android.os.SystemVibrator;
22import android.os.Vibrator;
23import android.provider.Settings;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070024import android.telecomm.CallState;
25
26import com.google.common.collect.Lists;
27
28import java.util.List;
29
30/**
31 * Controls the ringtone player.
32 */
33final class Ringer extends CallsManagerListenerBase {
Evan Charlton198fde82014-04-07 10:53:11 -070034 private static final long[] VIBRATION_PATTERN = new long[] {
35 0, // No delay before starting
36 1000, // How long to vibrate
37 1000, // How long to wait before vibrating again
38 };
39
40 /** Indicate that we want the pattern to repeat at the step which turns on vibration. */
41 private static final int VIBRATION_PATTERN_REPEAT = 1;
42
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070043 private final AsyncRingtonePlayer mRingtonePlayer = new AsyncRingtonePlayer();
44
45 /**
46 * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
47 * calls and explicit ordering is useful for maintaining the proper state of the ringer.
48 */
Santos Cordonb64c1502014-05-21 21:21:49 -070049 private final List<Call> mRingingCalls = Lists.newLinkedList();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070050
51 private final CallAudioManager mCallAudioManager;
Santos Cordon40f78c22014-04-07 02:11:42 -070052 private final CallsManager mCallsManager;
53 private final InCallTonePlayer.Factory mPlayerFactory;
54 private final Context mContext;
Evan Charlton198fde82014-04-07 10:53:11 -070055 private final Vibrator mVibrator;
56
Santos Cordon40f78c22014-04-07 02:11:42 -070057 private InCallTonePlayer mCallWaitingPlayer;
58
Evan Charlton198fde82014-04-07 10:53:11 -070059 /**
60 * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
61 */
62 private boolean mIsVibrating = false;
63
Santos Cordon40f78c22014-04-07 02:11:42 -070064 /** Initializes the Ringer. */
65 Ringer(
66 CallAudioManager callAudioManager,
67 CallsManager callsManager,
68 InCallTonePlayer.Factory playerFactory,
69 Context context) {
Evan Charlton198fde82014-04-07 10:53:11 -070070
Santos Cordon40f78c22014-04-07 02:11:42 -070071 mCallAudioManager = callAudioManager;
72 mCallsManager = callsManager;
73 mPlayerFactory = playerFactory;
74 mContext = context;
Evan Charlton198fde82014-04-07 10:53:11 -070075 // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
76 // vibrator object will be isolated from others.
77 mVibrator = new SystemVibrator(TelecommApp.getInstance());
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070078 }
79
80 @Override
81 public void onCallAdded(Call call) {
82 if (call.isIncoming() && call.getState() == CallState.RINGING) {
Santos Cordonb64c1502014-05-21 21:21:49 -070083 if (mRingingCalls.contains(call)) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070084 Log.wtf(this, "New ringing call is already in list of unanswered calls");
85 }
Santos Cordonb64c1502014-05-21 21:21:49 -070086 mRingingCalls.add(call);
Santos Cordon40f78c22014-04-07 02:11:42 -070087 updateRinging();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070088 }
89 }
90
Santos Cordon40f78c22014-04-07 02:11:42 -070091
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070092 @Override
93 public void onCallRemoved(Call call) {
Sailesh Nepale59bb192014-04-01 18:33:59 -070094 removeFromUnansweredCall(call);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070095 }
96
97 @Override
98 public void onCallStateChanged(Call call, CallState oldState, CallState newState) {
99 if (newState != CallState.RINGING) {
Sailesh Nepale59bb192014-04-01 18:33:59 -0700100 removeFromUnansweredCall(call);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700101 }
102 }
103
104 @Override
105 public void onIncomingCallAnswered(Call call) {
106 onRespondedToIncomingCall(call);
107 }
108
109 @Override
110 public void onIncomingCallRejected(Call call) {
111 onRespondedToIncomingCall(call);
112 }
113
Santos Cordon40f78c22014-04-07 02:11:42 -0700114 @Override
115 public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) {
Santos Cordonb64c1502014-05-21 21:21:49 -0700116 if (mRingingCalls.contains(oldForegroundCall) ||
117 mRingingCalls.contains(newForegroundCall)) {
Santos Cordon40f78c22014-04-07 02:11:42 -0700118 updateRinging();
119 }
120 }
121
Santos Cordonb64c1502014-05-21 21:21:49 -0700122 /**
123 * Silences the ringer for any actively ringing calls.
124 */
125 void silence() {
126 // Remove all calls from the "ringing" set and then update the ringer.
127 mRingingCalls.clear();
128 updateRinging();
129 }
130
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700131 private void onRespondedToIncomingCall(Call call) {
132 // Only stop the ringer if this call is the top-most incoming call.
Santos Cordon40f78c22014-04-07 02:11:42 -0700133 if (getTopMostUnansweredCall() == call) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700134 stopRinging();
Santos Cordon40f78c22014-04-07 02:11:42 -0700135 stopCallWaiting();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700136 }
Santos Cordon40f78c22014-04-07 02:11:42 -0700137
Santos Cordonb64c1502014-05-21 21:21:49 -0700138 // We do not remove the call from mRingingCalls until the call state changes from RINGING
Santos Cordon40f78c22014-04-07 02:11:42 -0700139 // or the call is removed. see onCallStateChanged or onCallRemoved.
140 }
141
142 private Call getTopMostUnansweredCall() {
Santos Cordonb64c1502014-05-21 21:21:49 -0700143 return mRingingCalls.isEmpty() ? null : mRingingCalls.get(0);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700144 }
145
146 /**
147 * Removes the specified call from the list of unanswered incoming calls and updates the ringer
Santos Cordonb64c1502014-05-21 21:21:49 -0700148 * based on the new state of {@link #mRingingCalls}. Safe to call with a call that is not
Sailesh Nepale59bb192014-04-01 18:33:59 -0700149 * present in the list of incoming calls.
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700150 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700151 private void removeFromUnansweredCall(Call call) {
Santos Cordonb64c1502014-05-21 21:21:49 -0700152 mRingingCalls.remove(call);
Santos Cordon40f78c22014-04-07 02:11:42 -0700153 updateRinging();
154 }
155
156 private void updateRinging() {
Santos Cordonb64c1502014-05-21 21:21:49 -0700157 if (mRingingCalls.isEmpty()) {
Santos Cordon40f78c22014-04-07 02:11:42 -0700158 stopRinging();
159 stopCallWaiting();
160 } else {
161 startRingingOrCallWaiting();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700162 }
163 }
164
Santos Cordon40f78c22014-04-07 02:11:42 -0700165 private void startRingingOrCallWaiting() {
166 Call foregroundCall = mCallsManager.getForegroundCall();
167 Log.v(this, "startRingingOrCallWaiting, foregroundCall: %s.", foregroundCall);
Evan Charlton198fde82014-04-07 10:53:11 -0700168
Santos Cordonb64c1502014-05-21 21:21:49 -0700169 if (mRingingCalls.contains(foregroundCall)) {
Santos Cordon40f78c22014-04-07 02:11:42 -0700170 // The foreground call is one of incoming calls so play the ringer out loud.
171 stopCallWaiting();
172
173 AudioManager audioManager =
174 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
175 if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) {
176 Log.v(this, "startRingingOrCallWaiting");
177 mCallAudioManager.setIsRinging(true);
178
Santos Cordonc7e85d42014-05-22 02:51:48 -0700179 // Only play ringtone if a bluetooth device is not available. When a BT device
180 // is available, then we send it a signal to do its own ringtone and we dont need
181 // to play the ringtone on the device.
182 if (!mCallAudioManager.isBluetoothDeviceAvailable()) {
183 mRingtonePlayer.play();
184 }
Santos Cordon40f78c22014-04-07 02:11:42 -0700185 } else {
186 Log.v(this, "startRingingOrCallWaiting, skipping because volume is 0");
187 }
Evan Charlton2186f7f2014-04-10 12:00:37 -0700188
189 if (shouldVibrate(TelecommApp.getInstance()) && !mIsVibrating) {
190 mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,
191 AudioManager.STREAM_RING);
192 mIsVibrating = true;
193 }
Santos Cordon40f78c22014-04-07 02:11:42 -0700194 } else {
195 Log.v(this, "Playing call-waiting tone.");
196
197 // All incoming calls are in background so play call waiting.
198 stopRinging();
199
200 if (mCallWaitingPlayer == null) {
201 mCallWaitingPlayer =
202 mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);
203 mCallWaitingPlayer.startTone();
204 }
Evan Charlton198fde82014-04-07 10:53:11 -0700205 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700206 }
207
208 private void stopRinging() {
209 Log.v(this, "stopRinging");
Santos Cordon40f78c22014-04-07 02:11:42 -0700210
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700211 mRingtonePlayer.stop();
Evan Charlton198fde82014-04-07 10:53:11 -0700212
213 if (mIsVibrating) {
214 mVibrator.cancel();
215 mIsVibrating = false;
216 }
Santos Cordon40f78c22014-04-07 02:11:42 -0700217
218 // Even though stop is asynchronous it's ok to update the audio manager. Things like audio
219 // focus are voluntary so releasing focus too early is not detrimental.
220 mCallAudioManager.setIsRinging(false);
221 }
222
223 private void stopCallWaiting() {
224 Log.v(this, "stop call waiting.");
225 if (mCallWaitingPlayer != null) {
226 mCallWaitingPlayer.stopTone();
227 mCallWaitingPlayer = null;
228 }
Evan Charlton198fde82014-04-07 10:53:11 -0700229 }
230
231 private boolean shouldVibrate(Context context) {
232 AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
233 int ringerMode = audioManager.getRingerMode();
234 if (getVibrateWhenRinging(context)) {
235 return ringerMode != AudioManager.RINGER_MODE_SILENT;
236 } else {
237 return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
238 }
239 }
240
241 private boolean getVibrateWhenRinging(Context context) {
242 if (!mVibrator.hasVibrator()) {
243 return false;
244 }
245 return Settings.System.getInt(context.getContentResolver(),
246 Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700247 }
248}