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