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 {
51         public VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
52             return VibrationEffect.createWaveform(timings, amplitudes, repeat);
53         }
54 
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];
84         System.arraycopy(
85             PULSE_PRIMING_PATTERN, 0, PULSE_PATTERN, 0, PULSE_PRIMING_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];
91         System.arraycopy(
92             PULSE_PRIMING_AMPLITUDE, 0, PULSE_AMPLITUDE, 0, PULSE_PRIMING_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
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
207     public void setBlockOnRingingFuture(CompletableFuture<Void> future) {
208         mBlockOnRingingFuture = future;
209     }
210 
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 
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 
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 
389     public void startCallWaiting(Call call) {
390         startCallWaiting(call, null);
391     }
392 
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 
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 
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 
456     public boolean isRinging() {
457         return mRingtonePlayer.isPlaying();
458     }
459 
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 
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 
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 
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 
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 
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