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.media.VolumeShaper;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.os.Vibrator;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 import com.android.server.telecom.LogUtils.EventTimer;
36 
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.List;
40 import java.util.concurrent.CompletableFuture;
41 import java.util.concurrent.ExecutionException;
42 import java.util.stream.Collectors;
43 
44 /**
45  * Controls the ringtone player.
46  */
47 @VisibleForTesting
48 public class Ringer {
49     public static class VibrationEffectProxy {
createWaveform(long[] timings, int[] amplitudes, int repeat)50         public VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
51             return VibrationEffect.createWaveform(timings, amplitudes, repeat);
52         }
53 
get(Uri ringtoneUri, Context context)54         public VibrationEffect get(Uri ringtoneUri, Context context) {
55             return VibrationEffect.get(ringtoneUri, context);
56         }
57     }
58     @VisibleForTesting
59     public VibrationEffect mDefaultVibrationEffect;
60 
61     private static final long[] PULSE_PRIMING_PATTERN = {0,12,250,12,500}; // priming  + interval
62 
63     private static final int[] PULSE_PRIMING_AMPLITUDE = {0,255,0,255,0};  // priming  + interval
64 
65     // ease-in + peak + pause
66     private static final long[] PULSE_RAMPING_PATTERN = {
67         50,50,50,50,50,50,50,50,50,50,50,50,50,50,300,1000};
68 
69     // ease-in (min amplitude = 30%) + peak + pause
70     private static final int[] PULSE_RAMPING_AMPLITUDE = {
71         77,77,78,79,81,84,87,93,101,114,133,162,205,255,255,0};
72 
73     private static final long[] PULSE_PATTERN;
74 
75     private static final int[] PULSE_AMPLITUDE;
76 
77     private static final int RAMPING_RINGER_VIBRATION_DURATION = 5000;
78     private static final int RAMPING_RINGER_DURATION = 10000;
79 
80     static {
81         // construct complete pulse pattern
82         PULSE_PATTERN = new long[PULSE_PRIMING_PATTERN.length + PULSE_RAMPING_PATTERN.length];
System.arraycopy( PULSE_PRIMING_PATTERN, 0, PULSE_PATTERN, 0, PULSE_PRIMING_PATTERN.length)83         System.arraycopy(
84             PULSE_PRIMING_PATTERN, 0, PULSE_PATTERN, 0, PULSE_PRIMING_PATTERN.length);
System.arraycopy(PULSE_RAMPING_PATTERN, 0, PULSE_PATTERN, PULSE_PRIMING_PATTERN.length, PULSE_RAMPING_PATTERN.length)85         System.arraycopy(PULSE_RAMPING_PATTERN, 0, PULSE_PATTERN,
86             PULSE_PRIMING_PATTERN.length, PULSE_RAMPING_PATTERN.length);
87 
88         // construct complete pulse amplitude
89         PULSE_AMPLITUDE = new int[PULSE_PRIMING_AMPLITUDE.length + PULSE_RAMPING_AMPLITUDE.length];
System.arraycopy( PULSE_PRIMING_AMPLITUDE, 0, PULSE_AMPLITUDE, 0, PULSE_PRIMING_AMPLITUDE.length)90         System.arraycopy(
91             PULSE_PRIMING_AMPLITUDE, 0, PULSE_AMPLITUDE, 0, PULSE_PRIMING_AMPLITUDE.length);
System.arraycopy(PULSE_RAMPING_AMPLITUDE, 0, PULSE_AMPLITUDE, PULSE_PRIMING_AMPLITUDE.length, PULSE_RAMPING_AMPLITUDE.length)92         System.arraycopy(PULSE_RAMPING_AMPLITUDE, 0, PULSE_AMPLITUDE,
93             PULSE_PRIMING_AMPLITUDE.length, PULSE_RAMPING_AMPLITUDE.length);
94     }
95 
96     private static final long[] SIMPLE_VIBRATION_PATTERN = {
97             0, // No delay before starting
98             1000, // How long to vibrate
99             1000, // How long to wait before vibrating again
100     };
101 
102     private static final int[] SIMPLE_VIBRATION_AMPLITUDE = {
103             0, // No delay before starting
104             255, // Vibrate full amplitude
105             0, // No amplitude while waiting
106     };
107 
108     /**
109      * Indicates that vibration should be repeated at element 5 in the {@link #PULSE_AMPLITUDE} and
110      * {@link #PULSE_PATTERN} arrays.  This means repetition will happen for the main ease-in/peak
111      * pattern, but the priming + interval part will not be repeated.
112      */
113     private static final int REPEAT_VIBRATION_AT = 5;
114 
115     private static final int REPEAT_SIMPLE_VIBRATION_AT = 1;
116 
117     private static final float EPSILON = 1e-6f;
118 
119     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
120             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
121             .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
122             .build();
123 
124     private static VibrationEffect mRampingRingerVibrationEffect;
125     private static VolumeShaper.Configuration mVolumeShaperConfig;
126 
127     /**
128      * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
129      * calls and explicit ordering is useful for maintaining the proper state of the ringer.
130      */
131 
132     private final SystemSettingsUtil mSystemSettingsUtil;
133     private final InCallTonePlayer.Factory mPlayerFactory;
134     private final AsyncRingtonePlayer mRingtonePlayer;
135     private final Context mContext;
136     private final Vibrator mVibrator;
137     private final InCallController mInCallController;
138     private final VibrationEffectProxy mVibrationEffectProxy;
139     private final boolean mIsHapticPlaybackSupportedByDevice;
140     /**
141      * For unit testing purposes only; when set, {@link #startRinging(Call, boolean)} will complete
142      * the future provided by the test using {@link #setBlockOnRingingFuture(CompletableFuture)}.
143      */
144     private CompletableFuture<Void> mBlockOnRingingFuture = null;
145 
146     private CompletableFuture<Void> mVibrateFuture = CompletableFuture.completedFuture(null);
147 
148     private InCallTonePlayer mCallWaitingPlayer;
149     private RingtoneFactory mRingtoneFactory;
150 
151     /**
152      * Call objects that are ringing, vibrating or call-waiting. These are used only for logging
153      * purposes.
154      */
155     private Call mRingingCall;
156     private Call mVibratingCall;
157     private Call mCallWaitingCall;
158 
159     /**
160      * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
161      */
162     private boolean mIsVibrating = false;
163 
164     /** Initializes the Ringer. */
165     @VisibleForTesting
Ringer( InCallTonePlayer.Factory playerFactory, Context context, SystemSettingsUtil systemSettingsUtil, AsyncRingtonePlayer asyncRingtonePlayer, RingtoneFactory ringtoneFactory, Vibrator vibrator, VibrationEffectProxy vibrationEffectProxy, InCallController inCallController)166     public Ringer(
167             InCallTonePlayer.Factory playerFactory,
168             Context context,
169             SystemSettingsUtil systemSettingsUtil,
170             AsyncRingtonePlayer asyncRingtonePlayer,
171             RingtoneFactory ringtoneFactory,
172             Vibrator vibrator,
173             VibrationEffectProxy vibrationEffectProxy,
174             InCallController inCallController) {
175 
176         mSystemSettingsUtil = systemSettingsUtil;
177         mPlayerFactory = playerFactory;
178         mContext = context;
179         // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
180         // vibrator object will be isolated from others.
181         mVibrator = vibrator;
182         mRingtonePlayer = asyncRingtonePlayer;
183         mRingtoneFactory = ringtoneFactory;
184         mInCallController = inCallController;
185         mVibrationEffectProxy = vibrationEffectProxy;
186 
187         if (mContext.getResources().getBoolean(R.bool.use_simple_vibration_pattern)) {
188             mDefaultVibrationEffect = mVibrationEffectProxy.createWaveform(SIMPLE_VIBRATION_PATTERN,
189                     SIMPLE_VIBRATION_AMPLITUDE, REPEAT_SIMPLE_VIBRATION_AT);
190         } else {
191             mDefaultVibrationEffect = mVibrationEffectProxy.createWaveform(PULSE_PATTERN,
192                     PULSE_AMPLITUDE, REPEAT_VIBRATION_AT);
193         }
194 
195         mIsHapticPlaybackSupportedByDevice =
196                 mSystemSettingsUtil.isHapticPlaybackSupported(mContext);
197     }
198 
199     @VisibleForTesting
setBlockOnRingingFuture(CompletableFuture<Void> future)200     public void setBlockOnRingingFuture(CompletableFuture<Void> future) {
201         mBlockOnRingingFuture = future;
202     }
203 
startRinging(Call foregroundCall, boolean isHfpDeviceAttached)204     public boolean startRinging(Call foregroundCall, boolean isHfpDeviceAttached) {
205         if (foregroundCall == null) {
206             Log.wtf(this, "startRinging called with null foreground call.");
207             return false;
208         }
209 
210         if (foregroundCall.getState() != CallState.RINGING
211                 && foregroundCall.getState() != CallState.SIMULATED_RINGING) {
212             // Its possible for bluetooth to connect JUST as a call goes active, which would mean
213             // the call would start ringing again.
214             Log.i(this, "startRinging called for non-ringing foreground callid=%s",
215                     foregroundCall.getId());
216             return false;
217         }
218 
219         AudioManager audioManager =
220                 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
221         LogUtils.EventTimer timer = new EventTimer();
222         boolean isVolumeOverZero = audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0;
223         timer.record("isVolumeOverZero");
224         boolean shouldRingForContact = shouldRingForContact(foregroundCall.getContactUri());
225         timer.record("shouldRingForContact");
226         boolean isRingtonePresent = !(mRingtoneFactory.getRingtone(foregroundCall) == null);
227         timer.record("getRingtone");
228         boolean isSelfManaged = foregroundCall.isSelfManaged();
229         timer.record("isSelfManaged");
230         boolean isSilentRingingRequested = foregroundCall.isSilentRingingRequested();
231         timer.record("isSilentRingRequested");
232 
233         boolean isRingerAudible = isVolumeOverZero && shouldRingForContact && isRingtonePresent;
234         timer.record("isRingerAudible");
235         boolean hasExternalRinger = hasExternalRinger(foregroundCall);
236         timer.record("hasExternalRinger");
237         // Don't do call waiting operations or vibration unless these are false.
238         boolean isTheaterModeOn = mSystemSettingsUtil.isTheaterModeOn(mContext);
239         timer.record("isTheaterModeOn");
240         boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging();
241         timer.record("letDialerHandleRinging");
242 
243         Log.i(this, "startRinging timings: " + timer);
244         boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged ||
245                 hasExternalRinger || isSilentRingingRequested;
246 
247         // Acquire audio focus under any of the following conditions:
248         // 1. Should ring for contact and there's an HFP device attached
249         // 2. Volume is over zero, we should ring for the contact, and there's a audible ringtone
250         //    present.
251         // 3. The call is self-managed.
252         boolean shouldAcquireAudioFocus =
253                 isRingerAudible || (isHfpDeviceAttached && shouldRingForContact) || isSelfManaged;
254 
255         if (endEarly) {
256             if (letDialerHandleRinging) {
257                 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Dialer handles");
258             }
259             if (isSilentRingingRequested) {
260                 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Silent ringing "
261                         + "requested");
262             }
263             Log.i(this, "Ending early -- isTheaterModeOn=%s, letDialerHandleRinging=%s, " +
264                             "isSelfManaged=%s, hasExternalRinger=%s, silentRingingRequested=%s",
265                     isTheaterModeOn, letDialerHandleRinging, isSelfManaged, hasExternalRinger,
266                     isSilentRingingRequested);
267             if (mBlockOnRingingFuture != null) {
268                 mBlockOnRingingFuture.complete(null);
269             }
270             return shouldAcquireAudioFocus;
271         }
272 
273         stopCallWaiting();
274 
275         VibrationEffect effect;
276         CompletableFuture<Boolean> hapticsFuture = null;
277         // Determine if the settings and DND mode indicate that the vibrator can be used right now.
278         boolean isVibratorEnabled = isVibratorEnabled(mContext, foregroundCall);
279         if (isRingerAudible) {
280             mRingingCall = foregroundCall;
281             Log.addEvent(foregroundCall, LogUtils.Events.START_RINGER);
282             // Because we wait until a contact info query to complete before processing a
283             // call (for the purposes of direct-to-voicemail), the information about custom
284             // ringtones should be available by the time this code executes. We can safely
285             // request the custom ringtone from the call and expect it to be current.
286             if (mSystemSettingsUtil.applyRampingRinger(mContext)) {
287                 Log.i(this, "start ramping ringer.");
288                 if (mSystemSettingsUtil.enableAudioCoupledVibrationForRampingRinger()) {
289                     effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall);
290                 } else {
291                     effect = mDefaultVibrationEffect;
292                 }
293                 if (mVolumeShaperConfig == null) {
294                     float silencePoint = (float) (RAMPING_RINGER_VIBRATION_DURATION)
295                         / (float) (RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION);
296                     mVolumeShaperConfig = new VolumeShaper.Configuration.Builder()
297                         .setDuration(RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION)
298                         .setCurve(new float[] {0.f, silencePoint + EPSILON /*keep monotonicity*/,
299                             1.f}, new float[] {0.f, 0.f, 1.f})
300                         .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
301                         .build();
302                 }
303                 hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall,
304                         mVolumeShaperConfig, isVibratorEnabled);
305             } else {
306                 // Ramping ringtone is not enabled.
307                 hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, null,
308                         isVibratorEnabled);
309                 effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall);
310             }
311         } else {
312             String reason = String.format(
313                     "isVolumeOverZero=%s, shouldRingForContact=%s, isRingtonePresent=%s",
314                     isVolumeOverZero, shouldRingForContact, isRingtonePresent);
315             Log.i(this, "startRinging: skipping because ringer would not be audible. " + reason);
316             Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Inaudible: " + reason);
317             effect = mDefaultVibrationEffect;
318         }
319 
320         if (hapticsFuture != null) {
321             mVibrateFuture = hapticsFuture.thenAccept(isUsingAudioCoupledHaptics -> {
322                 if (!isUsingAudioCoupledHaptics || !mIsHapticPlaybackSupportedByDevice) {
323                     Log.i(this, "startRinging: fileHasHaptics=%b, hapticsSupported=%b",
324                             isUsingAudioCoupledHaptics, mIsHapticPlaybackSupportedByDevice);
325                     maybeStartVibration(foregroundCall, shouldRingForContact, effect,
326                             isVibratorEnabled, isRingerAudible);
327                 } else if (mSystemSettingsUtil.applyRampingRinger(mContext)
328                            && !mSystemSettingsUtil.enableAudioCoupledVibrationForRampingRinger()) {
329                     Log.i(this, "startRinging: apply ramping ringer vibration");
330                     maybeStartVibration(foregroundCall, shouldRingForContact, effect,
331                             isVibratorEnabled, isRingerAudible);
332                 } else {
333                     Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION,
334                             "using audio-coupled haptics");
335                 }
336             });
337             if (mBlockOnRingingFuture != null) {
338                 mVibrateFuture.whenComplete((v, e) -> mBlockOnRingingFuture.complete(null));
339             }
340         } else {
341             if (mBlockOnRingingFuture != null) {
342                 mBlockOnRingingFuture.complete(null);
343             }
344             Log.w(this, "startRinging: No haptics future; fallback to default behavior");
345             maybeStartVibration(foregroundCall, shouldRingForContact, effect, isVibratorEnabled,
346                     isRingerAudible);
347         }
348 
349         return shouldAcquireAudioFocus;
350     }
351 
maybeStartVibration(Call foregroundCall, boolean shouldRingForContact, VibrationEffect effect, boolean isVibrationEnabled, boolean isRingerAudible)352     private void maybeStartVibration(Call foregroundCall, boolean shouldRingForContact,
353         VibrationEffect effect, boolean isVibrationEnabled, boolean isRingerAudible) {
354         if (isVibrationEnabled
355                 && !mIsVibrating && shouldRingForContact) {
356             if (mSystemSettingsUtil.applyRampingRinger(mContext)
357                     && isRingerAudible) {
358                 Log.i(this, "start vibration for ramping ringer.");
359                 mIsVibrating = true;
360                 mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
361             } else {
362                 Log.i(this, "start normal vibration.");
363                 mIsVibrating = true;
364                 mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
365             }
366         } else if (mIsVibrating) {
367             Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, "already vibrating");
368         }
369     }
370 
getVibrationEffectForCall(RingtoneFactory factory, Call call)371     private VibrationEffect getVibrationEffectForCall(RingtoneFactory factory, Call call) {
372         VibrationEffect effect = null;
373         Ringtone ringtone = factory.getRingtone(call);
374         Uri ringtoneUri = ringtone != null ? ringtone.getUri() : null;
375         if (ringtoneUri != null) {
376             try {
377                 effect = mVibrationEffectProxy.get(ringtoneUri, mContext);
378             } catch (IllegalArgumentException iae) {
379                 // Deep in the bowels of the VibrationEffect class it is possible for an
380                 // IllegalArgumentException to be thrown if there is an invalid URI specified in the
381                 // device config, or a content provider failure.  Rather than crashing the Telecom
382                 // process we will just use the default vibration effect.
383                 Log.e(this, iae, "getVibrationEffectForCall: failed to get vibration effect");
384                 effect = null;
385             }
386         }
387 
388         if (effect == null) {
389             effect = mDefaultVibrationEffect;
390         }
391         return effect;
392     }
393 
startCallWaiting(Call call)394     public void startCallWaiting(Call call) {
395         startCallWaiting(call, null);
396     }
397 
startCallWaiting(Call call, String reason)398     public void startCallWaiting(Call call, String reason) {
399         if (mSystemSettingsUtil.isTheaterModeOn(mContext)) {
400             return;
401         }
402 
403         if (mInCallController.doesConnectedDialerSupportRinging()) {
404             Log.addEvent(call, LogUtils.Events.SKIP_RINGING, "Dialer handles");
405             return;
406         }
407 
408         if (call.isSelfManaged()) {
409             Log.addEvent(call, LogUtils.Events.SKIP_RINGING, "Self-managed");
410             return;
411         }
412 
413         Log.v(this, "Playing call-waiting tone.");
414 
415         stopRinging();
416 
417         if (mCallWaitingPlayer == null) {
418             Log.addEvent(call, LogUtils.Events.START_CALL_WAITING_TONE, reason);
419             mCallWaitingCall = call;
420             mCallWaitingPlayer =
421                     mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);
422             mCallWaitingPlayer.startTone();
423         }
424     }
425 
stopRinging()426     public void stopRinging() {
427         if (mRingingCall != null) {
428             Log.addEvent(mRingingCall, LogUtils.Events.STOP_RINGER);
429             mRingingCall = null;
430         }
431 
432         mRingtonePlayer.stop();
433 
434         // If we haven't started vibrating because we were waiting for the haptics info, cancel
435         // it and don't vibrate at all.
436         if (mVibrateFuture != null) {
437             mVibrateFuture.cancel(true);
438         }
439 
440         if (mIsVibrating) {
441             Log.addEvent(mVibratingCall, LogUtils.Events.STOP_VIBRATOR);
442             mVibrator.cancel();
443             mIsVibrating = false;
444             mVibratingCall = null;
445         }
446     }
447 
stopCallWaiting()448     public void stopCallWaiting() {
449         Log.v(this, "stop call waiting.");
450         if (mCallWaitingPlayer != null) {
451             if (mCallWaitingCall != null) {
452                 Log.addEvent(mCallWaitingCall, LogUtils.Events.STOP_CALL_WAITING_TONE);
453                 mCallWaitingCall = null;
454             }
455 
456             mCallWaitingPlayer.stopTone();
457             mCallWaitingPlayer = null;
458         }
459     }
460 
isRinging()461     public boolean isRinging() {
462         return mRingtonePlayer.isPlaying();
463     }
464 
shouldRingForContact(Uri contactUri)465     private boolean shouldRingForContact(Uri contactUri) {
466         final NotificationManager manager =
467                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
468         final Bundle peopleExtras = new Bundle();
469         if (contactUri != null) {
470             ArrayList<Person> personList = new ArrayList<>();
471             personList.add(new Person.Builder().setUri(contactUri.toString()).build());
472             peopleExtras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, personList);
473         }
474         return manager.matchesCallFilter(peopleExtras);
475     }
476 
hasExternalRinger(Call foregroundCall)477     private boolean hasExternalRinger(Call foregroundCall) {
478         Bundle intentExtras = foregroundCall.getIntentExtras();
479         if (intentExtras != null) {
480             return intentExtras.getBoolean(TelecomManager.EXTRA_CALL_EXTERNAL_RINGER, false);
481         } else {
482             return false;
483         }
484     }
485 
isVibratorEnabled(Context context, Call call)486     private boolean isVibratorEnabled(Context context, Call call) {
487         AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
488         int ringerMode = audioManager.getRingerModeInternal();
489         boolean shouldVibrate;
490         if (getVibrateWhenRinging(context)) {
491             shouldVibrate = ringerMode != AudioManager.RINGER_MODE_SILENT;
492         } else {
493             shouldVibrate = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
494         }
495 
496         // Technically this should be in the calling method, but it seemed a little odd to pass
497         // around a whole bunch of state just for logging purposes.
498         if (shouldVibrate) {
499             Log.addEvent(call, LogUtils.Events.START_VIBRATOR,
500                     "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b",
501                     mVibrator.hasVibrator(), mSystemSettingsUtil.canVibrateWhenRinging(context),
502                     ringerMode, mIsVibrating);
503         } else {
504             Log.addEvent(call, LogUtils.Events.SKIP_VIBRATION,
505                     "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b",
506                     mVibrator.hasVibrator(), mSystemSettingsUtil.canVibrateWhenRinging(context),
507                     ringerMode, mIsVibrating);
508         }
509 
510         return shouldVibrate;
511     }
512 
getVibrateWhenRinging(Context context)513     private boolean getVibrateWhenRinging(Context context) {
514         if (!mVibrator.hasVibrator()) {
515             return false;
516         }
517         return mSystemSettingsUtil.canVibrateWhenRinging(context)
518             || mSystemSettingsUtil.applyRampingRinger(context);
519     }
520 }
521