blob: 682c74acafcb1fa7525672f6928af800b948eea7 [file] [log] [blame]
Santos Cordon2eaff902013-08-05 04:37:55 -07001/*
2 * Copyright (C) 2013 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
17package com.android.phone;
18
19import com.google.common.collect.ImmutableMap;
20
21import android.media.AudioManager;
22import android.media.ToneGenerator;
23import android.os.Handler;
24import android.os.Message;
25import android.provider.Settings;
26import android.util.Log;
27
28import com.android.internal.telephony.CallManager;
29import com.android.internal.telephony.Phone;
30import com.android.services.telephony.common.Call;
31
32import java.util.HashMap;
33import java.util.List;
34import java.util.Map;
35
36/**
37 * Playing DTMF tones through the CallManager.
38 */
39public class DTMFTonePlayer implements CallModeler.Listener {
40 private static final String LOG_TAG = DTMFTonePlayer.class.getSimpleName();
41 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
42
43 private static final int DTMF_STOP = 100;
44
45 /** Hash Map to map a character to a tone*/
46 private static final Map<Character, Integer> mToneMap =
47 ImmutableMap.<Character, Integer>builder()
48 .put('1', ToneGenerator.TONE_DTMF_1)
49 .put('2', ToneGenerator.TONE_DTMF_2)
50 .put('3', ToneGenerator.TONE_DTMF_3)
51 .put('4', ToneGenerator.TONE_DTMF_4)
52 .put('5', ToneGenerator.TONE_DTMF_5)
53 .put('6', ToneGenerator.TONE_DTMF_6)
54 .put('7', ToneGenerator.TONE_DTMF_7)
55 .put('8', ToneGenerator.TONE_DTMF_8)
56 .put('9', ToneGenerator.TONE_DTMF_9)
57 .put('0', ToneGenerator.TONE_DTMF_0)
58 .put('#', ToneGenerator.TONE_DTMF_P)
59 .put('*', ToneGenerator.TONE_DTMF_S)
60 .build();
61
62 private final CallManager mCallManager;
63 private final CallModeler mCallModeler;
64 private final Object mToneGeneratorLock = new Object();
65 private ToneGenerator mToneGenerator;
66 private boolean mLocalToneEnabled;
67
68 public DTMFTonePlayer(CallManager callManager, CallModeler callModeler) {
69 mCallManager = callManager;
70 mCallModeler = callModeler;
71 }
72
73 @Override
74 public void onDisconnect(Call call) {
75 checkCallState();
76 }
77
78 @Override
79 public void onUpdate(List<Call> calls, boolean full) {
80 checkCallState();
81 }
82
83 /**
84 * Allocates some resources we keep around during a "dialer session".
85 *
86 * (Currently, a "dialer session" just means any situation where we
87 * might need to play local DTMF tones, which means that we need to
88 * keep a ToneGenerator instance around. A ToneGenerator instance
89 * keeps an AudioTrack resource busy in AudioFlinger, so we don't want
90 * to keep it around forever.)
91 *
92 * Call {@link stopDialerSession} to release the dialer session
93 * resources.
94 */
95 public void startDialerSession() {
96 logD("startDialerSession()... this = " + this);
97
98 // see if we need to play local tones.
99 if (PhoneGlobals.getInstance().getResources().getBoolean(R.bool.allow_local_dtmf_tones)) {
100 mLocalToneEnabled = Settings.System.getInt(
101 PhoneGlobals.getInstance().getContentResolver(),
102 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
103 } else {
104 mLocalToneEnabled = false;
105 }
106 logD("- startDialerSession: mLocalToneEnabled = " + mLocalToneEnabled);
107
108 // create the tone generator
109 // if the mToneGenerator creation fails, just continue without it. It is
110 // a local audio signal, and is not as important as the dtmf tone itself.
111 if (mLocalToneEnabled) {
112 synchronized (mToneGeneratorLock) {
113 if (mToneGenerator == null) {
114 try {
115 mToneGenerator = new ToneGenerator(AudioManager.STREAM_DTMF, 80);
116 } catch (RuntimeException e) {
117 Log.e(LOG_TAG, "Exception caught while creating local tone generator", e);
118 mToneGenerator = null;
119 }
120 }
121 }
122 }
123 }
124
125 /**
126 * Releases resources we keep around during a "dialer session"
127 * (see {@link startDialerSession}).
128 *
129 * It's safe to call this even without a corresponding
130 * startDialerSession call.
131 */
132 public void stopDialerSession() {
133 // release the tone generator.
134 synchronized (mToneGeneratorLock) {
135 if (mToneGenerator != null) {
136 mToneGenerator.release();
137 mToneGenerator = null;
138 }
139 }
140 }
141
142 /**
143 * Starts playback of the dtmf tone corresponding to the parameter.
144 */
145 public void playDtmfTone(char c) {
146 // Only play the tone if it exists.
147 if (!mToneMap.containsKey(c)) {
148 return;
149 }
150
151 if (!okToDialDtmfTones()) {
152 return;
153 }
154
155 // Read the settings as it may be changed by the user during the call
156 Phone phone = mCallManager.getFgPhone();
157
158 logD("startDtmfTone()...");
159
160 // Pass as a char to be sent to network
161 logD("send long dtmf for " + c);
162 mCallManager.startDtmf(c);
163
164 startLocalToneIfNeeded(c);
165 }
166
167 public void stopDtmfTone() {
168 mCallManager.stopDtmf();
169 stopLocalToneIfNeeded();
170 }
171
172 /**
173 * Plays the local tone based the phone type, optionally forcing a short
174 * tone.
175 */
176 private void startLocalToneIfNeeded(char c) {
177 if (mLocalToneEnabled) {
178 synchronized (mToneGeneratorLock) {
179 if (mToneGenerator == null) {
180 logD("startDtmfTone: mToneGenerator == null, tone: " + c);
181 } else {
182 logD("starting local tone " + c);
183 int toneDuration = -1;
184 mToneGenerator.startTone(mToneMap.get(c), toneDuration);
185 }
186 }
187 }
188 }
189
190 /**
191 * Stops the local tone based on the phone type.
192 */
193 public void stopLocalToneIfNeeded() {
194 // if local tone playback is enabled, stop it.
195 logD("trying to stop local tone...");
196 if (mLocalToneEnabled) {
197 synchronized (mToneGeneratorLock) {
198 if (mToneGenerator == null) {
199 logD("stopLocalTone: mToneGenerator == null");
200 } else {
201 logD("stopping local tone.");
202 mToneGenerator.stopTone();
203 }
204 }
205 }
206 }
207
208 private boolean okToDialDtmfTones() {
209 boolean hasActiveCall = false;
210 boolean hasIncomingCall = false;
211
212 final List<Call> calls = mCallModeler.getFullList();
213 final int len = calls.size();
214
215 for (int i = 0; i < len; i++) {
216 hasActiveCall |= (calls.get(i).getState() == Call.State.ACTIVE);
217 hasIncomingCall |= (calls.get(i).getState() == Call.State.INCOMING);
218 }
219
220 return hasActiveCall && !hasIncomingCall;
221 }
222
223 /**
224 * Checks to see if there are any active calls. If there are, then we want to allocate the tone
225 * resources for playing DTMF tone, otherwise release them.
226 */
227 private void checkCallState() {
228 if (mCallModeler.hasOutstandingActiveCall()) {
229 startDialerSession();
230 } else {
231 stopDialerSession();
232 }
233 }
234
235 /**
236 * static logging method
237 */
238 private static void logD(String msg) {
239 if (DBG) {
240 Log.d(LOG_TAG, msg);
241 }
242 }
243
244}