1 /*
2  * Copyright (C) 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 static com.android.server.telecom.LogUtils.Events.START_RINBACK;
20 import static com.android.server.telecom.LogUtils.Events.STOP_RINGBACK;
21 
22 import com.android.internal.annotations.VisibleForTesting;
23 import com.android.internal.util.Preconditions;
24 import android.telecom.Log;
25 
26 /**
27  * Plays ringback tones. Ringback is different from other tones because it operates as the current
28  * audio for a call, whereas most tones play as simple timed events. This means ringback must be
29  * able to turn off and on as the user switches between calls. This is why it is implemented as its
30  * own class.
31  */
32 public class RingbackPlayer {
33 
34     private final InCallTonePlayer.Factory mPlayerFactory;
35 
36     /**
37      * The current call for which the ringback tone is being played.
38      */
39     private Call mCall;
40 
41     /**
42      * The currently active player.
43      */
44     private InCallTonePlayer mTonePlayer;
45 
46     private final Object mLock;
47 
48     @VisibleForTesting
RingbackPlayer(InCallTonePlayer.Factory playerFactory)49     public RingbackPlayer(InCallTonePlayer.Factory playerFactory) {
50         mPlayerFactory = playerFactory;
51         mLock = new Object();
52     }
53 
54     /**
55      * Starts ringback for the specified dialing call as needed.
56      *
57      * @param call The call for which to ringback.
58      */
startRingbackForCall(Call call)59     public void startRingbackForCall(Call call) {
60         synchronized (mLock) {
61             Preconditions.checkState(call.getState() == CallState.DIALING);
62 
63             if (mCall == call) {
64                 Log.w(this, "Ignoring duplicate requests to ring for %s.", call);
65                 return;
66             }
67 
68             if (mCall != null) {
69                 // We only get here for the foreground call so, there's no reason why there should
70                 // exist a current dialing call.
71                 Log.wtf(this, "Ringback player thinks there are two foreground-dialing calls.");
72             }
73 
74             mCall = call;
75             if (mTonePlayer == null) {
76                 Log.i(this, "Playing the ringback tone for %s.", call);
77                 Log.addEvent(call, START_RINBACK);
78                 mTonePlayer = mPlayerFactory.createPlayer(call, InCallTonePlayer.TONE_RING_BACK);
79                 mTonePlayer.startTone();
80             }
81         }
82     }
83 
84     /**
85      * Stops the ringback for the specified dialing call as needed.
86      *
87      * @param call The call for which to stop ringback.
88      */
stopRingbackForCall(Call call)89     public void stopRingbackForCall(Call call) {
90         synchronized (mLock) {
91             if (mCall == call) {
92                 // The foreground call is no longer dialing or is no longer the foreground call. In
93                 // either case, stop the ringback tone.
94                 mCall = null;
95 
96                 if (mTonePlayer == null) {
97                     Log.w(this, "No player found to stop.");
98                 } else {
99                     Log.i(this, "Stopping the ringback tone for %s.", call);
100                     Log.addEvent(call, STOP_RINGBACK);
101                     mTonePlayer.stopTone();
102                     mTonePlayer = null;
103                 }
104             }
105         }
106     }
107 
isRingbackPlaying()108     public boolean isRingbackPlaying() {
109         synchronized (mLock) {
110             return mTonePlayer != null;
111         }
112     }
113 }