• 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.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