blob: 304a698d82616b651cbfaf8c4739f655bf14a7eb [file] [log] [blame]
Santos Cordon92a2d812014-04-30 15:19:01 -07001/*
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
Tyler Gunn7cc70b42014-09-12 22:17:27 -070017package com.android.server.telecom;
Santos Cordon92a2d812014-04-30 15:19:01 -070018
Tyler Gunn91d43cf2014-09-17 12:19:39 -070019import android.content.Context;
Santos Cordon92a2d812014-04-30 15:19:01 -070020import android.media.AudioManager;
21import android.media.ToneGenerator;
Roshan Pius9bb88e92015-06-08 16:46:11 -070022import android.os.Handler;
23import android.os.HandlerThread;
Hall Liu55076712017-02-28 19:59:30 -080024import android.os.Looper;
Roshan Pius9bb88e92015-06-08 16:46:11 -070025import android.os.Message;
Santos Cordon92a2d812014-04-30 15:19:01 -070026import android.provider.Settings;
Brad Ebinger953e1af2016-10-05 15:45:22 -070027import android.telecom.Log;
Hall Liu55076712017-02-28 19:59:30 -080028import android.telecom.Logging.Session;
Santos Cordon92a2d812014-04-30 15:19:01 -070029
Hall Liu55076712017-02-28 19:59:30 -080030import com.android.internal.annotations.VisibleForTesting;
Roshan Pius9bb88e92015-06-08 16:46:11 -070031
Tyler Gunn91d43cf2014-09-17 12:19:39 -070032// TODO: Needed for move to system service: import com.android.internal.R;
Santos Cordon92a2d812014-04-30 15:19:01 -070033
34/**
35 * Plays DTMF tones locally for the caller to hear. In order to reduce (1) the amount of times we
36 * check the "play local tones" setting and (2) the length of time we keep the tone generator, this
37 * class employs a concept of a call "session" that starts and stops when the foreground call
38 * changes.
39 */
Hall Liue792b332016-04-19 14:23:09 -070040public class DtmfLocalTonePlayer {
Hall Liu55076712017-02-28 19:59:30 -080041 public static class ToneGeneratorProxy {
42 /** Generator used to actually play the tone. */
43 private ToneGenerator mToneGenerator;
44
45 public void create() {
46 if (mToneGenerator == null) {
47 try {
48 mToneGenerator = new ToneGenerator(AudioManager.STREAM_DTMF, 80);
49 } catch (RuntimeException e) {
50 Log.e(this, e, "Error creating local tone generator.");
51 mToneGenerator = null;
52 }
53 }
54 }
55
56 public void release() {
57 if (mToneGenerator != null) {
58 mToneGenerator.release();
59 mToneGenerator = null;
60 }
61 }
62
63 public boolean isPresent() {
64 return mToneGenerator != null;
65 }
66
67 public void startTone(int toneType, int durationMs) {
68 if (mToneGenerator != null) {
69 mToneGenerator.startTone(toneType, durationMs);
70 }
71 }
72
73 public void stopTone() {
74 if (mToneGenerator != null) {
75 mToneGenerator.stopTone();
76 }
77 }
78 }
79
80 private final class ToneHandler extends Handler {
81
82 public ToneHandler(Looper looper) {
83 super(looper);
84 }
85
86 @Override
87 public void handleMessage(Message msg) {
Tyler Gunnf7d32b52017-06-21 19:55:54 -070088 try {
89 if (msg.obj instanceof Session) {
90 Log.continueSession((Session) msg.obj, "DLTP.TH");
91 }
Hall Liu55076712017-02-28 19:59:30 -080092
Tyler Gunnf7d32b52017-06-21 19:55:54 -070093 switch (msg.what) {
94 case EVENT_START_SESSION:
95 mToneGeneratorProxy.create();
96 break;
97 case EVENT_END_SESSION:
98 mToneGeneratorProxy.release();
99 break;
100 case EVENT_PLAY_TONE:
101 char c = (char) msg.arg1;
102 if (!mToneGeneratorProxy.isPresent()) {
103 Log.d(this, "playTone: no tone generator, %c.", c);
104 } else {
105 Log.d(this, "starting local tone: %c.", c);
106 int tone = getMappedTone(c);
107 if (tone != ToneGenerator.TONE_UNKNOWN) {
108 mToneGeneratorProxy.startTone(tone, -1 /* toneDuration */);
109 }
Hall Liu55076712017-02-28 19:59:30 -0800110 }
Tyler Gunnf7d32b52017-06-21 19:55:54 -0700111 break;
112 case EVENT_STOP_TONE:
113 if (mToneGeneratorProxy.isPresent()) {
114 mToneGeneratorProxy.stopTone();
115 }
116 break;
117 default:
118 Log.w(this, "Unknown message: %d", msg.what);
119 break;
120 }
121 } finally {
122 Log.endSession();
Hall Liu55076712017-02-28 19:59:30 -0800123 }
124 }
125 }
Santos Cordon92a2d812014-04-30 15:19:01 -0700126
127 /** The current call associated with an existing dtmf session. */
128 private Call mCall;
129
Roshan Pius9bb88e92015-06-08 16:46:11 -0700130 /**
131 * Message codes to be used for creating and deleting ToneGenerator object in the tonegenerator
Hall Liu55076712017-02-28 19:59:30 -0800132 * thread, as well as for actually playing the tones.
Roshan Pius9bb88e92015-06-08 16:46:11 -0700133 */
Hall Liu55076712017-02-28 19:59:30 -0800134 private static final int EVENT_START_SESSION = 1;
135 private static final int EVENT_END_SESSION = 2;
136 private static final int EVENT_PLAY_TONE = 3;
137 private static final int EVENT_STOP_TONE = 4;
Roshan Pius9bb88e92015-06-08 16:46:11 -0700138
139 /** Handler running on the tonegenerator thread. */
Hall Liu55076712017-02-28 19:59:30 -0800140 private ToneHandler mHandler;
Roshan Pius9bb88e92015-06-08 16:46:11 -0700141
Hall Liu55076712017-02-28 19:59:30 -0800142 private final ToneGeneratorProxy mToneGeneratorProxy;
143
144 public DtmfLocalTonePlayer(ToneGeneratorProxy toneGeneratorProxy) {
145 mToneGeneratorProxy = toneGeneratorProxy;
146 }
Roshan Pius9bb88e92015-06-08 16:46:11 -0700147
Santos Cordon92a2d812014-04-30 15:19:01 -0700148 public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) {
Jay Shrauneracb91eb2014-08-08 16:04:53 -0700149 endDtmfSession(oldForegroundCall);
150 startDtmfSession(newForegroundCall);
Santos Cordon92a2d812014-04-30 15:19:01 -0700151 }
152
153 /**
154 * Starts playing the dtmf tone specified by c.
155 *
156 * @param call The associated call.
157 * @param c The digit to play.
158 */
Hall Liu55076712017-02-28 19:59:30 -0800159 public void playTone(Call call, char c) {
Santos Cordon92a2d812014-04-30 15:19:01 -0700160 // Do nothing if it is not the right call.
161 if (mCall != call) {
162 return;
163 }
Hall Liu55076712017-02-28 19:59:30 -0800164
165 getHandler().sendMessage(
166 getHandler().obtainMessage(EVENT_PLAY_TONE, (int) c, 0, Log.createSubsession()));
Santos Cordon92a2d812014-04-30 15:19:01 -0700167 }
168
169 /**
170 * Stops any currently playing dtmf tone.
171 *
172 * @param call The associated call.
173 */
Hall Liu55076712017-02-28 19:59:30 -0800174 public void stopTone(Call call) {
Santos Cordon92a2d812014-04-30 15:19:01 -0700175 // Do nothing if it's not the right call.
176 if (mCall != call) {
177 return;
178 }
Hall Liu55076712017-02-28 19:59:30 -0800179
180 getHandler().sendMessage(
181 getHandler().obtainMessage(EVENT_STOP_TONE, Log.createSubsession()));
Santos Cordon92a2d812014-04-30 15:19:01 -0700182 }
183
184 /**
185 * Runs initialization requires to play local tones during a call.
186 *
187 * @param call The call associated with this dtmf session.
188 */
189 private void startDtmfSession(Call call) {
Jay Shrauneracb91eb2014-08-08 16:04:53 -0700190 if (call == null) {
191 return;
192 }
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700193 final Context context = call.getContext();
Santos Cordon92a2d812014-04-30 15:19:01 -0700194 final boolean areLocalTonesEnabled;
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700195 if (context.getResources().getBoolean(R.bool.allow_local_dtmf_tones)) {
Santos Cordon92a2d812014-04-30 15:19:01 -0700196 areLocalTonesEnabled = Settings.System.getInt(
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700197 context.getContentResolver(), Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
Santos Cordon92a2d812014-04-30 15:19:01 -0700198 } else {
199 areLocalTonesEnabled = false;
200 }
201
202 mCall = call;
203
204 if (areLocalTonesEnabled) {
Roshan Pius9bb88e92015-06-08 16:46:11 -0700205 Log.d(this, "Posting create.");
Hall Liu55076712017-02-28 19:59:30 -0800206 getHandler().sendMessage(
207 getHandler().obtainMessage(EVENT_START_SESSION, Log.createSubsession()));
Santos Cordon92a2d812014-04-30 15:19:01 -0700208 }
209 }
210
211 /**
212 * Releases resources needed for playing local dtmf tones.
213 *
214 * @param call The call associated with the session to end.
215 */
216 private void endDtmfSession(Call call) {
Jay Shrauneracb91eb2014-08-08 16:04:53 -0700217 if (call != null && mCall == call) {
Santos Cordon92a2d812014-04-30 15:19:01 -0700218 // Do a stopTone() in case the sessions ends before we are told to stop the tone.
219 stopTone(call);
220
221 mCall = null;
Roshan Pius9bb88e92015-06-08 16:46:11 -0700222 Log.d(this, "Posting delete.");
Hall Liu55076712017-02-28 19:59:30 -0800223 getHandler().sendMessage(
224 getHandler().obtainMessage(EVENT_END_SESSION, Log.createSubsession()));
Roshan Pius9bb88e92015-06-08 16:46:11 -0700225 }
226 }
Santos Cordon92a2d812014-04-30 15:19:01 -0700227
Roshan Pius9bb88e92015-06-08 16:46:11 -0700228 /**
Hall Liu55076712017-02-28 19:59:30 -0800229 * Creates a new ToneHandler on a separate thread if none exists, and returns it.
230 * No need for locking, since everything that calls this is protected by the Telecom lock.
Roshan Pius9bb88e92015-06-08 16:46:11 -0700231 */
Hall Liu55076712017-02-28 19:59:30 -0800232 @VisibleForTesting
233 public ToneHandler getHandler() {
234 if (mHandler == null) {
235 HandlerThread thread = new HandlerThread("tonegenerator-dtmf");
236 thread.start();
237 mHandler = new ToneHandler(thread.getLooper());
Santos Cordon92a2d812014-04-30 15:19:01 -0700238 }
Hall Liu55076712017-02-28 19:59:30 -0800239 return mHandler;
Roshan Pius9bb88e92015-06-08 16:46:11 -0700240 }
241
Hall Liue091ab92015-12-18 17:05:30 -0800242 private static int getMappedTone(char digit) {
Santos Cordon4bc02452014-11-19 15:51:12 -0800243 if (digit >= '0' && digit <= '9') {
244 return ToneGenerator.TONE_DTMF_0 + digit - '0';
245 } else if (digit == '#') {
246 return ToneGenerator.TONE_DTMF_P;
247 } else if (digit == '*') {
248 return ToneGenerator.TONE_DTMF_S;
249 }
250 return ToneGenerator.TONE_UNKNOWN;
251 }
Santos Cordon92a2d812014-04-30 15:19:01 -0700252}