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