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