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