• 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.annotation.NonNull;
25 import android.app.Notification;
26 import android.app.NotificationManager;
27 import android.app.Person;
28 import android.content.Context;
29 import android.media.AudioManager;
30 import android.media.Ringtone;
31 import android.media.VolumeShaper;
32 import android.net.Uri;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.os.HandlerThread;
36 import android.os.UserHandle;
37 import android.os.UserManager;
38 import android.os.VibrationAttributes;
39 import android.os.VibrationEffect;
40 import android.os.Vibrator;
41 import android.telecom.Log;
42 import android.telecom.TelecomManager;
43 import android.view.accessibility.AccessibilityManager;
44 
45 import com.android.internal.annotations.VisibleForTesting;
46 import com.android.server.telecom.LogUtils.EventTimer;
47 
48 import java.util.ArrayList;
49 import java.util.concurrent.CompletableFuture;
50 import java.util.concurrent.CountDownLatch;
51 import java.util.concurrent.ExecutionException;
52 import java.util.concurrent.TimeUnit;
53 import java.util.concurrent.TimeoutException;
54 import java.util.function.BiConsumer;
55 import java.util.function.Supplier;
56 
57 /**
58  * Controls the ringtone player.
59  */
60 @VisibleForTesting
61 public class Ringer {
62     public interface AccessibilityManagerAdapter {
startFlashNotificationSequence(@onNull Context context, @AccessibilityManager.FlashNotificationReason int reason)63         boolean startFlashNotificationSequence(@NonNull Context context,
64                 @AccessibilityManager.FlashNotificationReason int reason);
stopFlashNotificationSequence(@onNull Context context)65         boolean stopFlashNotificationSequence(@NonNull Context context);
66     }
67     /**
68      * Flag only for local debugging. Do not submit enabled.
69      */
70     private static final boolean DEBUG_RINGER = false;
71 
72     public static class VibrationEffectProxy {
createWaveform(long[] timings, int[] amplitudes, int repeat)73         public VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
74             return VibrationEffect.createWaveform(timings, amplitudes, repeat);
75         }
76 
get(Uri ringtoneUri, Context context)77         public VibrationEffect get(Uri ringtoneUri, Context context) {
78             return VibrationEffect.get(ringtoneUri, context);
79         }
80     }
81     @VisibleForTesting
82     public VibrationEffect mDefaultVibrationEffect;
83 
84     // Used for test to notify the completion of RingerAttributes
85     private CountDownLatch mAttributesLatch;
86 
87     private static final long[] PULSE_PRIMING_PATTERN = {0,12,250,12,500}; // priming  + interval
88 
89     private static final int[] PULSE_PRIMING_AMPLITUDE = {0,255,0,255,0};  // priming  + interval
90 
91     // ease-in + peak + pause
92     private static final long[] PULSE_RAMPING_PATTERN = {
93         50,50,50,50,50,50,50,50,50,50,50,50,50,50,300,1000};
94 
95     // ease-in (min amplitude = 30%) + peak + pause
96     private static final int[] PULSE_RAMPING_AMPLITUDE = {
97         77,77,78,79,81,84,87,93,101,114,133,162,205,255,255,0};
98 
99     private static final long[] PULSE_PATTERN;
100 
101     private static final int[] PULSE_AMPLITUDE;
102 
103     private static final int RAMPING_RINGER_VIBRATION_DURATION = 5000;
104     private static final int RAMPING_RINGER_DURATION = 10000;
105 
106     static {
107         // construct complete pulse pattern
108         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)109         System.arraycopy(
110             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)111         System.arraycopy(PULSE_RAMPING_PATTERN, 0, PULSE_PATTERN,
112             PULSE_PRIMING_PATTERN.length, PULSE_RAMPING_PATTERN.length);
113 
114         // construct complete pulse amplitude
115         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)116         System.arraycopy(
117             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)118         System.arraycopy(PULSE_RAMPING_AMPLITUDE, 0, PULSE_AMPLITUDE,
119             PULSE_PRIMING_AMPLITUDE.length, PULSE_RAMPING_AMPLITUDE.length);
120     }
121 
122     private static final long[] SIMPLE_VIBRATION_PATTERN = {
123             0, // No delay before starting
124             1000, // How long to vibrate
125             1000, // How long to wait before vibrating again
126     };
127 
128     private static final int[] SIMPLE_VIBRATION_AMPLITUDE = {
129             0, // No delay before starting
130             255, // Vibrate full amplitude
131             0, // No amplitude while waiting
132     };
133 
134     /**
135      * Indicates that vibration should be repeated at element 5 in the {@link #PULSE_AMPLITUDE} and
136      * {@link #PULSE_PATTERN} arrays.  This means repetition will happen for the main ease-in/peak
137      * pattern, but the priming + interval part will not be repeated.
138      */
139     private static final int REPEAT_VIBRATION_AT = 5;
140 
141     private static final int REPEAT_SIMPLE_VIBRATION_AT = 1;
142 
143     private static final long RINGER_ATTRIBUTES_TIMEOUT = 5000; // 5 seconds
144 
145     private static final float EPSILON = 1e-6f;
146 
147     private static final VibrationAttributes VIBRATION_ATTRIBUTES =
148             new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_RINGTONE).build();
149 
150     private static VolumeShaper.Configuration mVolumeShaperConfig;
151 
152     /**
153      * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
154      * calls and explicit ordering is useful for maintaining the proper state of the ringer.
155      */
156 
157     private final SystemSettingsUtil mSystemSettingsUtil;
158     private final InCallTonePlayer.Factory mPlayerFactory;
159     private final AsyncRingtonePlayer mRingtonePlayer;
160     private final Context mContext;
161     private final Vibrator mVibrator;
162     private final InCallController mInCallController;
163     private final VibrationEffectProxy mVibrationEffectProxy;
164     private final boolean mIsHapticPlaybackSupportedByDevice;
165     /**
166      * For unit testing purposes only; when set, {@link #startRinging(Call, boolean)} will complete
167      * the future provided by the test using {@link #setBlockOnRingingFuture(CompletableFuture)}.
168      */
169     private CompletableFuture<Void> mBlockOnRingingFuture = null;
170 
171     private InCallTonePlayer mCallWaitingPlayer;
172     private RingtoneFactory mRingtoneFactory;
173     private AudioManager mAudioManager;
174     private NotificationManager mNotificationManager;
175     private AccessibilityManagerAdapter mAccessibilityManagerAdapter;
176 
177     /**
178      * Call objects that are ringing, vibrating or call-waiting. These are used only for logging
179      * purposes (except mVibratingCall is also used to ensure consistency).
180      */
181     private Call mRingingCall;
182     private Call mVibratingCall;
183     private Call mCallWaitingCall;
184 
185     /**
186      * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
187      */
188     private boolean mIsVibrating = false;
189 
190     private Handler mHandler = null;
191 
192     /**
193      * Use lock different from the Telecom sync because ringing process is asynchronous outside that
194      * lock
195      */
196     private final Object mLock;
197 
198     /** Initializes the Ringer. */
199     @VisibleForTesting
Ringer( InCallTonePlayer.Factory playerFactory, Context context, SystemSettingsUtil systemSettingsUtil, AsyncRingtonePlayer asyncRingtonePlayer, RingtoneFactory ringtoneFactory, Vibrator vibrator, VibrationEffectProxy vibrationEffectProxy, InCallController inCallController, NotificationManager notificationManager, AccessibilityManagerAdapter accessibilityManagerAdapter)200     public Ringer(
201             InCallTonePlayer.Factory playerFactory,
202             Context context,
203             SystemSettingsUtil systemSettingsUtil,
204             AsyncRingtonePlayer asyncRingtonePlayer,
205             RingtoneFactory ringtoneFactory,
206             Vibrator vibrator,
207             VibrationEffectProxy vibrationEffectProxy,
208             InCallController inCallController,
209             NotificationManager notificationManager,
210             AccessibilityManagerAdapter accessibilityManagerAdapter) {
211 
212         mLock = new Object();
213         mSystemSettingsUtil = systemSettingsUtil;
214         mPlayerFactory = playerFactory;
215         mContext = context;
216         // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
217         // vibrator object will be isolated from others.
218         mVibrator = vibrator;
219         mRingtonePlayer = asyncRingtonePlayer;
220         mRingtoneFactory = ringtoneFactory;
221         mInCallController = inCallController;
222         mVibrationEffectProxy = vibrationEffectProxy;
223         mNotificationManager = notificationManager;
224         mAccessibilityManagerAdapter = accessibilityManagerAdapter;
225 
226         if (mContext.getResources().getBoolean(R.bool.use_simple_vibration_pattern)) {
227             mDefaultVibrationEffect = mVibrationEffectProxy.createWaveform(SIMPLE_VIBRATION_PATTERN,
228                     SIMPLE_VIBRATION_AMPLITUDE, REPEAT_SIMPLE_VIBRATION_AT);
229         } else {
230             mDefaultVibrationEffect = mVibrationEffectProxy.createWaveform(PULSE_PATTERN,
231                     PULSE_AMPLITUDE, REPEAT_VIBRATION_AT);
232         }
233 
234         mIsHapticPlaybackSupportedByDevice =
235                 mSystemSettingsUtil.isHapticPlaybackSupported(mContext);
236 
237         mAudioManager = mContext.getSystemService(AudioManager.class);
238     }
239 
240     @VisibleForTesting
setBlockOnRingingFuture(CompletableFuture<Void> future)241     public void setBlockOnRingingFuture(CompletableFuture<Void> future) {
242         mBlockOnRingingFuture = future;
243     }
244 
245     @VisibleForTesting
setNotificationManager(NotificationManager notificationManager)246     public void setNotificationManager(NotificationManager notificationManager) {
247         mNotificationManager = notificationManager;
248     }
249 
startRinging(Call foregroundCall, boolean isHfpDeviceAttached)250     public boolean startRinging(Call foregroundCall, boolean isHfpDeviceAttached) {
251         boolean deferBlockOnRingingFuture = false;
252         // try-finally to ensure that the block on ringing future is always called.
253         try {
254             if (foregroundCall == null) {
255                 Log.wtf(this, "startRinging called with null foreground call.");
256                 return false;
257             }
258 
259             if (foregroundCall.getState() != CallState.RINGING
260                     && foregroundCall.getState() != CallState.SIMULATED_RINGING) {
261                 // It's possible for bluetooth to connect JUST as a call goes active, which would
262                 // mean the call would start ringing again.
263                 Log.i(this, "startRinging called for non-ringing foreground callid=%s",
264                         foregroundCall.getId());
265                 return false;
266             }
267 
268             // Use completable future to establish a timeout, not intent to make these work outside
269             // the main thread asynchronously
270             // TODO: moving these RingerAttributes calculation out of Telecom lock to avoid blocking
271             CompletableFuture<RingerAttributes> ringerAttributesFuture = CompletableFuture
272                     .supplyAsync(() -> getRingerAttributes(foregroundCall, isHfpDeviceAttached),
273                             new LoggedHandlerExecutor(getHandler(), "R.sR", null));
274 
275             RingerAttributes attributes = null;
276             try {
277                 mAttributesLatch = new CountDownLatch(1);
278                 attributes = ringerAttributesFuture.get(
279                         RINGER_ATTRIBUTES_TIMEOUT, TimeUnit.MILLISECONDS);
280             } catch (ExecutionException | InterruptedException | TimeoutException e) {
281                 // Keep attributes as null
282                 Log.i(this, "getAttributes error: " + e);
283             }
284 
285             if (attributes == null) {
286                 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING,
287                         "RingerAttributes error");
288                 return false;
289             }
290 
291             if (attributes.isEndEarly()) {
292                 boolean acquireAudioFocus = attributes.shouldAcquireAudioFocus();
293                 if (attributes.letDialerHandleRinging()) {
294                     Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Dialer handles");
295                     // Dialer will setup a ringtone, provide the audio focus if its audible.
296                     acquireAudioFocus |= attributes.isRingerAudible();
297                 }
298 
299                 if (attributes.isSilentRingingRequested()) {
300                     Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Silent ringing "
301                             + "requested");
302                 }
303                 if (attributes.isWorkProfileInQuietMode()) {
304                     Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING,
305                             "Work profile in quiet mode");
306                 }
307                 return acquireAudioFocus;
308             }
309 
310             stopCallWaiting();
311 
312             final boolean shouldFlash = attributes.shouldRingForContact();
313             if (mAccessibilityManagerAdapter != null && shouldFlash) {
314                 Log.addEvent(foregroundCall, LogUtils.Events.FLASH_NOTIFICATION_START);
315                 getHandler().post(() ->
316                         mAccessibilityManagerAdapter.startFlashNotificationSequence(mContext,
317                                 AccessibilityManager.FLASH_REASON_CALL));
318             }
319 
320             // Determine if the settings and DND mode indicate that the vibrator can be used right
321             // now.
322             final boolean isVibratorEnabled =
323                     isVibratorEnabled(mContext, attributes.shouldRingForContact());
324             boolean shouldApplyRampingRinger =
325                     isVibratorEnabled && mSystemSettingsUtil.isRampingRingerEnabled(mContext);
326 
327             boolean isHapticOnly = false;
328             boolean useCustomVibrationEffect = false;
329 
330             mVolumeShaperConfig = null;
331 
332             if (attributes.isRingerAudible()) {
333                 mRingingCall = foregroundCall;
334                 Log.addEvent(foregroundCall, LogUtils.Events.START_RINGER);
335                 // Because we wait until a contact info query to complete before processing a
336                 // call (for the purposes of direct-to-voicemail), the information about custom
337                 // ringtones should be available by the time this code executes. We can safely
338                 // request the custom ringtone from the call and expect it to be current.
339                 if (shouldApplyRampingRinger) {
340                     Log.i(this, "create ramping ringer.");
341                     float silencePoint = (float) (RAMPING_RINGER_VIBRATION_DURATION)
342                             / (float) (RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION);
343                     mVolumeShaperConfig =
344                             new VolumeShaper.Configuration.Builder()
345                                     .setDuration(RAMPING_RINGER_VIBRATION_DURATION
346                                             + RAMPING_RINGER_DURATION)
347                                     .setCurve(
348                                             new float[]{0.f, silencePoint + EPSILON
349                                                     /*keep monotonicity*/, 1.f},
350                                             new float[]{0.f, 0.f, 1.f})
351                                     .setInterpolatorType(
352                                             VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
353                                     .build();
354                     if (mSystemSettingsUtil.isAudioCoupledVibrationForRampingRingerEnabled()) {
355                         useCustomVibrationEffect = true;
356                     }
357                 } else {
358                     if (DEBUG_RINGER) {
359                         Log.i(this, "Create ringer with custom vibration effect");
360                     }
361                     // Ramping ringtone is not enabled.
362                     useCustomVibrationEffect = true;
363                 }
364             } else {
365                 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING,
366                         "Inaudible: " + attributes.getInaudibleReason()
367                                 + " isVibratorEnabled=" + isVibratorEnabled);
368 
369                 if (isVibratorEnabled) {
370                     // If ringer is not audible for this call, then the phone is in "Vibrate" mode.
371                     // Use haptic-only ringtone or do not play anything.
372                     isHapticOnly = true;
373                     if (DEBUG_RINGER) {
374                         Log.i(this, "Set ringtone as haptic only: " + isHapticOnly);
375                     }
376                 } else {
377                     foregroundCall.setUserMissed(USER_MISSED_NO_VIBRATE);
378                     return attributes.shouldAcquireAudioFocus(); // ringer not audible
379                 }
380             }
381 
382             boolean hapticChannelsMuted = !isVibratorEnabled || !mIsHapticPlaybackSupportedByDevice;
383             if (shouldApplyRampingRinger
384                     && !mSystemSettingsUtil.isAudioCoupledVibrationForRampingRingerEnabled()
385                     && isVibratorEnabled) {
386                 Log.i(this, "Muted haptic channels since audio coupled ramping ringer is disabled");
387                 hapticChannelsMuted = true;
388             } else if (hapticChannelsMuted) {
389                 Log.i(this,
390                         "Muted haptic channels isVibratorEnabled=%s, hapticPlaybackSupported=%s",
391                         isVibratorEnabled, mIsHapticPlaybackSupportedByDevice);
392             }
393             // Defer ringtone creation to the async player thread.
394             Supplier<Ringtone> ringtoneSupplier;
395             final boolean finalHapticChannelsMuted = hapticChannelsMuted;
396             if (isHapticOnly) {
397                 if (hapticChannelsMuted) {
398                     Log.i(this,
399                             "want haptic only ringtone but haptics are muted, skip ringtone play");
400                     ringtoneSupplier = null;
401                 } else {
402                     ringtoneSupplier = mRingtoneFactory::getHapticOnlyRingtone;
403                 }
404             } else {
405                 ringtoneSupplier = () -> mRingtoneFactory.getRingtone(
406                         foregroundCall, mVolumeShaperConfig, finalHapticChannelsMuted);
407             }
408 
409             // If vibration will be done, reserve the vibrator.
410             boolean vibratorReserved = isVibratorEnabled && attributes.shouldRingForContact()
411                 && tryReserveVibration(foregroundCall);
412             if (!vibratorReserved) {
413                 foregroundCall.setUserMissed(USER_MISSED_NO_VIBRATE);
414                 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION,
415                         "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, "
416                                 + "isVibratorEnabled=%b",
417                         mVibrator.hasVibrator(),
418                         mSystemSettingsUtil.isRingVibrationEnabled(mContext),
419                         mAudioManager.getRingerMode(), isVibratorEnabled);
420             }
421 
422             // The vibration logic depends on the loaded ringtone, but we need to defer the ringtone
423             // load to the async ringtone thread. Hence, we bundle up the final part of this method
424             // for that thread to run after loading the ringtone. This logic is intended to run even
425             // if the loaded ringtone is null. However if a stop event arrives before the ringtone
426             // creation finishes, then this consumer can be skipped.
427             final boolean finalUseCustomVibrationEffect = useCustomVibrationEffect;
428             BiConsumer<Ringtone, Boolean> afterRingtoneLogic =
429                     (Ringtone ringtone, Boolean stopped) -> {
430                 try {
431                     if (stopped.booleanValue() || !vibratorReserved) {
432                         // don't start vibration if the ringing is already abandoned, or the
433                         // vibrator wasn't reserved. This still triggers the mBlockOnRingingFuture.
434                         return;
435                     }
436                     final VibrationEffect vibrationEffect;
437                     if (ringtone != null && finalUseCustomVibrationEffect) {
438                         if (DEBUG_RINGER) {
439                             Log.d(this, "Using ringtone defined vibration effect.");
440                         }
441                         vibrationEffect = getVibrationEffectForRingtone(ringtone);
442                     } else {
443                         vibrationEffect = mDefaultVibrationEffect;
444                     }
445 
446                     boolean isUsingAudioCoupledHaptics =
447                             !finalHapticChannelsMuted && ringtone != null
448                                     && ringtone.hasHapticChannels();
449                     vibrateIfNeeded(isUsingAudioCoupledHaptics, foregroundCall, vibrationEffect);
450                 } finally {
451                     // This is used to signal to tests that the async play() call has completed.
452                     if (mBlockOnRingingFuture != null) {
453                         mBlockOnRingingFuture.complete(null);
454                     }
455                 }
456             };
457             deferBlockOnRingingFuture = true;  // Run in vibrationLogic.
458             if (ringtoneSupplier != null) {
459                 mRingtonePlayer.play(ringtoneSupplier, afterRingtoneLogic, isHfpDeviceAttached);
460             } else {
461                 afterRingtoneLogic.accept(/* ringtone= */ null, /* stopped= */ false);
462             }
463 
464             // shouldAcquireAudioFocus is meant to be true, but that check is deferred to here
465             // because until now is when we actually know if the ringtone loading worked.
466             return attributes.shouldAcquireAudioFocus()
467                     || (!isHapticOnly && attributes.isRingerAudible());
468         } finally {
469             // This is used to signal to tests that the async play() call has completed. It can
470             // be deferred into AsyncRingtonePlayer
471             if (mBlockOnRingingFuture != null && !deferBlockOnRingingFuture) {
472                 mBlockOnRingingFuture.complete(null);
473             }
474         }
475     }
476 
477     /**
478      * Try to reserve the vibrator for this call, returning false if it's already committed.
479      * The vibration will be started by AsyncRingtonePlayer to ensure timing is aligned with the
480      * audio. The logic uses mVibratingCall to say which call is currently getting ready to vibrate,
481      * or actually vibrating (indicated by mIsVibrating).
482      *
483      * Once reserved, the vibrateIfNeeded method is expected to be called. Note that if
484      * audio-coupled haptics were used instead of vibrator, the reservation still stays until
485      * ringing is stopped, because the vibrator is exclusive to a single vibration source.
486      *
487      * Note that this "reservation" is only local to the Ringer - it's not locking the vibrator, so
488      * if it's busy with some other important vibration, this ringer's one may not displace it.
489      */
tryReserveVibration(Call foregroundCall)490     private boolean tryReserveVibration(Call foregroundCall) {
491         synchronized (mLock) {
492             if (mVibratingCall != null || mIsVibrating) {
493                 return false;
494             }
495             mVibratingCall = foregroundCall;
496             return true;
497         }
498    }
499 
vibrateIfNeeded(boolean isUsingAudioCoupledHaptics, Call foregroundCall, VibrationEffect effect)500     private void vibrateIfNeeded(boolean isUsingAudioCoupledHaptics, Call foregroundCall,
501             VibrationEffect effect) {
502         if (isUsingAudioCoupledHaptics) {
503             Log.addEvent(
504                 foregroundCall, LogUtils.Events.SKIP_VIBRATION, "using audio-coupled haptics");
505             return;
506         }
507 
508         synchronized (mLock) {
509             // Ensure the reservation is live. The mIsVibrating check should be redundant.
510             if (foregroundCall == mVibratingCall && !mIsVibrating) {
511                 Log.addEvent(foregroundCall, LogUtils.Events.START_VIBRATOR,
512                     "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b",
513                     mVibrator.hasVibrator(), mSystemSettingsUtil.isRingVibrationEnabled(mContext),
514                     mAudioManager.getRingerMode(), mIsVibrating);
515                 mIsVibrating = true;
516                 mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
517                 Log.i(this, "start vibration.");
518             }
519             // else stopped already: this isn't started unless a reservation was made.
520         }
521     }
522 
getVibrationEffectForRingtone(@onNull Ringtone ringtone)523     private VibrationEffect getVibrationEffectForRingtone(@NonNull Ringtone ringtone) {
524         Uri ringtoneUri = ringtone.getUri();
525         if (ringtoneUri == null) {
526             return mDefaultVibrationEffect;
527         }
528         try {
529             VibrationEffect effect = mVibrationEffectProxy.get(ringtoneUri, mContext);
530             if (effect == null) {
531               Log.i(this, "did not find vibration effect, falling back to default vibration");
532               return mDefaultVibrationEffect;
533             }
534             return effect;
535         } catch (IllegalArgumentException iae) {
536             // Deep in the bowels of the VibrationEffect class it is possible for an
537             // IllegalArgumentException to be thrown if there is an invalid URI specified in the
538             // device config, or a content provider failure.  Rather than crashing the Telecom
539             // process we will just use the default vibration effect.
540             Log.e(this, iae, "getVibrationEffectForRingtone: failed to get vibration effect");
541             return mDefaultVibrationEffect;
542         }
543     }
544 
startCallWaiting(Call call)545     public void startCallWaiting(Call call) {
546         startCallWaiting(call, null);
547     }
548 
startCallWaiting(Call call, String reason)549     public void startCallWaiting(Call call, String reason) {
550         if (mSystemSettingsUtil.isTheaterModeOn(mContext)) {
551             return;
552         }
553 
554         if (mInCallController.doesConnectedDialerSupportRinging(
555                 call.getAssociatedUser())) {
556             Log.addEvent(call, LogUtils.Events.SKIP_RINGING, "Dialer handles");
557             return;
558         }
559 
560         if (call.isSelfManaged()) {
561             Log.addEvent(call, LogUtils.Events.SKIP_RINGING, "Self-managed");
562             return;
563         }
564 
565         Log.v(this, "Playing call-waiting tone.");
566 
567         stopRinging();
568 
569         if (mCallWaitingPlayer == null) {
570             Log.addEvent(call, LogUtils.Events.START_CALL_WAITING_TONE, reason);
571             mCallWaitingCall = call;
572             mCallWaitingPlayer =
573                     mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);
574             mCallWaitingPlayer.startTone();
575         }
576     }
577 
stopRinging()578     public void stopRinging() {
579         final Call foregroundCall = mRingingCall != null ? mRingingCall : mVibratingCall;
580         if (mAccessibilityManagerAdapter != null) {
581             Log.addEvent(foregroundCall, LogUtils.Events.FLASH_NOTIFICATION_STOP);
582             getHandler().post(() ->
583                     mAccessibilityManagerAdapter.stopFlashNotificationSequence(mContext));
584         }
585 
586         synchronized (mLock) {
587             if (mRingingCall != null) {
588                 Log.addEvent(mRingingCall, LogUtils.Events.STOP_RINGER);
589                 mRingingCall = null;
590             }
591 
592             mRingtonePlayer.stop();
593 
594             if (mIsVibrating) {
595                 Log.addEvent(mVibratingCall, LogUtils.Events.STOP_VIBRATOR);
596                 mVibrator.cancel();
597                 mIsVibrating = false;
598             }
599             mVibratingCall = null;  // Prevents vibrations from starting via AsyncRingtonePlayer.
600         }
601     }
602 
stopCallWaiting()603     public void stopCallWaiting() {
604         Log.v(this, "stop call waiting.");
605         if (mCallWaitingPlayer != null) {
606             if (mCallWaitingCall != null) {
607                 Log.addEvent(mCallWaitingCall, LogUtils.Events.STOP_CALL_WAITING_TONE);
608                 mCallWaitingCall = null;
609             }
610 
611             mCallWaitingPlayer.stopTone();
612             mCallWaitingPlayer = null;
613         }
614     }
615 
isRinging()616     public boolean isRinging() {
617         return mRingtonePlayer.isPlaying();
618     }
619 
620     /**
621      * shouldRingForContact checks if the caller matches one of the Do Not Disturb bypass
622      * settings (ex. A contact or repeat caller might be able to bypass DND settings). If
623      * matchesCallFilter returns true, this means the caller can bypass the Do Not Disturb settings
624      * and interrupt the user; otherwise call is suppressed.
625      */
shouldRingForContact(Call call)626     public boolean shouldRingForContact(Call call) {
627         // avoid re-computing manager.matcherCallFilter(Bundle)
628         if (call.wasDndCheckComputedForCall()) {
629             Log.i(this, "shouldRingForContact: returning computation from DndCallFilter.");
630             return !call.isCallSuppressedByDoNotDisturb();
631         }
632         final Uri contactUri = call.getHandle();
633         final Bundle peopleExtras = new Bundle();
634         if (contactUri != null) {
635             ArrayList<Person> personList = new ArrayList<>();
636             personList.add(new Person.Builder().setUri(contactUri.toString()).build());
637             peopleExtras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, personList);
638         }
639         return mNotificationManager.matchesCallFilter(peopleExtras);
640     }
641 
hasExternalRinger(Call foregroundCall)642     private boolean hasExternalRinger(Call foregroundCall) {
643         Bundle intentExtras = foregroundCall.getIntentExtras();
644         if (intentExtras != null) {
645             return intentExtras.getBoolean(TelecomManager.EXTRA_CALL_HAS_IN_BAND_RINGTONE, false);
646         } else {
647             return false;
648         }
649     }
650 
isVibratorEnabled(Context context, boolean shouldRingForContact)651     private boolean isVibratorEnabled(Context context, boolean shouldRingForContact) {
652         AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
653         // Use AudioManager#getRingerMode for more accurate result, instead of
654         // AudioManager#getRingerModeInternal which only useful for volume controllers
655         boolean zenModeOn = mNotificationManager != null
656                 && mNotificationManager.getZenMode() != ZEN_MODE_OFF;
657         return mVibrator.hasVibrator()
658                 && mSystemSettingsUtil.isRingVibrationEnabled(context)
659                 && (audioManager.getRingerMode() != AudioManager.RINGER_MODE_SILENT
660                 || (zenModeOn && shouldRingForContact));
661     }
662 
getRingerAttributes(Call call, boolean isHfpDeviceAttached)663     private RingerAttributes getRingerAttributes(Call call, boolean isHfpDeviceAttached) {
664         mAudioManager = mContext.getSystemService(AudioManager.class);
665         RingerAttributes.Builder builder = new RingerAttributes.Builder();
666 
667         LogUtils.EventTimer timer = new EventTimer();
668 
669         boolean isVolumeOverZero = mAudioManager.getStreamVolume(AudioManager.STREAM_RING) > 0;
670         timer.record("isVolumeOverZero");
671         boolean shouldRingForContact = shouldRingForContact(call);
672         timer.record("shouldRingForContact");
673         boolean isSelfManaged = call.isSelfManaged();
674         timer.record("isSelfManaged");
675         boolean isSilentRingingRequested = call.isSilentRingingRequested();
676         timer.record("isSilentRingRequested");
677 
678         boolean isRingerAudible = isVolumeOverZero && shouldRingForContact;
679         timer.record("isRingerAudible");
680         String inaudibleReason = "";
681         if (!isRingerAudible) {
682             inaudibleReason = String.format("isVolumeOverZero=%s, shouldRingForContact=%s",
683                 isVolumeOverZero, shouldRingForContact);
684         }
685 
686         boolean hasExternalRinger = hasExternalRinger(call);
687         timer.record("hasExternalRinger");
688         // Don't do call waiting operations or vibration unless these are false.
689         boolean isTheaterModeOn = mSystemSettingsUtil.isTheaterModeOn(mContext);
690         timer.record("isTheaterModeOn");
691         boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging(
692                 call.getAssociatedUser());
693         timer.record("letDialerHandleRinging");
694         boolean isWorkProfileInQuietMode =
695                 isProfileInQuietMode(call.getAssociatedUser());
696         timer.record("isWorkProfileInQuietMode");
697 
698         Log.i(this, "startRinging timings: " + timer);
699         boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged ||
700                 hasExternalRinger || isSilentRingingRequested || isWorkProfileInQuietMode;
701 
702         if (endEarly) {
703             Log.i(this, "Ending early -- isTheaterModeOn=%s, letDialerHandleRinging=%s, " +
704                             "isSelfManaged=%s, hasExternalRinger=%s, silentRingingRequested=%s, " +
705                             "isWorkProfileInQuietMode=%s",
706                     isTheaterModeOn, letDialerHandleRinging, isSelfManaged, hasExternalRinger,
707                     isSilentRingingRequested, isWorkProfileInQuietMode);
708         }
709 
710         // Acquire audio focus under any of the following conditions:
711         // 1. Should ring for contact and there's an HFP device attached
712         // 2. Volume is over zero, we should ring for the contact, and there's a audible ringtone
713         //    present. (This check is deferred until ringer knows the ringtone)
714         // 3. The call is self-managed.
715         boolean shouldAcquireAudioFocus = !isWorkProfileInQuietMode &&
716                 ((isHfpDeviceAttached && shouldRingForContact) || isSelfManaged);
717 
718         // Set missed reason according to attributes
719         if (!isVolumeOverZero) {
720             call.setUserMissed(USER_MISSED_LOW_RING_VOLUME);
721         }
722         if (!shouldRingForContact) {
723             call.setUserMissed(USER_MISSED_DND_MODE);
724         }
725 
726         mAttributesLatch.countDown();
727         return builder.setEndEarly(endEarly)
728                 .setLetDialerHandleRinging(letDialerHandleRinging)
729                 .setAcquireAudioFocus(shouldAcquireAudioFocus)
730                 .setRingerAudible(isRingerAudible)
731                 .setInaudibleReason(inaudibleReason)
732                 .setShouldRingForContact(shouldRingForContact)
733                 .setSilentRingingRequested(isSilentRingingRequested)
734                 .setWorkProfileQuietMode(isWorkProfileInQuietMode)
735                 .build();
736     }
737 
isProfileInQuietMode(UserHandle user)738     private boolean isProfileInQuietMode(UserHandle user) {
739         UserManager um = mContext.getSystemService(UserManager.class);
740         return um.isManagedProfile(user.getIdentifier()) && um.isQuietModeEnabled(user);
741     }
742 
getHandler()743     private Handler getHandler() {
744         if (mHandler == null) {
745             HandlerThread handlerThread = new HandlerThread("Ringer");
746             handlerThread.start();
747             mHandler = handlerThread.getThreadHandler();
748         }
749         return mHandler;
750     }
751 
752     @VisibleForTesting
waitForAttributesCompletion()753     public boolean waitForAttributesCompletion() throws InterruptedException {
754         if (mAttributesLatch != null) {
755             return mAttributesLatch.await(RINGER_ATTRIBUTES_TIMEOUT, TimeUnit.MILLISECONDS);
756         } else {
757             return false;
758         }
759     }
760 }
761