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.content.Context; 22 import android.telecom.Log; 23 import android.media.AudioAttributes; 24 import android.media.AudioManager; 25 import android.net.Uri; 26 import android.os.Bundle; 27 import android.os.Vibrator; 28 29 import com.android.internal.annotations.VisibleForTesting; 30 31 /** 32 * Controls the ringtone player. 33 */ 34 @VisibleForTesting 35 public class Ringer { 36 private static final long[] VIBRATION_PATTERN = new long[] { 37 0, // No delay before starting 38 1000, // How long to vibrate 39 1000, // How long to wait before vibrating again 40 }; 41 42 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() 43 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) 44 .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) 45 .build(); 46 47 /** Indicate that we want the pattern to repeat at the step which turns on vibration. */ 48 private static final int VIBRATION_PATTERN_REPEAT = 1; 49 50 /** 51 * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming 52 * calls and explicit ordering is useful for maintaining the proper state of the ringer. 53 */ 54 55 private final SystemSettingsUtil mSystemSettingsUtil; 56 private final InCallTonePlayer.Factory mPlayerFactory; 57 private final AsyncRingtonePlayer mRingtonePlayer; 58 private final Context mContext; 59 private final Vibrator mVibrator; 60 private final InCallController mInCallController; 61 62 private InCallTonePlayer mCallWaitingPlayer; 63 private RingtoneFactory mRingtoneFactory; 64 65 /** 66 * Call objects that are ringing, vibrating or call-waiting. These are used only for logging 67 * purposes. 68 */ 69 private Call mRingingCall; 70 private Call mVibratingCall; 71 private Call mCallWaitingCall; 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 @VisibleForTesting Ringer( InCallTonePlayer.Factory playerFactory, Context context, SystemSettingsUtil systemSettingsUtil, AsyncRingtonePlayer asyncRingtonePlayer, RingtoneFactory ringtoneFactory, Vibrator vibrator, InCallController inCallController)80 public Ringer( 81 InCallTonePlayer.Factory playerFactory, 82 Context context, 83 SystemSettingsUtil systemSettingsUtil, 84 AsyncRingtonePlayer asyncRingtonePlayer, 85 RingtoneFactory ringtoneFactory, 86 Vibrator vibrator, 87 InCallController inCallController) { 88 89 mSystemSettingsUtil = systemSettingsUtil; 90 mPlayerFactory = playerFactory; 91 mContext = context; 92 // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this 93 // vibrator object will be isolated from others. 94 mVibrator = vibrator; 95 mRingtonePlayer = asyncRingtonePlayer; 96 mRingtoneFactory = ringtoneFactory; 97 mInCallController = inCallController; 98 } 99 startRinging(Call foregroundCall, boolean isHfpDeviceAttached)100 public boolean startRinging(Call foregroundCall, boolean isHfpDeviceAttached) { 101 if (foregroundCall == null) { 102 Log.wtf(this, "startRinging called with null foreground call."); 103 return false; 104 } 105 106 AudioManager audioManager = 107 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 108 boolean isVolumeOverZero = audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0; 109 boolean shouldRingForContact = shouldRingForContact(foregroundCall.getContactUri()); 110 boolean isRingtonePresent = !(mRingtoneFactory.getRingtone(foregroundCall) == null); 111 boolean isSelfManaged = foregroundCall.isSelfManaged(); 112 113 boolean isRingerAudible = isVolumeOverZero && shouldRingForContact && isRingtonePresent; 114 // Acquire audio focus under any of the following conditions: 115 // 1. Should ring for contact and there's an HFP device attached 116 // 2. Volume is over zero, we should ring for the contact, and there's a audible ringtone 117 // present. 118 // 3. The call is self-managed. 119 boolean shouldAcquireAudioFocus = 120 isRingerAudible || (isHfpDeviceAttached && shouldRingForContact) || isSelfManaged; 121 122 // Don't do call waiting operations or vibration unless these are false. 123 boolean isTheaterModeOn = mSystemSettingsUtil.isTheaterModeOn(mContext); 124 boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging(); 125 boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged; 126 127 if (endEarly) { 128 if (letDialerHandleRinging) { 129 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING); 130 } 131 Log.i(this, "Ending early -- isTheaterModeOn=%s, letDialerHandleRinging=%s, " + 132 "isSelfManaged=%s", isTheaterModeOn, letDialerHandleRinging, isSelfManaged); 133 return shouldAcquireAudioFocus; 134 } 135 136 stopCallWaiting(); 137 138 if (isRingerAudible) { 139 mRingingCall = foregroundCall; 140 Log.addEvent(foregroundCall, LogUtils.Events.START_RINGER); 141 // Because we wait until a contact info query to complete before processing a 142 // call (for the purposes of direct-to-voicemail), the information about custom 143 // ringtones should be available by the time this code executes. We can safely 144 // request the custom ringtone from the call and expect it to be current. 145 mRingtonePlayer.play(mRingtoneFactory, foregroundCall); 146 } else { 147 Log.i(this, "startRinging: skipping because ringer would not be audible. " + 148 "isVolumeOverZero=%s, shouldRingForContact=%s, isRingtonePresent=%s", 149 isVolumeOverZero, shouldRingForContact, isRingtonePresent); 150 } 151 152 if (shouldVibrate(mContext, foregroundCall) && !mIsVibrating && shouldRingForContact) { 153 mVibratingCall = foregroundCall; 154 mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT, 155 VIBRATION_ATTRIBUTES); 156 mIsVibrating = true; 157 } else if (mIsVibrating) { 158 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, "already vibrating"); 159 } 160 161 return shouldAcquireAudioFocus; 162 } 163 startCallWaiting(Call call)164 public void startCallWaiting(Call call) { 165 if (mSystemSettingsUtil.isTheaterModeOn(mContext)) { 166 return; 167 } 168 169 if (mInCallController.doesConnectedDialerSupportRinging()) { 170 Log.addEvent(call, LogUtils.Events.SKIP_RINGING); 171 return; 172 } 173 174 if (call.isSelfManaged()) { 175 Log.addEvent(call, LogUtils.Events.SKIP_RINGING, "Self-managed"); 176 return; 177 } 178 179 Log.v(this, "Playing call-waiting tone."); 180 181 stopRinging(); 182 183 if (mCallWaitingPlayer == null) { 184 Log.addEvent(call, LogUtils.Events.START_CALL_WAITING_TONE); 185 mCallWaitingCall = call; 186 mCallWaitingPlayer = 187 mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING); 188 mCallWaitingPlayer.startTone(); 189 } 190 } 191 stopRinging()192 public void stopRinging() { 193 if (mRingingCall != null) { 194 Log.addEvent(mRingingCall, LogUtils.Events.STOP_RINGER); 195 mRingingCall = null; 196 } 197 198 mRingtonePlayer.stop(); 199 200 if (mIsVibrating) { 201 Log.addEvent(mVibratingCall, LogUtils.Events.STOP_VIBRATOR); 202 mVibrator.cancel(); 203 mIsVibrating = false; 204 mVibratingCall = null; 205 } 206 } 207 stopCallWaiting()208 public void stopCallWaiting() { 209 Log.v(this, "stop call waiting."); 210 if (mCallWaitingPlayer != null) { 211 if (mCallWaitingCall != null) { 212 Log.addEvent(mCallWaitingCall, LogUtils.Events.STOP_CALL_WAITING_TONE); 213 mCallWaitingCall = null; 214 } 215 216 mCallWaitingPlayer.stopTone(); 217 mCallWaitingPlayer = null; 218 } 219 } 220 shouldRingForContact(Uri contactUri)221 private boolean shouldRingForContact(Uri contactUri) { 222 final NotificationManager manager = 223 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 224 final Bundle extras = new Bundle(); 225 if (contactUri != null) { 226 extras.putStringArray(Notification.EXTRA_PEOPLE, new String[] {contactUri.toString()}); 227 } 228 return manager.matchesCallFilter(extras); 229 } 230 shouldVibrate(Context context, Call call)231 private boolean shouldVibrate(Context context, Call call) { 232 AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 233 int ringerMode = audioManager.getRingerModeInternal(); 234 boolean shouldVibrate; 235 if (getVibrateWhenRinging(context)) { 236 shouldVibrate = ringerMode != AudioManager.RINGER_MODE_SILENT; 237 } else { 238 shouldVibrate = ringerMode == AudioManager.RINGER_MODE_VIBRATE; 239 } 240 241 // Technically this should be in the calling method, but it seemed a little odd to pass 242 // around a whole bunch of state just for logging purposes. 243 if (shouldVibrate) { 244 Log.addEvent(call, LogUtils.Events.START_VIBRATOR, 245 "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b", 246 mVibrator.hasVibrator(), mSystemSettingsUtil.canVibrateWhenRinging(context), 247 ringerMode, mIsVibrating); 248 } else { 249 Log.addEvent(call, LogUtils.Events.SKIP_VIBRATION, 250 "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b", 251 mVibrator.hasVibrator(), mSystemSettingsUtil.canVibrateWhenRinging(context), 252 ringerMode, mIsVibrating); 253 } 254 255 return shouldVibrate; 256 } 257 getVibrateWhenRinging(Context context)258 private boolean getVibrateWhenRinging(Context context) { 259 if (!mVibrator.hasVibrator()) { 260 return false; 261 } 262 return mSystemSettingsUtil.canVibrateWhenRinging(context); 263 } 264 } 265