• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.app.Person;
22 import android.content.Context;
23 import android.os.VibrationEffect;
24 import android.telecom.Log;
25 import android.telecom.TelecomManager;
26 import android.media.AudioAttributes;
27 import android.media.AudioManager;
28 import android.media.Ringtone;
29 import android.net.Uri;
30 import android.os.Bundle;
31 import android.os.Vibrator;
32 
33 import com.android.internal.annotations.VisibleForTesting;
34 
35 import java.util.ArrayList;
36 
37 /**
38  * Controls the ringtone player.
39  */
40 @VisibleForTesting
41 public class Ringer {
42     @VisibleForTesting
43     public VibrationEffect mDefaultVibrationEffect;
44 
45     private static final long[] PULSE_PATTERN = {0,12,250,12,500, // priming  + interval
46             50,50,50,50,50,50,50,50,50,50,50,50,50,50, // ease-in
47             300, // Peak
48             1000}; // pause before repetition
49 
50     private static final int[] PULSE_AMPLITUDE = {0,255,0,255,0, // priming  + interval
51             77,77,78,79,81,84,87,93,101,114,133,162,205,255, // ease-in (min amplitude = 30%)
52             255, // Peak
53             0}; // pause before repetition
54 
55     private static final long[] SIMPLE_VIBRATION_PATTERN = {
56             0, // No delay before starting
57             1000, // How long to vibrate
58             1000, // How long to wait before vibrating again
59     };
60 
61     private static final int[] SIMPLE_VIBRATION_AMPLITUDE = {
62             0, // No delay before starting
63             255, // Vibrate full amplitude
64             0, // No amplitude while waiting
65     };
66 
67     /**
68      * Indicates that vibration should be repeated at element 5 in the {@link #PULSE_AMPLITUDE} and
69      * {@link #PULSE_PATTERN} arrays.  This means repetition will happen for the main ease-in/peak
70      * pattern, but the priming + interval part will not be repeated.
71      */
72     private static final int REPEAT_VIBRATION_AT = 5;
73 
74     private static final int REPEAT_SIMPLE_VIBRATION_AT = 1;
75 
76     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
77             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
78             .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
79             .build();
80 
81     /**
82      * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
83      * calls and explicit ordering is useful for maintaining the proper state of the ringer.
84      */
85 
86     private final SystemSettingsUtil mSystemSettingsUtil;
87     private final InCallTonePlayer.Factory mPlayerFactory;
88     private final AsyncRingtonePlayer mRingtonePlayer;
89     private final Context mContext;
90     private final Vibrator mVibrator;
91     private final InCallController mInCallController;
92 
93     private InCallTonePlayer mCallWaitingPlayer;
94     private RingtoneFactory mRingtoneFactory;
95 
96     /**
97      * Call objects that are ringing, vibrating or call-waiting. These are used only for logging
98      * purposes.
99      */
100     private Call mRingingCall;
101     private Call mVibratingCall;
102     private Call mCallWaitingCall;
103 
104     /**
105      * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
106      */
107     private boolean mIsVibrating = false;
108 
109     /** Initializes the Ringer. */
110     @VisibleForTesting
Ringer( InCallTonePlayer.Factory playerFactory, Context context, SystemSettingsUtil systemSettingsUtil, AsyncRingtonePlayer asyncRingtonePlayer, RingtoneFactory ringtoneFactory, Vibrator vibrator, InCallController inCallController)111     public Ringer(
112             InCallTonePlayer.Factory playerFactory,
113             Context context,
114             SystemSettingsUtil systemSettingsUtil,
115             AsyncRingtonePlayer asyncRingtonePlayer,
116             RingtoneFactory ringtoneFactory,
117             Vibrator vibrator,
118             InCallController inCallController) {
119 
120         mSystemSettingsUtil = systemSettingsUtil;
121         mPlayerFactory = playerFactory;
122         mContext = context;
123         // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
124         // vibrator object will be isolated from others.
125         mVibrator = vibrator;
126         mRingtonePlayer = asyncRingtonePlayer;
127         mRingtoneFactory = ringtoneFactory;
128         mInCallController = inCallController;
129 
130         if (mContext.getResources().getBoolean(R.bool.use_simple_vibration_pattern)) {
131             mDefaultVibrationEffect = VibrationEffect.createWaveform(SIMPLE_VIBRATION_PATTERN,
132                     SIMPLE_VIBRATION_AMPLITUDE, REPEAT_SIMPLE_VIBRATION_AT);
133         } else {
134             mDefaultVibrationEffect = VibrationEffect.createWaveform(PULSE_PATTERN,
135                     PULSE_AMPLITUDE, REPEAT_VIBRATION_AT);
136         }
137     }
138 
startRinging(Call foregroundCall, boolean isHfpDeviceAttached)139     public boolean startRinging(Call foregroundCall, boolean isHfpDeviceAttached) {
140         if (foregroundCall == null) {
141             Log.wtf(this, "startRinging called with null foreground call.");
142             return false;
143         }
144 
145         AudioManager audioManager =
146                 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
147         boolean isVolumeOverZero = audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0;
148         boolean shouldRingForContact = shouldRingForContact(foregroundCall.getContactUri());
149         boolean isRingtonePresent = !(mRingtoneFactory.getRingtone(foregroundCall) == null);
150         boolean isSelfManaged = foregroundCall.isSelfManaged();
151 
152         boolean isRingerAudible = isVolumeOverZero && shouldRingForContact && isRingtonePresent;
153         boolean hasExternalRinger = hasExternalRinger(foregroundCall);
154         // Acquire audio focus under any of the following conditions:
155         // 1. Should ring for contact and there's an HFP device attached
156         // 2. Volume is over zero, we should ring for the contact, and there's a audible ringtone
157         //    present.
158         // 3. The call is self-managed.
159         boolean shouldAcquireAudioFocus =
160                 isRingerAudible || (isHfpDeviceAttached && shouldRingForContact) || isSelfManaged;
161 
162         // Don't do call waiting operations or vibration unless these are false.
163         boolean isTheaterModeOn = mSystemSettingsUtil.isTheaterModeOn(mContext);
164         boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging();
165         boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged ||
166                 hasExternalRinger;
167 
168         if (endEarly) {
169             if (letDialerHandleRinging) {
170                 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING);
171             }
172             Log.i(this, "Ending early -- isTheaterModeOn=%s, letDialerHandleRinging=%s, " +
173                     "isSelfManaged=%s, hasExternalRinger=%s", isTheaterModeOn,
174                     letDialerHandleRinging, isSelfManaged, hasExternalRinger);
175             return shouldAcquireAudioFocus;
176         }
177 
178         stopCallWaiting();
179 
180         VibrationEffect effect;
181         if (isRingerAudible) {
182             mRingingCall = foregroundCall;
183             Log.addEvent(foregroundCall, LogUtils.Events.START_RINGER);
184             // Because we wait until a contact info query to complete before processing a
185             // call (for the purposes of direct-to-voicemail), the information about custom
186             // ringtones should be available by the time this code executes. We can safely
187             // request the custom ringtone from the call and expect it to be current.
188             mRingtonePlayer.play(mRingtoneFactory, foregroundCall);
189             effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall);
190         } else {
191             Log.i(this, "startRinging: skipping because ringer would not be audible. " +
192                     "isVolumeOverZero=%s, shouldRingForContact=%s, isRingtonePresent=%s",
193                     isVolumeOverZero, shouldRingForContact, isRingtonePresent);
194             effect = mDefaultVibrationEffect;
195         }
196 
197         if (shouldVibrate(mContext, foregroundCall) && !mIsVibrating && shouldRingForContact) {
198             mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
199             mIsVibrating = true;
200         } else if (mIsVibrating) {
201             Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, "already vibrating");
202         }
203 
204         return shouldAcquireAudioFocus;
205     }
206 
getVibrationEffectForCall(RingtoneFactory factory, Call call)207     private VibrationEffect getVibrationEffectForCall(RingtoneFactory factory, Call call) {
208         VibrationEffect effect = null;
209         Ringtone ringtone = factory.getRingtone(call);
210         Uri ringtoneUri = ringtone != null ? ringtone.getUri() : null;
211         if (ringtoneUri != null) {
212             effect = VibrationEffect.get(ringtoneUri, mContext);
213         }
214 
215         if (effect == null) {
216             effect = mDefaultVibrationEffect;
217         }
218         return effect;
219     }
220 
startCallWaiting(Call call)221     public void startCallWaiting(Call call) {
222         if (mSystemSettingsUtil.isTheaterModeOn(mContext)) {
223             return;
224         }
225 
226         if (mInCallController.doesConnectedDialerSupportRinging()) {
227             Log.addEvent(call, LogUtils.Events.SKIP_RINGING);
228             return;
229         }
230 
231         if (call.isSelfManaged()) {
232             Log.addEvent(call, LogUtils.Events.SKIP_RINGING, "Self-managed");
233             return;
234         }
235 
236         Log.v(this, "Playing call-waiting tone.");
237 
238         stopRinging();
239 
240         if (mCallWaitingPlayer == null) {
241             Log.addEvent(call, LogUtils.Events.START_CALL_WAITING_TONE);
242             mCallWaitingCall = call;
243             mCallWaitingPlayer =
244                     mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);
245             mCallWaitingPlayer.startTone();
246         }
247     }
248 
stopRinging()249     public void stopRinging() {
250         if (mRingingCall != null) {
251             Log.addEvent(mRingingCall, LogUtils.Events.STOP_RINGER);
252             mRingingCall = null;
253         }
254 
255         mRingtonePlayer.stop();
256 
257         if (mIsVibrating) {
258             Log.addEvent(mVibratingCall, LogUtils.Events.STOP_VIBRATOR);
259             mVibrator.cancel();
260             mIsVibrating = false;
261             mVibratingCall = null;
262         }
263     }
264 
stopCallWaiting()265     public void stopCallWaiting() {
266         Log.v(this, "stop call waiting.");
267         if (mCallWaitingPlayer != null) {
268             if (mCallWaitingCall != null) {
269                 Log.addEvent(mCallWaitingCall, LogUtils.Events.STOP_CALL_WAITING_TONE);
270                 mCallWaitingCall = null;
271             }
272 
273             mCallWaitingPlayer.stopTone();
274             mCallWaitingPlayer = null;
275         }
276     }
277 
shouldRingForContact(Uri contactUri)278     private boolean shouldRingForContact(Uri contactUri) {
279         final NotificationManager manager =
280                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
281         final Bundle peopleExtras = new Bundle();
282         if (contactUri != null) {
283             ArrayList<Person> personList = new ArrayList<>();
284             personList.add(new Person.Builder().setUri(contactUri.toString()).build());
285             peopleExtras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, personList);
286         }
287         return manager.matchesCallFilter(peopleExtras);
288     }
289 
hasExternalRinger(Call foregroundCall)290     private boolean hasExternalRinger(Call foregroundCall) {
291         Bundle intentExtras = foregroundCall.getIntentExtras();
292         if (intentExtras != null) {
293             return intentExtras.getBoolean(TelecomManager.EXTRA_CALL_EXTERNAL_RINGER, false);
294         } else {
295             return false;
296         }
297     }
298 
shouldVibrate(Context context, Call call)299     private boolean shouldVibrate(Context context, Call call) {
300         AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
301         int ringerMode = audioManager.getRingerModeInternal();
302         boolean shouldVibrate;
303         if (getVibrateWhenRinging(context)) {
304             shouldVibrate = ringerMode != AudioManager.RINGER_MODE_SILENT;
305         } else {
306             shouldVibrate = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
307         }
308 
309         // Technically this should be in the calling method, but it seemed a little odd to pass
310         // around a whole bunch of state just for logging purposes.
311         if (shouldVibrate) {
312             Log.addEvent(call, LogUtils.Events.START_VIBRATOR,
313                     "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b",
314                     mVibrator.hasVibrator(), mSystemSettingsUtil.canVibrateWhenRinging(context),
315                     ringerMode, mIsVibrating);
316         } else {
317             Log.addEvent(call, LogUtils.Events.SKIP_VIBRATION,
318                     "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b",
319                     mVibrator.hasVibrator(), mSystemSettingsUtil.canVibrateWhenRinging(context),
320                     ringerMode, mIsVibrating);
321         }
322 
323         return shouldVibrate;
324     }
325 
getVibrateWhenRinging(Context context)326     private boolean getVibrateWhenRinging(Context context) {
327         if (!mVibrator.hasVibrator()) {
328             return false;
329         }
330         return mSystemSettingsUtil.canVibrateWhenRinging(context);
331     }
332 }
333