Home | History | Annotate | Download | only in telecom
      1 /*
      2  * Copyright (C) 2014 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.content.Context;
     22 import android.media.AudioAttributes;
     23 import android.media.AudioManager;
     24 import android.net.Uri;
     25 import android.os.Bundle;
     26 import android.os.SystemVibrator;
     27 import android.os.Vibrator;
     28 import android.provider.Settings;
     29 import android.telecom.CallState;
     30 
     31 import java.util.LinkedList;
     32 import java.util.List;
     33 
     34 /**
     35  * Controls the ringtone player.
     36  */
     37 final class Ringer extends CallsManagerListenerBase {
     38     private static final long[] VIBRATION_PATTERN = new long[] {
     39         0, // No delay before starting
     40         1000, // How long to vibrate
     41         1000, // How long to wait before vibrating again
     42     };
     43 
     44     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
     45             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
     46             .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
     47             .build();
     48 
     49     /** Indicate that we want the pattern to repeat at the step which turns on vibration. */
     50     private static final int VIBRATION_PATTERN_REPEAT = 1;
     51 
     52     private final AsyncRingtonePlayer mRingtonePlayer;
     53 
     54     /**
     55      * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
     56      * calls and explicit ordering is useful for maintaining the proper state of the ringer.
     57      */
     58     private final List<Call> mRingingCalls = new LinkedList<>();
     59 
     60     private final CallAudioManager mCallAudioManager;
     61     private final CallsManager mCallsManager;
     62     private final InCallTonePlayer.Factory mPlayerFactory;
     63     private final Context mContext;
     64     private final Vibrator mVibrator;
     65 
     66     private InCallTonePlayer mCallWaitingPlayer;
     67 
     68     /**
     69      * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
     70      */
     71     private boolean mIsVibrating = false;
     72 
     73     /** Initializes the Ringer. */
     74     Ringer(
     75             CallAudioManager callAudioManager,
     76             CallsManager callsManager,
     77             InCallTonePlayer.Factory playerFactory,
     78             Context context) {
     79 
     80         mCallAudioManager = callAudioManager;
     81         mCallsManager = callsManager;
     82         mPlayerFactory = playerFactory;
     83         mContext = context;
     84         // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
     85         // vibrator object will be isolated from others.
     86         mVibrator = new SystemVibrator(context);
     87         mRingtonePlayer = new AsyncRingtonePlayer(context);
     88     }
     89 
     90     @Override
     91     public void onCallAdded(final Call call) {
     92         if (call.isIncoming() && call.getState() == CallState.RINGING) {
     93             if (mRingingCalls.contains(call)) {
     94                 Log.wtf(this, "New ringing call is already in list of unanswered calls");
     95             }
     96             mRingingCalls.add(call);
     97             updateRinging();
     98         }
     99     }
    100 
    101     @Override
    102     public void onCallRemoved(Call call) {
    103         removeFromUnansweredCall(call);
    104     }
    105 
    106     @Override
    107     public void onCallStateChanged(Call call, int oldState, int newState) {
    108         if (newState != CallState.RINGING) {
    109             removeFromUnansweredCall(call);
    110         }
    111     }
    112 
    113     @Override
    114     public void onIncomingCallAnswered(Call call) {
    115         onRespondedToIncomingCall(call);
    116     }
    117 
    118     @Override
    119     public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage) {
    120         onRespondedToIncomingCall(call);
    121     }
    122 
    123     @Override
    124     public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) {
    125         if (mRingingCalls.contains(oldForegroundCall) ||
    126                 mRingingCalls.contains(newForegroundCall)) {
    127             updateRinging();
    128         }
    129     }
    130 
    131     /**
    132      * Silences the ringer for any actively ringing calls.
    133      */
    134     void silence() {
    135         // Remove all calls from the "ringing" set and then update the ringer.
    136         mRingingCalls.clear();
    137         updateRinging();
    138     }
    139 
    140     private void onRespondedToIncomingCall(Call call) {
    141         // Only stop the ringer if this call is the top-most incoming call.
    142         if (getTopMostUnansweredCall() == call) {
    143             removeFromUnansweredCall(call);
    144         }
    145     }
    146 
    147     private Call getTopMostUnansweredCall() {
    148         return mRingingCalls.isEmpty() ? null : mRingingCalls.get(0);
    149     }
    150 
    151     /**
    152      * Removes the specified call from the list of unanswered incoming calls and updates the ringer
    153      * based on the new state of {@link #mRingingCalls}. Safe to call with a call that is not
    154      * present in the list of incoming calls.
    155      */
    156     private void removeFromUnansweredCall(Call call) {
    157         mRingingCalls.remove(call);
    158         updateRinging();
    159     }
    160 
    161     private void updateRinging() {
    162         if (mRingingCalls.isEmpty()) {
    163             stopRinging();
    164             stopCallWaiting();
    165         } else {
    166             startRingingOrCallWaiting();
    167         }
    168     }
    169 
    170     private void startRingingOrCallWaiting() {
    171         Call foregroundCall = mCallsManager.getForegroundCall();
    172         Log.v(this, "startRingingOrCallWaiting, foregroundCall: %s.", foregroundCall);
    173 
    174         if (mRingingCalls.contains(foregroundCall)) {
    175             // The foreground call is one of incoming calls so play the ringer out loud.
    176             stopCallWaiting();
    177 
    178             if (!shouldRingForContact(foregroundCall.getContactUri())) {
    179                 return;
    180             }
    181 
    182             AudioManager audioManager =
    183                     (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
    184             if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) {
    185                 Log.v(this, "startRingingOrCallWaiting");
    186                 mCallAudioManager.setIsRinging(true);
    187 
    188                 // Because we wait until a contact info query to complete before processing a
    189                 // call (for the purposes of direct-to-voicemail), the information about custom
    190                 // ringtones should be available by the time this code executes. We can safely
    191                 // request the custom ringtone from the call and expect it to be current.
    192                 mRingtonePlayer.play(foregroundCall.getRingtone());
    193             } else {
    194                 Log.v(this, "startRingingOrCallWaiting, skipping because volume is 0");
    195             }
    196 
    197             if (shouldVibrate(mContext) && !mIsVibrating) {
    198                 mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,
    199                         VIBRATION_ATTRIBUTES);
    200                 mIsVibrating = true;
    201             }
    202         } else if (foregroundCall != null) {
    203             // The first incoming call added to Telecom is not a foreground call at this point
    204             // in time. If the current foreground call is null at point, don't play call-waiting
    205             // as the call will eventually be promoted to the foreground call and play the
    206             // ring tone.
    207             Log.v(this, "Playing call-waiting tone.");
    208 
    209             // All incoming calls are in background so play call waiting.
    210             stopRinging();
    211 
    212             if (mCallWaitingPlayer == null) {
    213                 mCallWaitingPlayer =
    214                         mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);
    215                 mCallWaitingPlayer.startTone();
    216             }
    217         }
    218     }
    219 
    220     private boolean shouldRingForContact(Uri contactUri) {
    221         final NotificationManager manager =
    222                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    223         final Bundle extras = new Bundle();
    224         if (contactUri != null) {
    225             extras.putStringArray(Notification.EXTRA_PEOPLE, new String[] {contactUri.toString()});
    226         }
    227         return manager.matchesCallFilter(extras);
    228     }
    229 
    230     private void stopRinging() {
    231         Log.v(this, "stopRinging");
    232 
    233         mRingtonePlayer.stop();
    234 
    235         if (mIsVibrating) {
    236             mVibrator.cancel();
    237             mIsVibrating = false;
    238         }
    239 
    240         // Even though stop is asynchronous it's ok to update the audio manager. Things like audio
    241         // focus are voluntary so releasing focus too early is not detrimental.
    242         mCallAudioManager.setIsRinging(false);
    243     }
    244 
    245     private void stopCallWaiting() {
    246         Log.v(this, "stop call waiting.");
    247         if (mCallWaitingPlayer != null) {
    248             mCallWaitingPlayer.stopTone();
    249             mCallWaitingPlayer = null;
    250         }
    251     }
    252 
    253     private boolean shouldVibrate(Context context) {
    254         AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    255         int ringerMode = audioManager.getRingerModeInternal();
    256         if (getVibrateWhenRinging(context)) {
    257             return ringerMode != AudioManager.RINGER_MODE_SILENT;
    258         } else {
    259             return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
    260         }
    261     }
    262 
    263     private boolean getVibrateWhenRinging(Context context) {
    264         if (!mVibrator.hasVibrator()) {
    265             return false;
    266         }
    267         return Settings.System.getInt(context.getContentResolver(),
    268                 Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
    269     }
    270 }
    271