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