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
Ringer( InCallTonePlayer.Factory playerFactory, Context context, SystemSettingsUtil systemSettingsUtil, AsyncRingtonePlayer asyncRingtonePlayer, RingtoneFactory ringtoneFactory, Vibrator vibrator, InCallController inCallController)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 
startRinging(Call foregroundCall)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 
startCallWaiting(Call call)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 
stopRinging()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 
stopCallWaiting()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 
shouldRingForContact(Uri contactUri)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 
shouldVibrate(Context context)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 
getVibrateWhenRinging(Context context)209     private boolean getVibrateWhenRinging(Context context) {
210         if (!mVibrator.hasVibrator()) {
211             return false;
212         }
213         return mSystemSettingsUtil.canVibrateWhenRinging(context);
214     }
215 }
216