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 or call-waiting. These are used only for logging purposes. 66 */ 67 private Call mRingingCall; 68 private Call mCallWaitingCall; 69 70 /** 71 * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls. 72 */ 73 private boolean mIsVibrating = false; 74 75 /** Initializes the Ringer. */ 76 @VisibleForTesting 77 public Ringer( 78 InCallTonePlayer.Factory playerFactory, 79 Context context, 80 SystemSettingsUtil systemSettingsUtil, 81 AsyncRingtonePlayer asyncRingtonePlayer, 82 RingtoneFactory ringtoneFactory, 83 Vibrator vibrator, 84 InCallController inCallController) { 85 86 mSystemSettingsUtil = systemSettingsUtil; 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 = vibrator; 92 mRingtonePlayer = asyncRingtonePlayer; 93 mRingtoneFactory = ringtoneFactory; 94 mInCallController = inCallController; 95 } 96 97 public void startRinging(Call foregroundCall) { 98 if (mSystemSettingsUtil.isTheaterModeOn(mContext)) { 99 return; 100 } 101 102 if (foregroundCall == null) { 103 Log.wtf(this, "startRinging called with null foreground call."); 104 return; 105 } 106 107 if (mInCallController.doesConnectedDialerSupportRinging()) { 108 Log.event(foregroundCall, Log.Events.SKIP_RINGING); 109 return; 110 } 111 112 stopCallWaiting(); 113 114 if (!shouldRingForContact(foregroundCall.getContactUri())) { 115 return; 116 } 117 118 AudioManager audioManager = 119 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 120 if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) { 121 mRingingCall = foregroundCall; 122 Log.event(foregroundCall, Log.Events.START_RINGER); 123 // Because we wait until a contact info query to complete before processing a 124 // call (for the purposes of direct-to-voicemail), the information about custom 125 // ringtones should be available by the time this code executes. We can safely 126 // request the custom ringtone from the call and expect it to be current. 127 mRingtonePlayer.play(mRingtoneFactory, foregroundCall); 128 } else { 129 Log.v(this, "startRingingOrCallWaiting, skipping because volume is 0"); 130 } 131 132 if (shouldVibrate(mContext) && !mIsVibrating) { 133 mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT, 134 VIBRATION_ATTRIBUTES); 135 mIsVibrating = true; 136 } 137 } 138 139 public void startCallWaiting(Call call) { 140 if (mSystemSettingsUtil.isTheaterModeOn(mContext)) { 141 return; 142 } 143 144 if (mInCallController.doesConnectedDialerSupportRinging()) { 145 Log.event(call, Log.Events.SKIP_RINGING); 146 return; 147 } 148 149 Log.v(this, "Playing call-waiting tone."); 150 151 stopRinging(); 152 153 if (mCallWaitingPlayer == null) { 154 Log.event(call, Log.Events.START_CALL_WAITING_TONE); 155 mCallWaitingCall = call; 156 mCallWaitingPlayer = 157 mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING); 158 mCallWaitingPlayer.startTone(); 159 } 160 } 161 162 public void stopRinging() { 163 if (mRingingCall != null) { 164 Log.event(mRingingCall, Log.Events.STOP_RINGER); 165 mRingingCall = null; 166 } 167 168 mRingtonePlayer.stop(); 169 170 if (mIsVibrating) { 171 mVibrator.cancel(); 172 mIsVibrating = false; 173 } 174 } 175 176 public void stopCallWaiting() { 177 Log.v(this, "stop call waiting."); 178 if (mCallWaitingPlayer != null) { 179 if (mCallWaitingCall != null) { 180 Log.event(mCallWaitingCall, Log.Events.STOP_CALL_WAITING_TONE); 181 mCallWaitingCall = null; 182 } 183 184 mCallWaitingPlayer.stopTone(); 185 mCallWaitingPlayer = null; 186 } 187 } 188 189 private boolean shouldRingForContact(Uri contactUri) { 190 final NotificationManager manager = 191 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 192 final Bundle extras = new Bundle(); 193 if (contactUri != null) { 194 extras.putStringArray(Notification.EXTRA_PEOPLE, new String[] {contactUri.toString()}); 195 } 196 return manager.matchesCallFilter(extras); 197 } 198 199 private boolean shouldVibrate(Context context) { 200 AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 201 int ringerMode = audioManager.getRingerModeInternal(); 202 if (getVibrateWhenRinging(context)) { 203 return ringerMode != AudioManager.RINGER_MODE_SILENT; 204 } else { 205 return ringerMode == AudioManager.RINGER_MODE_VIBRATE; 206 } 207 } 208 209 private boolean getVibrateWhenRinging(Context context) { 210 if (!mVibrator.hasVibrator()) { 211 return false; 212 } 213 return mSystemSettingsUtil.canVibrateWhenRinging(context); 214 } 215 } 216