• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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