blob: 344814fcd1fc2a4e380539b7cef193b4bd0f5537 [file] [log] [blame]
Ihab Awad542e0ea2014-05-16 10:22:16 -07001/*
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
17package android.telecomm;
18
19import android.net.Uri;
20import android.os.Bundle;
Ihab Awad542e0ea2014-05-16 10:22:16 -070021
22import java.util.HashSet;
23import java.util.Set;
24
25/**
26 * Represents a connection to a remote endpoint that carries voice traffic.
27 */
28public abstract class Connection {
29
Ihab Awad542e0ea2014-05-16 10:22:16 -070030 public interface Listener {
31 void onStateChanged(Connection c, int state);
32 void onAudioStateChanged(Connection c, CallAudioState state);
33 void onHandleChanged(Connection c, Uri newHandle);
34 void onSignalChanged(Connection c, Bundle details);
35 void onDisconnected(Connection c, int cause, String message);
Ihab Awadf8358972014-05-28 16:46:42 -070036 void onRequestingRingback(Connection c, boolean ringback);
Ihab Awad542e0ea2014-05-16 10:22:16 -070037 void onDestroyed(Connection c);
38 }
39
40 public static class ListenerBase implements Listener {
41 /** {@inheritDoc} */
42 @Override
43 public void onStateChanged(Connection c, int state) {}
44
45 /** {@inheritDoc} */
46 @Override
47 public void onAudioStateChanged(Connection c, CallAudioState state) {}
48
49 /** {@inheritDoc} */
50 @Override
51 public void onHandleChanged(Connection c, Uri newHandle) {}
52
53 /** {@inheritDoc} */
54 @Override
55 public void onSignalChanged(Connection c, Bundle details) {}
56
57 /** {@inheritDoc} */
58 @Override
59 public void onDisconnected(Connection c, int cause, String message) {}
60
61 /** {@inheritDoc} */
62 @Override
63 public void onDestroyed(Connection c) {}
Ihab Awadf8358972014-05-28 16:46:42 -070064
65 /** {@inheritDoc} */
66 @Override
67 public void onRequestingRingback(Connection c, boolean ringback) {}
Ihab Awad542e0ea2014-05-16 10:22:16 -070068 }
69
70 public final class State {
71 private State() {}
72
73 public static final int NEW = 0;
74 public static final int RINGING = 1;
75 public static final int DIALING = 2;
76 public static final int ACTIVE = 3;
77 public static final int HOLDING = 4;
78 public static final int DISCONNECTED = 5;
79 }
80
81 private final Set<Listener> mListeners = new HashSet<>();
82 private int mState = State.NEW;
83 private CallAudioState mCallAudioState;
84 private Uri mHandle;
Ihab Awadf8358972014-05-28 16:46:42 -070085 private boolean mRequestingRingback = false;
Ihab Awad542e0ea2014-05-16 10:22:16 -070086
87 /**
88 * Create a new Connection.
89 */
90 protected Connection() {}
91
92 /**
93 * @return The handle (e.g., phone number) to which this Connection
94 * is currently communicating.
95 */
96 public final Uri getHandle() {
97 return mHandle;
98 }
99
100 /**
101 * @return The state of this Connection.
102 *
103 * @hide
104 */
105 public final int getState() {
106 return mState;
107 }
108
109 /**
110 * @return The audio state of the call, describing how its audio is currently
111 * being routed by the system. This is {@code null} if this Connection
112 * does not directly know about its audio state.
113 */
114 public final CallAudioState getCallAudioState() {
115 return mCallAudioState;
116 }
117
118 /**
119 * Assign a listener to be notified of state changes.
120 *
121 * @param l A listener.
122 * @return This Connection.
123 *
124 * @hide
125 */
126 public final Connection addConnectionListener(Listener l) {
127 mListeners.add(l);
128 return this;
129 }
130
131 /**
132 * Remove a previously assigned listener that was being notified of state changes.
133 *
134 * @param l A Listener.
135 * @return This Connection.
136 *
137 * @hide
138 */
139 public final Connection removeConnectionListener(Listener l) {
140 mListeners.remove(l);
141 return this;
142 }
143
144 /**
145 * Play a DTMF tone in this Connection.
146 *
147 * @param c A DTMF character.
148 *
149 * @hide
150 */
151 public final void playDtmfTone(char c) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700152 Log.d(this, "playDtmfTone %c", c);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700153 onPlayDtmfTone(c);
154 }
155
156 /**
157 * Stop any DTMF tones which may be playing in this Connection.
158 *
159 * @hide
160 */
161 public final void stopDtmfTone() {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700162 Log.d(this, "stopDtmfTone");
Ihab Awad542e0ea2014-05-16 10:22:16 -0700163 onStopDtmfTone();
164 }
165
166 /**
167 * Disconnect this Connection. If and when the Connection can comply with
168 * this request, it will transition to the {@link State#DISCONNECTED}
169 * state and notify its listeners.
170 *
171 * @hide
172 */
173 public final void disconnect() {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700174 Log.d(this, "disconnect");
Ihab Awad542e0ea2014-05-16 10:22:16 -0700175 onDisconnect();
176 }
177
178 /**
179 * Abort this Connection. The Connection will immediately transition to
180 * the {@link State#DISCONNECTED} state, and send no notifications of this
181 * or any other future events.
182 *
183 * @hide
184 */
185 public final void abort() {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700186 Log.d(this, "abort");
Ihab Awad542e0ea2014-05-16 10:22:16 -0700187 onAbort();
188 }
189
190 /**
191 * Place this Connection on hold. If and when the Connection can comply with
192 * this request, it will transition to the {@link State#HOLDING}
193 * state and notify its listeners.
194 *
195 * @hide
196 */
197 public final void hold() {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700198 Log.d(this, "hold");
Ihab Awad542e0ea2014-05-16 10:22:16 -0700199 onHold();
200 }
201
202 /**
203 * Un-hold this Connection. If and when the Connection can comply with
204 * this request, it will transition to the {@link State#ACTIVE}
205 * state and notify its listeners.
206 *
207 * @hide
208 */
209 public final void unhold() {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700210 Log.d(this, "unhold");
Ihab Awad542e0ea2014-05-16 10:22:16 -0700211 onUnhold();
212 }
213
214 /**
215 * Accept a {@link State#RINGING} Connection. If and when the Connection
216 * can comply with this request, it will transition to the {@link State#ACTIVE}
217 * state and notify its listeners.
218 *
219 * @hide
220 */
221 public final void answer() {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700222 Log.d(this, "answer");
Ihab Awad542e0ea2014-05-16 10:22:16 -0700223 if (mState == State.RINGING) {
224 onAnswer();
225 }
226 }
227
228 /**
229 * Reject a {@link State#RINGING} Connection. If and when the Connection
230 * can comply with this request, it will transition to the {@link State#ACTIVE}
231 * state and notify its listeners.
232 *
233 * @hide
234 */
235 public final void reject() {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700236 Log.d(this, "reject");
Ihab Awad542e0ea2014-05-16 10:22:16 -0700237 if (mState == State.RINGING) {
238 onReject();
239 }
240 }
241
242 /**
243 * Inform this Connection that the state of its audio output has been changed externally.
244 *
245 * @param state The new audio state.
246 */
247 public void setAudioState(CallAudioState state) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700248 Log.d(this, "setAudioState %s", state);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700249 onSetAudioState(state);
250 }
251
252 /**
253 * @param state An integer value from {@link State}.
254 * @return A string representation of the value.
255 */
256 public static String stateToString(int state) {
257 switch (state) {
258 case State.NEW:
259 return "NEW";
260 case State.RINGING:
261 return "RINGING";
262 case State.DIALING:
263 return "DIALING";
264 case State.ACTIVE:
265 return "ACTIVE";
266 case State.HOLDING:
267 return "HOLDING";
268 case State.DISCONNECTED:
269 return "DISCONNECTED";
270 default:
Ihab Awad60ac30b2014-05-20 22:32:12 -0700271 Log.wtf(Connection.class, "Unknown state %d", state);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700272 return "UNKNOWN";
273 }
274 }
275
276 /**
Ihab Awadf8358972014-05-28 16:46:42 -0700277 * @return Whether this connection is requesting that the system play a ringback tone
278 * on its behalf.
279 */
280 public boolean isRequestingRingback() {
281 return mRequestingRingback;
282 }
283
284 /**
Ihab Awad542e0ea2014-05-16 10:22:16 -0700285 * Sets the value of the {@link #getHandle()} property and notifies listeners.
286 *
287 * @param handle The new handle.
288 */
289 protected void setHandle(Uri handle) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700290 Log.d(this, "setHandle %s", handle);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700291 // TODO: Enforce super called
292 mHandle = handle;
293 for (Listener l : mListeners) {
294 l.onHandleChanged(this, handle);
295 }
296 }
297
298 /**
299 * Sets state to active (e.g., an ongoing call where two or more parties can actively
300 * communicate).
301 */
302 protected void setActive() {
Ihab Awadf8358972014-05-28 16:46:42 -0700303 setRequestingRingback(false);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700304 setState(State.ACTIVE);
305 }
306
307 /**
308 * Sets state to ringing (e.g., an inbound ringing call).
309 */
310 protected void setRinging() {
311 setState(State.RINGING);
312 }
313
314 /**
315 * Sets state to dialing (e.g., dialing an outbound call).
316 */
317 protected void setDialing() {
318 setState(State.DIALING);
319 }
320
321 /**
322 * Sets state to be on hold.
323 */
324 protected void setOnHold() {
325 setState(State.HOLDING);
326 }
327
328 /**
329 * Sets state to disconnected. This will first notify listeners with an
330 * {@link Listener#onStateChanged(Connection, int)} event, then will fire an
331 * {@link Listener#onDisconnected(Connection, int, String)} event with additional
332 * details.
333 *
334 * @param cause The reason for the disconnection, any of
335 * {@link android.telephony.DisconnectCause}.
336 * @param message Optional call-service-provided message about the disconnect.
337 */
338 protected void setDisconnected(int cause, String message) {
339 setState(State.DISCONNECTED);
Ihab Awad60ac30b2014-05-20 22:32:12 -0700340 Log.d(this, "Disconnected with cause %d message %s", cause, message);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700341 for (Listener l : mListeners) {
342 l.onDisconnected(this, cause, message);
343 }
344 }
345
346 /**
Ihab Awadf8358972014-05-28 16:46:42 -0700347 * Requests that the framework play a ringback tone. This is to be invoked by implementations
348 * that do not play a ringback tone themselves in the call's audio stream.
349 *
350 * @param ringback Whether the ringback tone is to be played.
351 */
352 protected void setRequestingRingback(boolean ringback) {
353 if (mRequestingRingback != ringback) {
354 mRequestingRingback = ringback;
355 for (Listener l : mListeners) {
356 l.onRequestingRingback(this, ringback);
357 }
358 }
359 }
360
361 /**
Ihab Awad542e0ea2014-05-16 10:22:16 -0700362 * Notifies this Connection and listeners that the {@link #getCallAudioState()} property
363 * has a new value.
364 *
365 * @param state The new call audio state.
366 */
367 protected void onSetAudioState(CallAudioState state) {
368 // TODO: Enforce super called
Ihab Awadf8358972014-05-28 16:46:42 -0700369 mCallAudioState = state;
Ihab Awad542e0ea2014-05-16 10:22:16 -0700370 for (Listener l : mListeners) {
371 l.onAudioStateChanged(this, state);
372 }
373 }
374
375 /**
376 * Notifies this Connection and listeners of a change in the current signal levels
377 * for the underlying data transport.
378 *
379 * @param details A {@link android.os.Bundle} containing details of the current level.
380 */
381 protected void onSetSignal(Bundle details) {
382 // TODO: Enforce super called
383 for (Listener l : mListeners) {
384 l.onSignalChanged(this, details);
385 }
386 }
387
388 /**
Ihab Awadf8358972014-05-28 16:46:42 -0700389 * Notifies this Connection of an internal state change. This method is called before the
390 * state is actually changed. Overriding implementations must call
391 * {@code super.onSetState(state)}.
392 *
393 * @param state The new state, a {@link Connection.State} member.
394 */
395 protected void onSetState(int state) {
396 // TODO: Enforce super called
397 this.mState = state;
398 for (Listener l : mListeners) {
399 l.onStateChanged(this, state);
400 }
401 }
402
403 /**
Ihab Awad542e0ea2014-05-16 10:22:16 -0700404 * Notifies this Connection of a request to play a DTMF tone.
405 *
406 * @param c A DTMF character.
407 */
408 protected void onPlayDtmfTone(char c) {}
409
410 /**
411 * Notifies this Connection of a request to stop any currently playing DTMF tones.
412 */
413 protected void onStopDtmfTone() {}
414
415 /**
416 * Notifies this Connection of a request to disconnect.
417 */
418 protected void onDisconnect() {}
419
420 /**
421 * Notifies this Connection of a request to abort.
422 */
423 protected void onAbort() {}
424
425 /**
426 * Notifies this Connection of a request to hold.
427 */
428 protected void onHold() {}
429
430 /**
431 * Notifies this Connection of a request to exit a hold state.
432 */
433 protected void onUnhold() {}
434
435 /**
436 * Notifies this Connection, which is in {@link State#RINGING}, of
437 * a request to accept.
438 */
439 protected void onAnswer() {}
440
441 /**
442 * Notifies this Connection, which is in {@link State#RINGING}, of
443 * a request to reject.
444 */
445 protected void onReject() {}
446
Evan Charlton6dea4ac2014-06-03 14:07:13 -0700447 /**
448 * Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
449 */
450 protected void onPostDialContinue(boolean proceed) {}
451
Ihab Awad542e0ea2014-05-16 10:22:16 -0700452 private void setState(int state) {
Ihab Awad60ac30b2014-05-20 22:32:12 -0700453 Log.d(this, "setState: %s", stateToString(state));
Ihab Awadf8358972014-05-28 16:46:42 -0700454 onSetState(state);
Ihab Awad542e0ea2014-05-16 10:22:16 -0700455 }
456}