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