1 /*
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 
17 package com.android.server.telecom;
18 
19 import android.app.Notification;
20 import android.app.NotificationManager;
21 import android.content.Context;
22 import android.media.AudioAttributes;
23 import android.media.AudioManager;
24 import android.net.Uri;
25 import android.os.Bundle;
26 import android.os.SystemVibrator;
27 import android.os.Vibrator;
28 import android.provider.Settings;
29 import android.telecom.CallState;
30 
31 import java.util.LinkedList;
32 import java.util.List;
33 
34 /**
35  * Controls the ringtone player.
36  */
37 final class Ringer extends CallsManagerListenerBase {
38     private static final long[] VIBRATION_PATTERN = new long[] {
39         0, // No delay before starting
40         1000, // How long to vibrate
41         1000, // How long to wait before vibrating again
42     };
43 
44     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
45             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
46             .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
47             .build();
48 
49     /** Indicate that we want the pattern to repeat at the step which turns on vibration. */
50     private static final int VIBRATION_PATTERN_REPEAT = 1;
51 
52     private final AsyncRingtonePlayer mRingtonePlayer;
53 
54     /**
55      * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
56      * calls and explicit ordering is useful for maintaining the proper state of the ringer.
57      */
58     private final List<Call> mRingingCalls = new LinkedList<>();
59 
60     private final CallAudioManager mCallAudioManager;
61     private final CallsManager mCallsManager;
62     private final InCallTonePlayer.Factory mPlayerFactory;
63     private final Context mContext;
64     private final Vibrator mVibrator;
65 
66     private InCallTonePlayer mCallWaitingPlayer;
67 
68     /**
69      * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
70      */
71     private boolean mIsVibrating = false;
72 
73     /** Initializes the Ringer. */
Ringer( CallAudioManager callAudioManager, CallsManager callsManager, InCallTonePlayer.Factory playerFactory, Context context)74     Ringer(
75             CallAudioManager callAudioManager,
76             CallsManager callsManager,
77             InCallTonePlayer.Factory playerFactory,
78             Context context) {
79 
80         mCallAudioManager = callAudioManager;
81         mCallsManager = callsManager;
82         mPlayerFactory = playerFactory;
83         mContext = context;
84         // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
85         // vibrator object will be isolated from others.
86         mVibrator = new SystemVibrator(context);
87         mRingtonePlayer = new AsyncRingtonePlayer(context);
88     }
89 
90     @Override
onCallAdded(final Call call)91     public void onCallAdded(final Call call) {
92         if (call.isIncoming() && call.getState() == CallState.RINGING) {
93             if (mRingingCalls.contains(call)) {
94                 Log.wtf(this, "New ringing call is already in list of unanswered calls");
95             }
96             mRingingCalls.add(call);
97             updateRinging();
98         }
99     }
100 
101     @Override
onCallRemoved(Call call)102     public void onCallRemoved(Call call) {
103         removeFromUnansweredCall(call);
104     }
105 
106     @Override
onCallStateChanged(Call call, int oldState, int newState)107     public void onCallStateChanged(Call call, int oldState, int newState) {
108         if (newState != CallState.RINGING) {
109             removeFromUnansweredCall(call);
110         }
111     }
112 
113     @Override
onIncomingCallAnswered(Call call)114     public void onIncomingCallAnswered(Call call) {
115         onRespondedToIncomingCall(call);
116     }
117 
118     @Override
onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage)119     public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage) {
120         onRespondedToIncomingCall(call);
121     }
122 
123     @Override
onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall)124     public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) {
125         if (mRingingCalls.contains(oldForegroundCall) ||
126                 mRingingCalls.contains(newForegroundCall)) {
127             updateRinging();
128         }
129     }
130 
131     /**
132      * Silences the ringer for any actively ringing calls.
133      */
silence()134     void silence() {
135         // Remove all calls from the "ringing" set and then update the ringer.
136         mRingingCalls.clear();
137         updateRinging();
138     }
139 
onRespondedToIncomingCall(Call call)140     private void onRespondedToIncomingCall(Call call) {
141         // Only stop the ringer if this call is the top-most incoming call.
142         if (getTopMostUnansweredCall() == call) {
143             removeFromUnansweredCall(call);
144         }
145     }
146 
getTopMostUnansweredCall()147     private Call getTopMostUnansweredCall() {
148         return mRingingCalls.isEmpty() ? null : mRingingCalls.get(0);
149     }
150 
151     /**
152      * Removes the specified call from the list of unanswered incoming calls and updates the ringer
153      * based on the new state of {@link #mRingingCalls}. Safe to call with a call that is not
154      * present in the list of incoming calls.
155      */
removeFromUnansweredCall(Call call)156     private void removeFromUnansweredCall(Call call) {
157         mRingingCalls.remove(call);
158         updateRinging();
159     }
160 
updateRinging()161     private void updateRinging() {
162         if (mRingingCalls.isEmpty()) {
163             stopRinging();
164             stopCallWaiting();
165         } else {
166             startRingingOrCallWaiting();
167         }
168     }
169 
startRingingOrCallWaiting()170     private void startRingingOrCallWaiting() {
171         Call foregroundCall = mCallsManager.getForegroundCall();
172         Log.v(this, "startRingingOrCallWaiting, foregroundCall: %s.", foregroundCall);
173 
174         if (mRingingCalls.contains(foregroundCall)) {
175             // The foreground call is one of incoming calls so play the ringer out loud.
176             stopCallWaiting();
177 
178             if (!shouldRingForContact(foregroundCall.getContactUri())) {
179                 return;
180             }
181 
182             AudioManager audioManager =
183                     (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
184             if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) {
185                 Log.v(this, "startRingingOrCallWaiting");
186                 mCallAudioManager.setIsRinging(true);
187 
188                 // Because we wait until a contact info query to complete before processing a
189                 // call (for the purposes of direct-to-voicemail), the information about custom
190                 // ringtones should be available by the time this code executes. We can safely
191                 // request the custom ringtone from the call and expect it to be current.
192                 mRingtonePlayer.play(foregroundCall.getRingtone());
193             } else {
194                 Log.v(this, "startRingingOrCallWaiting, skipping because volume is 0");
195             }
196 
197             if (shouldVibrate(mContext) && !mIsVibrating) {
198                 mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,
199                         VIBRATION_ATTRIBUTES);
200                 mIsVibrating = true;
201             }
202         } else if (foregroundCall != null) {
203             // The first incoming call added to Telecom is not a foreground call at this point
204             // in time. If the current foreground call is null at point, don't play call-waiting
205             // as the call will eventually be promoted to the foreground call and play the
206             // ring tone.
207             Log.v(this, "Playing call-waiting tone.");
208 
209             // All incoming calls are in background so play call waiting.
210             stopRinging();
211 
212             if (mCallWaitingPlayer == null) {
213                 mCallWaitingPlayer =
214                         mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);
215                 mCallWaitingPlayer.startTone();
216             }
217         }
218     }
219 
shouldRingForContact(Uri contactUri)220     private boolean shouldRingForContact(Uri contactUri) {
221         final NotificationManager manager =
222                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
223         final Bundle extras = new Bundle();
224         if (contactUri != null) {
225             extras.putStringArray(Notification.EXTRA_PEOPLE, new String[] {contactUri.toString()});
226         }
227         return manager.matchesCallFilter(extras);
228     }
229 
stopRinging()230     private void stopRinging() {
231         Log.v(this, "stopRinging");
232 
233         mRingtonePlayer.stop();
234 
235         if (mIsVibrating) {
236             mVibrator.cancel();
237             mIsVibrating = false;
238         }
239 
240         // Even though stop is asynchronous it's ok to update the audio manager. Things like audio
241         // focus are voluntary so releasing focus too early is not detrimental.
242         mCallAudioManager.setIsRinging(false);
243     }
244 
stopCallWaiting()245     private void stopCallWaiting() {
246         Log.v(this, "stop call waiting.");
247         if (mCallWaitingPlayer != null) {
248             mCallWaitingPlayer.stopTone();
249             mCallWaitingPlayer = null;
250         }
251     }
252 
shouldVibrate(Context context)253     private boolean shouldVibrate(Context context) {
254         AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
255         int ringerMode = audioManager.getRingerModeInternal();
256         if (getVibrateWhenRinging(context)) {
257             return ringerMode != AudioManager.RINGER_MODE_SILENT;
258         } else {
259             return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
260         }
261     }
262 
getVibrateWhenRinging(Context context)263     private boolean getVibrateWhenRinging(Context context) {
264         if (!mVibrator.hasVibrator()) {
265             return false;
266         }
267         return Settings.System.getInt(context.getContentResolver(),
268                 Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
269     }
270 }
271