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. */ Ringer( CallAudioManager callAudioManager, CallsManager callsManager, InCallTonePlayer.Factory playerFactory, Context context)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 onCallAdded(final Call call)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 onCallRemoved(Call call)102 public void onCallRemoved(Call call) { 103 removeFromUnansweredCall(call); 104 } 105 106 @Override onCallStateChanged(Call call, int oldState, int newState)107 public void onCallStateChanged(Call call, int oldState, int newState) { 108 if (newState != CallState.RINGING) { 109 removeFromUnansweredCall(call); 110 } 111 } 112 113 @Override onIncomingCallAnswered(Call call)114 public void onIncomingCallAnswered(Call call) { 115 onRespondedToIncomingCall(call); 116 } 117 118 @Override onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage)119 public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage) { 120 onRespondedToIncomingCall(call); 121 } 122 123 @Override onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall)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 */ silence()134 void silence() { 135 // Remove all calls from the "ringing" set and then update the ringer. 136 mRingingCalls.clear(); 137 updateRinging(); 138 } 139 onRespondedToIncomingCall(Call call)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 getTopMostUnansweredCall()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 */ removeFromUnansweredCall(Call call)156 private void removeFromUnansweredCall(Call call) { 157 mRingingCalls.remove(call); 158 updateRinging(); 159 } 160 updateRinging()161 private void updateRinging() { 162 if (mRingingCalls.isEmpty()) { 163 stopRinging(); 164 stopCallWaiting(); 165 } else { 166 startRingingOrCallWaiting(); 167 } 168 } 169 startRingingOrCallWaiting()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 shouldRingForContact(Uri contactUri)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 stopRinging()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 stopCallWaiting()245 private void stopCallWaiting() { 246 Log.v(this, "stop call waiting."); 247 if (mCallWaitingPlayer != null) { 248 mCallWaitingPlayer.stopTone(); 249 mCallWaitingPlayer = null; 250 } 251 } 252 shouldVibrate(Context context)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 getVibrateWhenRinging(Context context)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