• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014, 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.media.AudioManager;
20 import android.media.ToneGenerator;
21 import android.os.Handler;
22 import android.os.Looper;
23 import android.telecom.Log;
24 import android.telecom.Logging.Runnable;
25 import android.telecom.Logging.Session;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 
29 /**
30  * Play a call-related tone (ringback, busy signal, etc.) through ToneGenerator. To use, create an
31  * instance using InCallTonePlayer.Factory (passing in the TONE_* constant for the tone you want)
32  * and start() it. Implemented on top of {@link Thread} so that the tone plays in its own thread.
33  */
34 public class InCallTonePlayer extends Thread {
35 
36     /**
37      * Factory used to create InCallTonePlayers. Exists to aid with testing mocks.
38      */
39     public static class Factory {
40         private CallAudioManager mCallAudioManager;
41         private final CallAudioRoutePeripheralAdapter mCallAudioRoutePeripheralAdapter;
42         private final TelecomSystem.SyncRoot mLock;
43         private final ToneGeneratorFactory mToneGeneratorFactory;
44 
Factory(CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter, TelecomSystem.SyncRoot lock, ToneGeneratorFactory toneGeneratorFactory)45         Factory(CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter,
46                 TelecomSystem.SyncRoot lock, ToneGeneratorFactory toneGeneratorFactory) {
47             mCallAudioRoutePeripheralAdapter = callAudioRoutePeripheralAdapter;
48             mLock = lock;
49             mToneGeneratorFactory = toneGeneratorFactory;
50         }
51 
setCallAudioManager(CallAudioManager callAudioManager)52         public void setCallAudioManager(CallAudioManager callAudioManager) {
53             mCallAudioManager = callAudioManager;
54         }
55 
createPlayer(int tone)56         public InCallTonePlayer createPlayer(int tone) {
57             return new InCallTonePlayer(tone, mCallAudioManager,
58                     mCallAudioRoutePeripheralAdapter, mLock, mToneGeneratorFactory);
59         }
60     }
61 
62     public interface ToneGeneratorFactory {
get(int streamType, int volume)63         ToneGenerator get (int streamType, int volume);
64     }
65 
66     // The possible tones that we can play.
67     public static final int TONE_INVALID = 0;
68     public static final int TONE_BUSY = 1;
69     public static final int TONE_CALL_ENDED = 2;
70     public static final int TONE_OTA_CALL_ENDED = 3;
71     public static final int TONE_CALL_WAITING = 4;
72     public static final int TONE_CDMA_DROP = 5;
73     public static final int TONE_CONGESTION = 6;
74     public static final int TONE_INTERCEPT = 7;
75     public static final int TONE_OUT_OF_SERVICE = 8;
76     public static final int TONE_REDIAL = 9;
77     public static final int TONE_REORDER = 10;
78     public static final int TONE_RING_BACK = 11;
79     public static final int TONE_UNOBTAINABLE_NUMBER = 12;
80     public static final int TONE_VOICE_PRIVACY = 13;
81     public static final int TONE_VIDEO_UPGRADE = 14;
82 
83     private static final int RELATIVE_VOLUME_EMERGENCY = 100;
84     private static final int RELATIVE_VOLUME_HIPRI = 80;
85     private static final int RELATIVE_VOLUME_LOPRI = 50;
86 
87     // Buffer time (in msec) to add on to the tone timeout value. Needed mainly when the timeout
88     // value for a tone is exact duration of the tone itself.
89     private static final int TIMEOUT_BUFFER_MILLIS = 20;
90 
91     // The tone state.
92     private static final int STATE_OFF = 0;
93     private static final int STATE_ON = 1;
94     private static final int STATE_STOPPED = 2;
95 
96     /**
97      * Keeps count of the number of actively playing tones so that we can notify CallAudioManager
98      * when we need focus and when it can be release. This should only be manipulated from the main
99      * thread.
100      */
101     private static int sTonesPlaying = 0;
102 
103     private final CallAudioManager mCallAudioManager;
104     private final CallAudioRoutePeripheralAdapter mCallAudioRoutePeripheralAdapter;
105 
106     private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
107 
108     /** The ID of the tone to play. */
109     private final int mToneId;
110 
111     /** Current state of the tone player. */
112     private int mState;
113 
114     /** Telecom lock object. */
115     private final TelecomSystem.SyncRoot mLock;
116 
117     private Session mSession;
118     private final Object mSessionLock = new Object();
119 
120     private final ToneGeneratorFactory mToneGenerator;
121 
122     /**
123      * Initializes the tone player. Private; use the {@link Factory} to create tone players.
124      *
125      * @param toneId ID of the tone to play, see TONE_* constants.
126      */
InCallTonePlayer( int toneId, CallAudioManager callAudioManager, CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter, TelecomSystem.SyncRoot lock, ToneGeneratorFactory toneGeneratorFactory)127     private InCallTonePlayer(
128             int toneId,
129             CallAudioManager callAudioManager,
130             CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter,
131             TelecomSystem.SyncRoot lock,
132             ToneGeneratorFactory toneGeneratorFactory) {
133         mState = STATE_OFF;
134         mToneId = toneId;
135         mCallAudioManager = callAudioManager;
136         mCallAudioRoutePeripheralAdapter = callAudioRoutePeripheralAdapter;
137         mLock = lock;
138         mToneGenerator = toneGeneratorFactory;
139     }
140 
141     /** {@inheritDoc} */
142     @Override
run()143     public void run() {
144         ToneGenerator toneGenerator = null;
145         try {
146             synchronized (mSessionLock) {
147                 if (mSession != null) {
148                     Log.continueSession(mSession, "ICTP.r");
149                     mSession = null;
150                 }
151             }
152             Log.d(this, "run(toneId = %s)", mToneId);
153 
154             final int toneType;  // Passed to ToneGenerator.startTone.
155             final int toneVolume;  // Passed to the ToneGenerator constructor.
156             final int toneLengthMillis;
157 
158             switch (mToneId) {
159                 case TONE_BUSY:
160                     // TODO: CDMA-specific tones
161                     toneType = ToneGenerator.TONE_SUP_BUSY;
162                     toneVolume = RELATIVE_VOLUME_HIPRI;
163                     toneLengthMillis = 4000;
164                     break;
165                 case TONE_CALL_ENDED:
166                     toneType = ToneGenerator.TONE_PROP_PROMPT;
167                     toneVolume = RELATIVE_VOLUME_HIPRI;
168                     toneLengthMillis = 200;
169                     break;
170                 case TONE_OTA_CALL_ENDED:
171                     // TODO: fill in
172                     throw new IllegalStateException("OTA Call ended NYI.");
173                 case TONE_CALL_WAITING:
174                     toneType = ToneGenerator.TONE_SUP_CALL_WAITING;
175                     toneVolume = RELATIVE_VOLUME_HIPRI;
176                     toneLengthMillis = Integer.MAX_VALUE - TIMEOUT_BUFFER_MILLIS;
177                     break;
178                 case TONE_CDMA_DROP:
179                     toneType = ToneGenerator.TONE_CDMA_CALLDROP_LITE;
180                     toneVolume = RELATIVE_VOLUME_LOPRI;
181                     toneLengthMillis = 375;
182                     break;
183                 case TONE_CONGESTION:
184                     toneType = ToneGenerator.TONE_SUP_CONGESTION;
185                     toneVolume = RELATIVE_VOLUME_HIPRI;
186                     toneLengthMillis = 4000;
187                     break;
188                 case TONE_INTERCEPT:
189                     toneType = ToneGenerator.TONE_CDMA_ABBR_INTERCEPT;
190                     toneVolume = RELATIVE_VOLUME_LOPRI;
191                     toneLengthMillis = 500;
192                     break;
193                 case TONE_OUT_OF_SERVICE:
194                     toneType = ToneGenerator.TONE_CDMA_CALLDROP_LITE;
195                     toneVolume = RELATIVE_VOLUME_LOPRI;
196                     toneLengthMillis = 375;
197                     break;
198                 case TONE_REDIAL:
199                     toneType = ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE;
200                     toneVolume = RELATIVE_VOLUME_LOPRI;
201                     toneLengthMillis = 5000;
202                     break;
203                 case TONE_REORDER:
204                     toneType = ToneGenerator.TONE_CDMA_REORDER;
205                     toneVolume = RELATIVE_VOLUME_HIPRI;
206                     toneLengthMillis = 4000;
207                     break;
208                 case TONE_RING_BACK:
209                     toneType = ToneGenerator.TONE_SUP_RINGTONE;
210                     toneVolume = RELATIVE_VOLUME_HIPRI;
211                     toneLengthMillis = Integer.MAX_VALUE - TIMEOUT_BUFFER_MILLIS;
212                     break;
213                 case TONE_UNOBTAINABLE_NUMBER:
214                     toneType = ToneGenerator.TONE_SUP_ERROR;
215                     toneVolume = RELATIVE_VOLUME_HIPRI;
216                     toneLengthMillis = 4000;
217                     break;
218                 case TONE_VOICE_PRIVACY:
219                     // TODO: fill in.
220                     throw new IllegalStateException("Voice privacy tone NYI.");
221                 case TONE_VIDEO_UPGRADE:
222                     // Similar to the call waiting tone, but does not repeat.
223                     toneType = ToneGenerator.TONE_SUP_CALL_WAITING;
224                     toneVolume = RELATIVE_VOLUME_HIPRI;
225                     toneLengthMillis = 4000;
226                     break;
227                 default:
228                     throw new IllegalStateException("Bad toneId: " + mToneId);
229             }
230 
231             int stream = AudioManager.STREAM_VOICE_CALL;
232             if (mCallAudioRoutePeripheralAdapter.isBluetoothAudioOn()) {
233                 stream = AudioManager.STREAM_BLUETOOTH_SCO;
234             }
235 
236             // If the ToneGenerator creation fails, just continue without it. It is a local audio
237             // signal, and is not as important.
238             try {
239                 Log.v(this, "Creating generator");
240                 toneGenerator = mToneGenerator.get(stream, toneVolume);
241             } catch (RuntimeException e) {
242                 Log.w(this, "Failed to create ToneGenerator.", e);
243                 return;
244             }
245 
246             // TODO: Certain CDMA tones need to check the ringer-volume state before
247             // playing. See CallNotifier.InCallTonePlayer.
248 
249             // TODO: Some tones play through the end of a call so we need to inform
250             // CallAudioManager that we want focus the same way that Ringer does.
251 
252             synchronized (this) {
253                 if (mState != STATE_STOPPED) {
254                     mState = STATE_ON;
255                     toneGenerator.startTone(toneType);
256                     try {
257                         Log.v(this, "Starting tone %d...waiting for %d ms.", mToneId,
258                                 toneLengthMillis + TIMEOUT_BUFFER_MILLIS);
259                         wait(toneLengthMillis + TIMEOUT_BUFFER_MILLIS);
260                     } catch (InterruptedException e) {
261                         Log.w(this, "wait interrupted", e);
262                     }
263                 }
264             }
265             mState = STATE_OFF;
266         } finally {
267             if (toneGenerator != null) {
268                 toneGenerator.release();
269             }
270             cleanUpTonePlayer();
271             Log.endSession();
272         }
273     }
274 
275     @VisibleForTesting
startTone()276     public void startTone() {
277         sTonesPlaying++;
278         if (sTonesPlaying == 1) {
279             mCallAudioManager.setIsTonePlaying(true);
280         }
281 
282         synchronized (mSessionLock) {
283             if (mSession != null) {
284                 Log.cancelSubsession(mSession);
285             }
286             mSession = Log.createSubsession();
287         }
288 
289         super.start();
290     }
291 
292     @Override
start()293     public void start() {
294         Log.w(this, "Do not call the start method directly; use startTone instead.");
295     }
296 
297     /**
298      * Stops the tone.
299      */
300     @VisibleForTesting
stopTone()301     public void stopTone() {
302         synchronized (this) {
303             if (mState == STATE_ON) {
304                 Log.d(this, "Stopping the tone %d.", mToneId);
305                 notify();
306             }
307             mState = STATE_STOPPED;
308         }
309     }
310 
cleanUpTonePlayer()311     private void cleanUpTonePlayer() {
312         // Release focus on the main thread.
313         mMainThreadHandler.post(new Runnable("ICTP.cUTP", mLock) {
314             @Override
315             public void loggedRun() {
316                 if (sTonesPlaying == 0) {
317                     Log.wtf(this, "Over-releasing focus for tone player.");
318                 } else if (--sTonesPlaying == 0) {
319                     mCallAudioManager.setIsTonePlaying(false);
320                 }
321             }
322         }.prepare());
323     }
324 }
325