blob: 0b54a2b0269a71779a2989da0627e0427c0b0cd6 [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
Santos Cordon5ba7f272014-05-28 13:59:49 -070026import java.util.LinkedList;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070027import java.util.List;
28
29/**
30 * Controls the ringtone player.
31 */
32final class Ringer extends CallsManagerListenerBase {
Evan Charlton198fde82014-04-07 10:53:11 -070033 private static final long[] VIBRATION_PATTERN = new long[] {
34 0, // No delay before starting
35 1000, // How long to vibrate
36 1000, // How long to wait before vibrating again
37 };
38
39 /** Indicate that we want the pattern to repeat at the step which turns on vibration. */
40 private static final int VIBRATION_PATTERN_REPEAT = 1;
41
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070042 private final AsyncRingtonePlayer mRingtonePlayer = new AsyncRingtonePlayer();
43
44 /**
45 * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
46 * calls and explicit ordering is useful for maintaining the proper state of the ringer.
47 */
Santos Cordon5ba7f272014-05-28 13:59:49 -070048 private final List<Call> mRingingCalls = new LinkedList<>();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070049
50 private final CallAudioManager mCallAudioManager;
Santos Cordon40f78c22014-04-07 02:11:42 -070051 private final CallsManager mCallsManager;
52 private final InCallTonePlayer.Factory mPlayerFactory;
53 private final Context mContext;
Evan Charlton198fde82014-04-07 10:53:11 -070054 private final Vibrator mVibrator;
55
Santos Cordon40f78c22014-04-07 02:11:42 -070056 private InCallTonePlayer mCallWaitingPlayer;
57
Evan Charlton198fde82014-04-07 10:53:11 -070058 /**
59 * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
60 */
61 private boolean mIsVibrating = false;
62
Santos Cordon40f78c22014-04-07 02:11:42 -070063 /** Initializes the Ringer. */
64 Ringer(
65 CallAudioManager callAudioManager,
66 CallsManager callsManager,
67 InCallTonePlayer.Factory playerFactory,
68 Context context) {
Evan Charlton198fde82014-04-07 10:53:11 -070069
Santos Cordon40f78c22014-04-07 02:11:42 -070070 mCallAudioManager = callAudioManager;
71 mCallsManager = callsManager;
72 mPlayerFactory = playerFactory;
73 mContext = context;
Evan Charlton198fde82014-04-07 10:53:11 -070074 // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
75 // vibrator object will be isolated from others.
76 mVibrator = new SystemVibrator(TelecommApp.getInstance());
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070077 }
78
79 @Override
Santos Cordon5ba7f272014-05-28 13:59:49 -070080 public void onCallAdded(final Call call) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070081 if (call.isIncoming() && call.getState() == CallState.RINGING) {
Santos Cordonb64c1502014-05-21 21:21:49 -070082 if (mRingingCalls.contains(call)) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070083 Log.wtf(this, "New ringing call is already in list of unanswered calls");
84 }
Santos Cordonb64c1502014-05-21 21:21:49 -070085 mRingingCalls.add(call);
Santos Cordon40f78c22014-04-07 02:11:42 -070086 updateRinging();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070087 }
88 }
89
90 @Override
91 public void onCallRemoved(Call call) {
Sailesh Nepale59bb192014-04-01 18:33:59 -070092 removeFromUnansweredCall(call);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070093 }
94
95 @Override
96 public void onCallStateChanged(Call call, CallState oldState, CallState newState) {
97 if (newState != CallState.RINGING) {
Sailesh Nepale59bb192014-04-01 18:33:59 -070098 removeFromUnansweredCall(call);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070099 }
100 }
101
102 @Override
103 public void onIncomingCallAnswered(Call call) {
104 onRespondedToIncomingCall(call);
105 }
106
107 @Override
Ihab Awadff7493a2014-06-10 13:47:44 -0700108 public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700109 onRespondedToIncomingCall(call);
110 }
111
Santos Cordon40f78c22014-04-07 02:11:42 -0700112 @Override
113 public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) {
Santos Cordonb64c1502014-05-21 21:21:49 -0700114 if (mRingingCalls.contains(oldForegroundCall) ||
115 mRingingCalls.contains(newForegroundCall)) {
Santos Cordon40f78c22014-04-07 02:11:42 -0700116 updateRinging();
117 }
118 }
119
Santos Cordonb64c1502014-05-21 21:21:49 -0700120 /**
121 * Silences the ringer for any actively ringing calls.
122 */
123 void silence() {
124 // Remove all calls from the "ringing" set and then update the ringer.
125 mRingingCalls.clear();
126 updateRinging();
127 }
128
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700129 private void onRespondedToIncomingCall(Call call) {
130 // Only stop the ringer if this call is the top-most incoming call.
Santos Cordon40f78c22014-04-07 02:11:42 -0700131 if (getTopMostUnansweredCall() == call) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700132 stopRinging();
Santos Cordon40f78c22014-04-07 02:11:42 -0700133 stopCallWaiting();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700134 }
Santos Cordon40f78c22014-04-07 02:11:42 -0700135
Santos Cordonb64c1502014-05-21 21:21:49 -0700136 // We do not remove the call from mRingingCalls until the call state changes from RINGING
Santos Cordon40f78c22014-04-07 02:11:42 -0700137 // or the call is removed. see onCallStateChanged or onCallRemoved.
138 }
139
140 private Call getTopMostUnansweredCall() {
Santos Cordonb64c1502014-05-21 21:21:49 -0700141 return mRingingCalls.isEmpty() ? null : mRingingCalls.get(0);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700142 }
143
144 /**
145 * Removes the specified call from the list of unanswered incoming calls and updates the ringer
Santos Cordonb64c1502014-05-21 21:21:49 -0700146 * based on the new state of {@link #mRingingCalls}. Safe to call with a call that is not
Sailesh Nepale59bb192014-04-01 18:33:59 -0700147 * present in the list of incoming calls.
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700148 */
Sailesh Nepale59bb192014-04-01 18:33:59 -0700149 private void removeFromUnansweredCall(Call call) {
Santos Cordonb64c1502014-05-21 21:21:49 -0700150 mRingingCalls.remove(call);
Santos Cordon40f78c22014-04-07 02:11:42 -0700151 updateRinging();
152 }
153
154 private void updateRinging() {
Santos Cordonb64c1502014-05-21 21:21:49 -0700155 if (mRingingCalls.isEmpty()) {
Santos Cordon40f78c22014-04-07 02:11:42 -0700156 stopRinging();
157 stopCallWaiting();
158 } else {
159 startRingingOrCallWaiting();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700160 }
161 }
162
Santos Cordon40f78c22014-04-07 02:11:42 -0700163 private void startRingingOrCallWaiting() {
164 Call foregroundCall = mCallsManager.getForegroundCall();
165 Log.v(this, "startRingingOrCallWaiting, foregroundCall: %s.", foregroundCall);
Evan Charlton198fde82014-04-07 10:53:11 -0700166
Santos Cordonb64c1502014-05-21 21:21:49 -0700167 if (mRingingCalls.contains(foregroundCall)) {
Santos Cordon40f78c22014-04-07 02:11:42 -0700168 // The foreground call is one of incoming calls so play the ringer out loud.
169 stopCallWaiting();
170
171 AudioManager audioManager =
172 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
173 if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) {
174 Log.v(this, "startRingingOrCallWaiting");
175 mCallAudioManager.setIsRinging(true);
176
Santos Cordonc7e85d42014-05-22 02:51:48 -0700177 // Only play ringtone if a bluetooth device is not available. When a BT device
178 // is available, then we send it a signal to do its own ringtone and we dont need
179 // to play the ringtone on the device.
180 if (!mCallAudioManager.isBluetoothDeviceAvailable()) {
Santos Cordon5ba7f272014-05-28 13:59:49 -0700181 // Because we wait until a contact info query to complete before processing a
182 // call (for the purposes of direct-to-voicemail), the information about custom
183 // ringtones should be available by the time this code executes. We can safely
184 // request the custom ringtone from the call and expect it to be current.
185 mRingtonePlayer.play(foregroundCall.getRingtone());
Santos Cordonc7e85d42014-05-22 02:51:48 -0700186 }
Santos Cordon40f78c22014-04-07 02:11:42 -0700187 } else {
188 Log.v(this, "startRingingOrCallWaiting, skipping because volume is 0");
189 }
Evan Charlton2186f7f2014-04-10 12:00:37 -0700190
191 if (shouldVibrate(TelecommApp.getInstance()) && !mIsVibrating) {
192 mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,
193 AudioManager.STREAM_RING);
194 mIsVibrating = true;
195 }
Santos Cordon40f78c22014-04-07 02:11:42 -0700196 } else {
197 Log.v(this, "Playing call-waiting tone.");
198
199 // All incoming calls are in background so play call waiting.
200 stopRinging();
201
202 if (mCallWaitingPlayer == null) {
203 mCallWaitingPlayer =
204 mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);
205 mCallWaitingPlayer.startTone();
206 }
Evan Charlton198fde82014-04-07 10:53:11 -0700207 }
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700208 }
209
210 private void stopRinging() {
211 Log.v(this, "stopRinging");
Santos Cordon40f78c22014-04-07 02:11:42 -0700212
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700213 mRingtonePlayer.stop();
Evan Charlton198fde82014-04-07 10:53:11 -0700214
215 if (mIsVibrating) {
216 mVibrator.cancel();
217 mIsVibrating = false;
218 }
Santos Cordon40f78c22014-04-07 02:11:42 -0700219
220 // Even though stop is asynchronous it's ok to update the audio manager. Things like audio
221 // focus are voluntary so releasing focus too early is not detrimental.
222 mCallAudioManager.setIsRinging(false);
223 }
224
225 private void stopCallWaiting() {
226 Log.v(this, "stop call waiting.");
227 if (mCallWaitingPlayer != null) {
228 mCallWaitingPlayer.stopTone();
229 mCallWaitingPlayer = null;
230 }
Evan Charlton198fde82014-04-07 10:53:11 -0700231 }
232
233 private boolean shouldVibrate(Context context) {
234 AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
235 int ringerMode = audioManager.getRingerMode();
236 if (getVibrateWhenRinging(context)) {
237 return ringerMode != AudioManager.RINGER_MODE_SILENT;
238 } else {
239 return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
240 }
241 }
242
243 private boolean getVibrateWhenRinging(Context context) {
244 if (!mVibrator.hasVibrator()) {
245 return false;
246 }
247 return Settings.System.getInt(context.getContentResolver(),
248 Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700249 }
250}