Home | History | Annotate | Download | only in telecom
      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.os.VibrationEffect;
     24 import android.telecom.Log;
     25 import android.telecom.TelecomManager;
     26 import android.media.AudioAttributes;
     27 import android.media.AudioManager;
     28 import android.media.Ringtone;
     29 import android.net.Uri;
     30 import android.os.Bundle;
     31 import android.os.Vibrator;
     32 
     33 import com.android.internal.annotations.VisibleForTesting;
     34 
     35 import java.util.ArrayList;
     36 
     37 /**
     38  * Controls the ringtone player.
     39  */
     40 @VisibleForTesting
     41 public class Ringer {
     42     @VisibleForTesting
     43     public VibrationEffect mDefaultVibrationEffect;
     44 
     45     private static final long[] PULSE_PATTERN = {0,12,250,12,500, // priming  + interval
     46             50,50,50,50,50,50,50,50,50,50,50,50,50,50, // ease-in
     47             300, // Peak
     48             1000}; // pause before repetition
     49 
     50     private static final int[] PULSE_AMPLITUDE = {0,255,0,255,0, // priming  + interval
     51             77,77,78,79,81,84,87,93,101,114,133,162,205,255, // ease-in (min amplitude = 30%)
     52             255, // Peak
     53             0}; // pause before repetition
     54 
     55     private static final long[] SIMPLE_VIBRATION_PATTERN = {
     56             0, // No delay before starting
     57             1000, // How long to vibrate
     58             1000, // How long to wait before vibrating again
     59     };
     60 
     61     private static final int[] SIMPLE_VIBRATION_AMPLITUDE = {
     62             0, // No delay before starting
     63             255, // Vibrate full amplitude
     64             0, // No amplitude while waiting
     65     };
     66 
     67     /**
     68      * Indicates that vibration should be repeated at element 5 in the {@link #PULSE_AMPLITUDE} and
     69      * {@link #PULSE_PATTERN} arrays.  This means repetition will happen for the main ease-in/peak
     70      * pattern, but the priming + interval part will not be repeated.
     71      */
     72     private static final int REPEAT_VIBRATION_AT = 5;
     73 
     74     private static final int REPEAT_SIMPLE_VIBRATION_AT = 1;
     75 
     76     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
     77             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
     78             .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
     79             .build();
     80 
     81     /**
     82      * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
     83      * calls and explicit ordering is useful for maintaining the proper state of the ringer.
     84      */
     85 
     86     private final SystemSettingsUtil mSystemSettingsUtil;
     87     private final InCallTonePlayer.Factory mPlayerFactory;
     88     private final AsyncRingtonePlayer mRingtonePlayer;
     89     private final Context mContext;
     90     private final Vibrator mVibrator;
     91     private final InCallController mInCallController;
     92 
     93     private InCallTonePlayer mCallWaitingPlayer;
     94     private RingtoneFactory mRingtoneFactory;
     95 
     96     /**
     97      * Call objects that are ringing, vibrating or call-waiting. These are used only for logging
     98      * purposes.
     99      */
    100     private Call mRingingCall;
    101     private Call mVibratingCall;
    102     private Call mCallWaitingCall;
    103 
    104     /**
    105      * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
    106      */
    107     private boolean mIsVibrating = false;
    108 
    109     /** Initializes the Ringer. */
    110     @VisibleForTesting
    111     public Ringer(
    112             InCallTonePlayer.Factory playerFactory,
    113             Context context,
    114             SystemSettingsUtil systemSettingsUtil,
    115             AsyncRingtonePlayer asyncRingtonePlayer,
    116             RingtoneFactory ringtoneFactory,
    117             Vibrator vibrator,
    118             InCallController inCallController) {
    119 
    120         mSystemSettingsUtil = systemSettingsUtil;
    121         mPlayerFactory = playerFactory;
    122         mContext = context;
    123         // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
    124         // vibrator object will be isolated from others.
    125         mVibrator = vibrator;
    126         mRingtonePlayer = asyncRingtonePlayer;
    127         mRingtoneFactory = ringtoneFactory;
    128         mInCallController = inCallController;
    129 
    130         if (mContext.getResources().getBoolean(R.bool.use_simple_vibration_pattern)) {
    131             mDefaultVibrationEffect = VibrationEffect.createWaveform(SIMPLE_VIBRATION_PATTERN,
    132                     SIMPLE_VIBRATION_AMPLITUDE, REPEAT_SIMPLE_VIBRATION_AT);
    133         } else {
    134             mDefaultVibrationEffect = VibrationEffect.createWaveform(PULSE_PATTERN,
    135                     PULSE_AMPLITUDE, REPEAT_VIBRATION_AT);
    136         }
    137     }
    138 
    139     public boolean startRinging(Call foregroundCall, boolean isHfpDeviceAttached) {
    140         if (foregroundCall == null) {
    141             Log.wtf(this, "startRinging called with null foreground call.");
    142             return false;
    143         }
    144 
    145         AudioManager audioManager =
    146                 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
    147         boolean isVolumeOverZero = audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0;
    148         boolean shouldRingForContact = shouldRingForContact(foregroundCall.getContactUri());
    149         boolean isRingtonePresent = !(mRingtoneFactory.getRingtone(foregroundCall) == null);
    150         boolean isSelfManaged = foregroundCall.isSelfManaged();
    151 
    152         boolean isRingerAudible = isVolumeOverZero && shouldRingForContact && isRingtonePresent;
    153         boolean hasExternalRinger = hasExternalRinger(foregroundCall);
    154         // Acquire audio focus under any of the following conditions:
    155         // 1. Should ring for contact and there's an HFP device attached
    156         // 2. Volume is over zero, we should ring for the contact, and there's a audible ringtone
    157         //    present.
    158         // 3. The call is self-managed.
    159         boolean shouldAcquireAudioFocus =
    160                 isRingerAudible || (isHfpDeviceAttached && shouldRingForContact) || isSelfManaged;
    161 
    162         // Don't do call waiting operations or vibration unless these are false.
    163         boolean isTheaterModeOn = mSystemSettingsUtil.isTheaterModeOn(mContext);
    164         boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging();
    165         boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged ||
    166                 hasExternalRinger;
    167 
    168         if (endEarly) {
    169             if (letDialerHandleRinging) {
    170                 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING);
    171             }
    172             Log.i(this, "Ending early -- isTheaterModeOn=%s, letDialerHandleRinging=%s, " +
    173                     "isSelfManaged=%s, hasExternalRinger=%s", isTheaterModeOn,
    174                     letDialerHandleRinging, isSelfManaged, hasExternalRinger);
    175             return shouldAcquireAudioFocus;
    176         }
    177 
    178         stopCallWaiting();
    179 
    180         VibrationEffect effect;
    181         if (isRingerAudible) {
    182             mRingingCall = foregroundCall;
    183             Log.addEvent(foregroundCall, LogUtils.Events.START_RINGER);
    184             // Because we wait until a contact info query to complete before processing a
    185             // call (for the purposes of direct-to-voicemail), the information about custom
    186             // ringtones should be available by the time this code executes. We can safely
    187             // request the custom ringtone from the call and expect it to be current.
    188             mRingtonePlayer.play(mRingtoneFactory, foregroundCall);
    189             effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall);
    190         } else {
    191             Log.i(this, "startRinging: skipping because ringer would not be audible. " +
    192                     "isVolumeOverZero=%s, shouldRingForContact=%s, isRingtonePresent=%s",
    193                     isVolumeOverZero, shouldRingForContact, isRingtonePresent);
    194             effect = mDefaultVibrationEffect;
    195         }
    196 
    197         if (shouldVibrate(mContext, foregroundCall) && !mIsVibrating && shouldRingForContact) {
    198             mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
    199             mIsVibrating = true;
    200         } else if (mIsVibrating) {
    201             Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, "already vibrating");
    202         }
    203 
    204         return shouldAcquireAudioFocus;
    205     }
    206 
    207     private VibrationEffect getVibrationEffectForCall(RingtoneFactory factory, Call call) {
    208         VibrationEffect effect = null;
    209         Ringtone ringtone = factory.getRingtone(call);
    210         Uri ringtoneUri = ringtone != null ? ringtone.getUri() : null;
    211         if (ringtoneUri != null) {
    212             effect = VibrationEffect.get(ringtoneUri, mContext);
    213         }
    214 
    215         if (effect == null) {
    216             effect = mDefaultVibrationEffect;
    217         }
    218         return effect;
    219     }
    220 
    221     public void startCallWaiting(Call call) {
    222         if (mSystemSettingsUtil.isTheaterModeOn(mContext)) {
    223             return;
    224         }
    225 
    226         if (mInCallController.doesConnectedDialerSupportRinging()) {
    227             Log.addEvent(call, LogUtils.Events.SKIP_RINGING);
    228             return;
    229         }
    230 
    231         if (call.isSelfManaged()) {
    232             Log.addEvent(call, LogUtils.Events.SKIP_RINGING, "Self-managed");
    233             return;
    234         }
    235 
    236         Log.v(this, "Playing call-waiting tone.");
    237 
    238         stopRinging();
    239 
    240         if (mCallWaitingPlayer == null) {
    241             Log.addEvent(call, LogUtils.Events.START_CALL_WAITING_TONE);
    242             mCallWaitingCall = call;
    243             mCallWaitingPlayer =
    244                     mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);
    245             mCallWaitingPlayer.startTone();
    246         }
    247     }
    248 
    249     public void stopRinging() {
    250         if (mRingingCall != null) {
    251             Log.addEvent(mRingingCall, LogUtils.Events.STOP_RINGER);
    252             mRingingCall = null;
    253         }
    254 
    255         mRingtonePlayer.stop();
    256 
    257         if (mIsVibrating) {
    258             Log.addEvent(mVibratingCall, LogUtils.Events.STOP_VIBRATOR);
    259             mVibrator.cancel();
    260             mIsVibrating = false;
    261             mVibratingCall = null;
    262         }
    263     }
    264 
    265     public void stopCallWaiting() {
    266         Log.v(this, "stop call waiting.");
    267         if (mCallWaitingPlayer != null) {
    268             if (mCallWaitingCall != null) {
    269                 Log.addEvent(mCallWaitingCall, LogUtils.Events.STOP_CALL_WAITING_TONE);
    270                 mCallWaitingCall = null;
    271             }
    272 
    273             mCallWaitingPlayer.stopTone();
    274             mCallWaitingPlayer = null;
    275         }
    276     }
    277 
    278     private boolean shouldRingForContact(Uri contactUri) {
    279         final NotificationManager manager =
    280                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    281         final Bundle peopleExtras = new Bundle();
    282         if (contactUri != null) {
    283             ArrayList<Person> personList = new ArrayList<>();
    284             personList.add(new Person.Builder().setUri(contactUri.toString()).build());
    285             peopleExtras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, personList);
    286         }
    287         return manager.matchesCallFilter(peopleExtras);
    288     }
    289 
    290     private boolean hasExternalRinger(Call foregroundCall) {
    291         Bundle intentExtras = foregroundCall.getIntentExtras();
    292         if (intentExtras != null) {
    293             return intentExtras.getBoolean(TelecomManager.EXTRA_CALL_EXTERNAL_RINGER, false);
    294         } else {
    295             return false;
    296         }
    297     }
    298 
    299     private boolean shouldVibrate(Context context, Call call) {
    300         AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    301         int ringerMode = audioManager.getRingerModeInternal();
    302         boolean shouldVibrate;
    303         if (getVibrateWhenRinging(context)) {
    304             shouldVibrate = ringerMode != AudioManager.RINGER_MODE_SILENT;
    305         } else {
    306             shouldVibrate = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
    307         }
    308 
    309         // Technically this should be in the calling method, but it seemed a little odd to pass
    310         // around a whole bunch of state just for logging purposes.
    311         if (shouldVibrate) {
    312             Log.addEvent(call, LogUtils.Events.START_VIBRATOR,
    313                     "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b",
    314                     mVibrator.hasVibrator(), mSystemSettingsUtil.canVibrateWhenRinging(context),
    315                     ringerMode, mIsVibrating);
    316         } else {
    317             Log.addEvent(call, LogUtils.Events.SKIP_VIBRATION,
    318                     "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b",
    319                     mVibrator.hasVibrator(), mSystemSettingsUtil.canVibrateWhenRinging(context),
    320                     ringerMode, mIsVibrating);
    321         }
    322 
    323         return shouldVibrate;
    324     }
    325 
    326     private boolean getVibrateWhenRinging(Context context) {
    327         if (!mVibrator.hasVibrator()) {
    328             return false;
    329         }
    330         return mSystemSettingsUtil.canVibrateWhenRinging(context);
    331     }
    332 }
    333