blob: 63879a4f26656d10c775bcb330f4bcb33b543e4d [file] [log] [blame]
Santos Cordon63a84242013-07-23 13:32:52 -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.android.collect.Lists;
20import com.google.android.collect.Maps;
21import com.google.common.base.Preconditions;
Santos Cordone38b1ff2013-08-07 12:12:16 -070022import com.google.common.collect.ImmutableMap;
Santos Cordon63a84242013-07-23 13:32:52 -070023
24import android.os.AsyncResult;
25import android.os.Handler;
26import android.os.Message;
Santos Cordone38b1ff2013-08-07 12:12:16 -070027import android.text.TextUtils;
28import android.util.Log;
Santos Cordon63a84242013-07-23 13:32:52 -070029
Santos Cordona3d05142013-07-29 11:25:17 -070030import com.android.internal.telephony.CallManager;
Santos Cordon63a84242013-07-23 13:32:52 -070031import com.android.internal.telephony.Connection;
Santos Cordoneead6ec2013-08-07 22:16:33 -070032import com.android.internal.telephony.Phone;
Santos Cordona3d05142013-07-29 11:25:17 -070033import com.android.internal.telephony.PhoneConstants;
Santos Cordon26e7b242013-08-07 21:15:45 -070034import com.android.internal.telephony.TelephonyCapabilities;
Santos Cordon995c8162013-07-29 09:22:22 -070035import com.android.services.telephony.common.Call;
Santos Cordon26e7b242013-08-07 21:15:45 -070036import com.android.services.telephony.common.Call.Capabilities;
Santos Cordona3d05142013-07-29 11:25:17 -070037import com.android.services.telephony.common.Call.State;
Santos Cordon63a84242013-07-23 13:32:52 -070038
39import java.util.ArrayList;
40import java.util.HashMap;
41import java.util.List;
Santos Cordon249efd02013-08-05 03:33:56 -070042import java.util.Map.Entry;
Santos Cordon63a84242013-07-23 13:32:52 -070043import java.util.concurrent.atomic.AtomicInteger;
44
45/**
46 * Creates a Call model from Call state and data received from the telephony
47 * layer. The telephony layer maintains 3 conceptual objects: Phone, Call,
48 * Connection.
49 *
50 * Phone represents the radio and there is an implementation per technology
51 * type such as GSMPhone, SipPhone, CDMAPhone, etc. Generally, we will only ever
52 * deal with one instance of this object for the lifetime of this class.
53 *
54 * There are 3 Call instances that exist for the lifetime of this class which
55 * are created by CallTracker. The three are RingingCall, ForegroundCall, and
56 * BackgroundCall.
57 *
58 * A Connection most closely resembles what the layperson would consider a call.
59 * A Connection is created when a user dials and it is "owned" by one of the
60 * three Call instances. Which of the three Calls owns the Connection changes
61 * as the Connection goes between ACTIVE, HOLD, RINGING, and other states.
62 *
63 * This class models a new Call class from Connection objects received from
64 * the telephony layer. We use Connection references as identifiers for a call;
65 * new reference = new call.
66 *
67 * TODO(klp): Create a new Call class to replace the simple call Id ints
68 * being used currently.
69 *
70 * The new Call models are parcellable for transfer via the CallHandlerService
71 * API.
72 */
73public class CallModeler extends Handler {
74
75 private static final String TAG = CallModeler.class.getSimpleName();
76
77 private static final int CALL_ID_START_VALUE = 1;
Santos Cordon63a84242013-07-23 13:32:52 -070078
Santos Cordon998f42b2013-08-02 16:13:12 -070079 private final CallStateMonitor mCallStateMonitor;
80 private final CallManager mCallManager;
81 private final HashMap<Connection, Call> mCallMap = Maps.newHashMap();
82 private final AtomicInteger mNextCallId = new AtomicInteger(CALL_ID_START_VALUE);
Christine Chendaf7bf62013-08-05 19:12:31 -070083 private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
Christine Chenee09a492013-08-06 16:02:29 -070084 private RejectWithTextMessageManager mRejectWithTextMessageManager;
Santos Cordon63a84242013-07-23 13:32:52 -070085
Christine Chenee09a492013-08-06 16:02:29 -070086 public CallModeler(CallStateMonitor callStateMonitor, CallManager callManager,
87 RejectWithTextMessageManager rejectWithTextMessageManager) {
Santos Cordon63a84242013-07-23 13:32:52 -070088 mCallStateMonitor = callStateMonitor;
Santos Cordona3d05142013-07-29 11:25:17 -070089 mCallManager = callManager;
Christine Chenee09a492013-08-06 16:02:29 -070090 mRejectWithTextMessageManager = rejectWithTextMessageManager;
Santos Cordon63a84242013-07-23 13:32:52 -070091
92 mCallStateMonitor.addListener(this);
93 }
94
Santos Cordone38b1ff2013-08-07 12:12:16 -070095 //@Override
Santos Cordon63a84242013-07-23 13:32:52 -070096 public void handleMessage(Message msg) {
97 switch(msg.what) {
98 case CallStateMonitor.PHONE_NEW_RINGING_CONNECTION:
99 onNewRingingConnection((AsyncResult) msg.obj);
100 break;
101 case CallStateMonitor.PHONE_DISCONNECT:
102 onDisconnect((AsyncResult) msg.obj);
Santos Cordon995c8162013-07-29 09:22:22 -0700103 break;
104 case CallStateMonitor.PHONE_STATE_CHANGED:
105 onPhoneStateChanged((AsyncResult) msg.obj);
106 break;
Santos Cordon63a84242013-07-23 13:32:52 -0700107 default:
108 break;
109 }
110 }
111
Christine Chendaf7bf62013-08-05 19:12:31 -0700112 public void addListener(Listener listener) {
Santos Cordon63a84242013-07-23 13:32:52 -0700113 Preconditions.checkNotNull(listener);
Christine Chendaf7bf62013-08-05 19:12:31 -0700114 Preconditions.checkNotNull(mListeners);
Christine Chen4748abd2013-08-07 15:44:15 -0700115 if (!mListeners.contains(listener)) {
116 mListeners.add(listener);
117 }
Santos Cordon998f42b2013-08-02 16:13:12 -0700118 }
119
120 public List<Call> getFullList() {
121 final List<Call> retval = Lists.newArrayList();
122 doUpdate(true, retval);
123 return retval;
Santos Cordon63a84242013-07-23 13:32:52 -0700124 }
125
Santos Cordon249efd02013-08-05 03:33:56 -0700126 public CallResult getCallWithId(int callId) {
127 // max 8 connections, so this should be fast even through we are traversing the entire map.
128 for (Entry<Connection, Call> entry : mCallMap.entrySet()) {
129 if (entry.getValue().getCallId() == callId) {
130 return new CallResult(entry.getValue(), entry.getKey());
131 }
132 }
133 return null;
134 }
135
Santos Cordon2eaff902013-08-05 04:37:55 -0700136 public boolean hasOutstandingActiveCall() {
137 for (Call call : mCallMap.values()) {
138 int state = call.getState();
139 if (Call.State.INVALID != state &&
140 Call.State.IDLE != state &&
141 Call.State.INCOMING != state) {
142 return true;
143 }
144 }
145
146 return false;
147 }
148
Santos Cordon63a84242013-07-23 13:32:52 -0700149 private void onNewRingingConnection(AsyncResult r) {
150 final Connection conn = (Connection) r.result;
Santos Cordon995c8162013-07-29 09:22:22 -0700151 final Call call = getCallFromConnection(conn, true);
Santos Cordone38b1ff2013-08-07 12:12:16 -0700152
153 updateCallFromConnection(call, conn);
Santos Cordona3d05142013-07-29 11:25:17 -0700154 call.setState(Call.State.INCOMING);
Santos Cordon63a84242013-07-23 13:32:52 -0700155
Christine Chendaf7bf62013-08-05 19:12:31 -0700156 for (int i = 0; i < mListeners.size(); ++i) {
Christine Chenee09a492013-08-06 16:02:29 -0700157 if (call != null) {
158 mListeners.get(i).onIncoming(call,
159 mRejectWithTextMessageManager.loadCannedResponses());
160 }
Santos Cordon63a84242013-07-23 13:32:52 -0700161 }
162 }
163
164 private void onDisconnect(AsyncResult r) {
165 final Connection conn = (Connection) r.result;
Santos Cordon995c8162013-07-29 09:22:22 -0700166 final Call call = getCallFromConnection(conn, false);
Santos Cordone38b1ff2013-08-07 12:12:16 -0700167
168 updateCallFromConnection(call, conn);
169 call.setState(Call.State.DISCONNECTED);
Santos Cordon63a84242013-07-23 13:32:52 -0700170
Santos Cordon995c8162013-07-29 09:22:22 -0700171 if (call != null) {
172 mCallMap.remove(conn);
Santos Cordon63a84242013-07-23 13:32:52 -0700173
Christine Chendaf7bf62013-08-05 19:12:31 -0700174 for (int i = 0; i < mListeners.size(); ++i) {
175 mListeners.get(i).onDisconnect(call);
Santos Cordon63a84242013-07-23 13:32:52 -0700176 }
177 }
178 }
179
Santos Cordona3d05142013-07-29 11:25:17 -0700180 /**
181 * Called when the phone state changes.
Santos Cordona3d05142013-07-29 11:25:17 -0700182 */
Santos Cordon995c8162013-07-29 09:22:22 -0700183 private void onPhoneStateChanged(AsyncResult r) {
Santos Cordon998f42b2013-08-02 16:13:12 -0700184 final List<Call> updatedCalls = Lists.newArrayList();
185 doUpdate(false, updatedCalls);
186
Christine Chendaf7bf62013-08-05 19:12:31 -0700187 for (int i = 0; i < mListeners.size(); ++i) {
188 mListeners.get(i).onUpdate(updatedCalls, false);
Santos Cordon998f42b2013-08-02 16:13:12 -0700189 }
190 }
191
192
193 /**
194 * Go through the Calls from CallManager and return the list of calls that were updated.
195 * Or, the full list if requested.
196 */
197 private void doUpdate(boolean fullUpdate, List<Call> out) {
Santos Cordona3d05142013-07-29 11:25:17 -0700198 final List<com.android.internal.telephony.Call> telephonyCalls = Lists.newArrayList();
199 telephonyCalls.addAll(mCallManager.getRingingCalls());
200 telephonyCalls.addAll(mCallManager.getForegroundCalls());
201 telephonyCalls.addAll(mCallManager.getBackgroundCalls());
202
Santos Cordona3d05142013-07-29 11:25:17 -0700203 // Cycle through all the Connections on all the Calls. Update our Call objects
204 // to reflect any new state and send the updated Call objects to the handler service.
205 for (com.android.internal.telephony.Call telephonyCall : telephonyCalls) {
Santos Cordona3d05142013-07-29 11:25:17 -0700206
207 for (Connection connection : telephonyCall.getConnections()) {
Santos Cordon998f42b2013-08-02 16:13:12 -0700208 // new connections return a Call with INVALID state, which does not translate to
Santos Cordone38b1ff2013-08-07 12:12:16 -0700209 // a state in the internal.telephony.Call object. This ensures that staleness
210 // check below fails and we always add the item to the update list if it is new.
Santos Cordona3d05142013-07-29 11:25:17 -0700211 final Call call = getCallFromConnection(connection, true);
212
Santos Cordone38b1ff2013-08-07 12:12:16 -0700213 boolean changed = updateCallFromConnection(call, connection);
214
215 if (fullUpdate || changed) {
Santos Cordon998f42b2013-08-02 16:13:12 -0700216 out.add(call);
Santos Cordona3d05142013-07-29 11:25:17 -0700217 }
218 }
219 }
Santos Cordona3d05142013-07-29 11:25:17 -0700220 }
221
Santos Cordone38b1ff2013-08-07 12:12:16 -0700222 /**
223 * Updates the Call properties to match the state of the connection object
224 * that it represents.
225 */
226 private boolean updateCallFromConnection(Call call, Connection connection) {
227 boolean changed = false;
228
229 com.android.internal.telephony.Call telephonyCall = connection.getCall();
230 final int newState = translateStateFromTelephony(telephonyCall.getState());
231
232 if (call.getState() != newState) {
233 call.setState(newState);
234 changed = true;
235 }
236
237 final String oldNumber = call.getNumber();
238 if (TextUtils.isEmpty(oldNumber) || !oldNumber.equals(connection.getAddress())) {
239 call.setNumber(connection.getAddress());
240 changed = true;
241 }
242
243 final Call.DisconnectCause newDisconnectCause =
244 translateDisconnectCauseFromTelephony(connection.getDisconnectCause());
245 if (call.getDisconnectCause() != newDisconnectCause) {
246 call.setDisconnectCause(newDisconnectCause);
247 changed = true;
248 }
249
250 final int newNumberPresentation = connection.getNumberPresentation();
251 if (call.getNumberPresentation() != newNumberPresentation) {
252 call.setNumberPresentation(newNumberPresentation);
253 changed = true;
254 }
255
256 final int newCnapNamePresentation = connection.getCnapNamePresentation();
257 if (call.getCnapNamePresentation() != newCnapNamePresentation) {
258 call.setCnapNamePresentation(newCnapNamePresentation);
259 changed = true;
260 }
261
262 final String oldCnapName = call.getCnapName();
263 if (TextUtils.isEmpty(oldCnapName) || !oldCnapName.equals(connection.getCnapName())) {
264 call.setCnapName(connection.getCnapName());
265 changed = true;
266 }
267
Santos Cordonbbe8ecf2013-08-13 15:26:18 -0700268 final long oldConnectTime = call.getConnectTime();
269 if (oldConnectTime != connection.getConnectTime()) {
270 call.setConnectTime(connection.getConnectTime());
271 changed = true;
272 }
273
Santos Cordoneead6ec2013-08-07 22:16:33 -0700274 /**
275 * !!! Uses values from connection and call collected above so this part must be last !!!
276 */
277 final int newCapabilities = getCapabilitiesFor(connection, call);
Santos Cordon26e7b242013-08-07 21:15:45 -0700278 if (call.getCapabilities() != newCapabilities) {
279 call.setCapabilities(newCapabilities);
280 changed = true;
281 }
282
Santos Cordone38b1ff2013-08-07 12:12:16 -0700283 return changed;
284 }
285
Santos Cordon26e7b242013-08-07 21:15:45 -0700286 /**
287 * Returns a mask of capabilities for the connection such as merge, hold, etc.
288 */
Santos Cordoneead6ec2013-08-07 22:16:33 -0700289 private int getCapabilitiesFor(Connection connection, Call call) {
290 final boolean callIsActive = (call.getState() == Call.State.ACTIVE);
291 final Phone phone = connection.getCall().getPhone();
292
293 final boolean canHold = TelephonyCapabilities.supportsAnswerAndHold(phone);
294 boolean canAddCall = false;
295 boolean canMergeCall = false;
296 boolean canSwapCall = false;
297
298 // only applies to active calls
299 if (callIsActive) {
300 canAddCall = PhoneUtils.okToAddCall(mCallManager);
301 canMergeCall = PhoneUtils.okToMergeCalls(mCallManager);
302 canSwapCall = PhoneUtils.okToSwapCalls(mCallManager);
303 }
304
305 // special rules section!
306 // CDMA always has Add
307 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
308 canAddCall = true;
309 } else {
310 // if neither merge nor add is on...then allow add
311 canAddCall |= !(canAddCall || canMergeCall);
312 }
313
Santos Cordon26e7b242013-08-07 21:15:45 -0700314 int retval = 0x0;
Santos Cordoneead6ec2013-08-07 22:16:33 -0700315 if (canHold) {
Santos Cordon26e7b242013-08-07 21:15:45 -0700316 retval |= Capabilities.HOLD;
317 }
Santos Cordoneead6ec2013-08-07 22:16:33 -0700318 if (canAddCall) {
319 retval |= Capabilities.ADD_CALL;
320 }
321 if (canMergeCall) {
322 retval |= Capabilities.MERGE_CALLS;
323 }
324 if (canSwapCall) {
325 retval |= Capabilities.SWAP_CALLS;
326 }
Santos Cordon26e7b242013-08-07 21:15:45 -0700327
328 return retval;
329 }
330
Santos Cordona3d05142013-07-29 11:25:17 -0700331 private int translateStateFromTelephony(com.android.internal.telephony.Call.State teleState) {
332 int retval = State.IDLE;
333 switch (teleState) {
334 case ACTIVE:
335 retval = State.ACTIVE;
336 break;
337 case INCOMING:
338 retval = State.INCOMING;
339 break;
340 case DIALING:
341 case ALERTING:
342 retval = State.DIALING;
343 break;
344 case WAITING:
345 retval = State.CALL_WAITING;
346 break;
347 case HOLDING:
348 retval = State.ONHOLD;
349 break;
Santos Cordone38b1ff2013-08-07 12:12:16 -0700350 case DISCONNECTED:
351 case DISCONNECTING:
352 retval = State.DISCONNECTED;
Santos Cordona3d05142013-07-29 11:25:17 -0700353 default:
354 }
355
356 return retval;
Santos Cordon995c8162013-07-29 09:22:22 -0700357 }
358
Santos Cordone38b1ff2013-08-07 12:12:16 -0700359 private final ImmutableMap<Connection.DisconnectCause, Call.DisconnectCause> CAUSE_MAP =
360 ImmutableMap.<Connection.DisconnectCause, Call.DisconnectCause>builder()
361 .put(Connection.DisconnectCause.BUSY, Call.DisconnectCause.BUSY)
362 .put(Connection.DisconnectCause.CALL_BARRED, Call.DisconnectCause.CALL_BARRED)
363 .put(Connection.DisconnectCause.CDMA_ACCESS_BLOCKED,
364 Call.DisconnectCause.CDMA_ACCESS_BLOCKED)
365 .put(Connection.DisconnectCause.CDMA_ACCESS_FAILURE,
366 Call.DisconnectCause.CDMA_ACCESS_FAILURE)
367 .put(Connection.DisconnectCause.CDMA_DROP, Call.DisconnectCause.CDMA_DROP)
368 .put(Connection.DisconnectCause.CDMA_INTERCEPT, Call.DisconnectCause.CDMA_INTERCEPT)
369 .put(Connection.DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE,
370 Call.DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE)
371 .put(Connection.DisconnectCause.CDMA_NOT_EMERGENCY,
372 Call.DisconnectCause.CDMA_NOT_EMERGENCY)
373 .put(Connection.DisconnectCause.CDMA_PREEMPTED, Call.DisconnectCause.CDMA_PREEMPTED)
374 .put(Connection.DisconnectCause.CDMA_REORDER, Call.DisconnectCause.CDMA_REORDER)
375 .put(Connection.DisconnectCause.CDMA_RETRY_ORDER,
376 Call.DisconnectCause.CDMA_RETRY_ORDER)
377 .put(Connection.DisconnectCause.CDMA_SO_REJECT, Call.DisconnectCause.CDMA_SO_REJECT)
378 .put(Connection.DisconnectCause.CONGESTION, Call.DisconnectCause.CONGESTION)
379 .put(Connection.DisconnectCause.CS_RESTRICTED, Call.DisconnectCause.CS_RESTRICTED)
380 .put(Connection.DisconnectCause.CS_RESTRICTED_EMERGENCY,
381 Call.DisconnectCause.CS_RESTRICTED_EMERGENCY)
382 .put(Connection.DisconnectCause.CS_RESTRICTED_NORMAL,
383 Call.DisconnectCause.CS_RESTRICTED_NORMAL)
384 .put(Connection.DisconnectCause.ERROR_UNSPECIFIED,
385 Call.DisconnectCause.ERROR_UNSPECIFIED)
386 .put(Connection.DisconnectCause.FDN_BLOCKED, Call.DisconnectCause.FDN_BLOCKED)
387 .put(Connection.DisconnectCause.ICC_ERROR, Call.DisconnectCause.ICC_ERROR)
388 .put(Connection.DisconnectCause.INCOMING_MISSED,
389 Call.DisconnectCause.INCOMING_MISSED)
390 .put(Connection.DisconnectCause.INCOMING_REJECTED,
391 Call.DisconnectCause.INCOMING_REJECTED)
392 .put(Connection.DisconnectCause.INVALID_CREDENTIALS,
393 Call.DisconnectCause.INVALID_CREDENTIALS)
394 .put(Connection.DisconnectCause.INVALID_NUMBER,
395 Call.DisconnectCause.INVALID_NUMBER)
396 .put(Connection.DisconnectCause.LIMIT_EXCEEDED, Call.DisconnectCause.LIMIT_EXCEEDED)
397 .put(Connection.DisconnectCause.LOCAL, Call.DisconnectCause.LOCAL)
398 .put(Connection.DisconnectCause.LOST_SIGNAL, Call.DisconnectCause.LOST_SIGNAL)
399 .put(Connection.DisconnectCause.MMI, Call.DisconnectCause.MMI)
400 .put(Connection.DisconnectCause.NORMAL, Call.DisconnectCause.NORMAL)
401 .put(Connection.DisconnectCause.NOT_DISCONNECTED,
402 Call.DisconnectCause.NOT_DISCONNECTED)
403 .put(Connection.DisconnectCause.NUMBER_UNREACHABLE,
404 Call.DisconnectCause.NUMBER_UNREACHABLE)
405 .put(Connection.DisconnectCause.OUT_OF_NETWORK, Call.DisconnectCause.OUT_OF_NETWORK)
406 .put(Connection.DisconnectCause.OUT_OF_SERVICE, Call.DisconnectCause.OUT_OF_SERVICE)
407 .put(Connection.DisconnectCause.POWER_OFF, Call.DisconnectCause.POWER_OFF)
408 .put(Connection.DisconnectCause.SERVER_ERROR, Call.DisconnectCause.SERVER_ERROR)
409 .put(Connection.DisconnectCause.SERVER_UNREACHABLE,
410 Call.DisconnectCause.SERVER_UNREACHABLE)
411 .put(Connection.DisconnectCause.TIMED_OUT, Call.DisconnectCause.TIMED_OUT)
412 .put(Connection.DisconnectCause.UNOBTAINABLE_NUMBER,
413 Call.DisconnectCause.UNOBTAINABLE_NUMBER)
414 .build();
415
416 private Call.DisconnectCause translateDisconnectCauseFromTelephony(
417 Connection.DisconnectCause causeSource) {
418
419 if (CAUSE_MAP.containsKey(causeSource)) {
420 return CAUSE_MAP.get(causeSource);
421 }
422
423 return Call.DisconnectCause.UNKNOWN;
424 }
425
Santos Cordon63a84242013-07-23 13:32:52 -0700426 /**
Santos Cordone38b1ff2013-08-07 12:12:16 -0700427 * Gets an existing callId for a connection, or creates one if none exists.
428 * This function does NOT set any of the Connection data onto the Call class.
429 * A separate call to updateCallFromConnection must be made for that purpose.
Santos Cordon63a84242013-07-23 13:32:52 -0700430 */
Santos Cordon995c8162013-07-29 09:22:22 -0700431 private Call getCallFromConnection(Connection conn, boolean createIfMissing) {
432 Call call = null;
Santos Cordon63a84242013-07-23 13:32:52 -0700433
434 // Find the call id or create if missing and requested.
435 if (conn != null) {
Santos Cordon995c8162013-07-29 09:22:22 -0700436 if (mCallMap.containsKey(conn)) {
437 call = mCallMap.get(conn);
Santos Cordon63a84242013-07-23 13:32:52 -0700438 } else if (createIfMissing) {
Santos Cordon995c8162013-07-29 09:22:22 -0700439 int callId;
Santos Cordon63a84242013-07-23 13:32:52 -0700440 int newNextCallId;
441 do {
442 callId = mNextCallId.get();
443
444 // protect against overflow
445 newNextCallId = (callId == Integer.MAX_VALUE ?
446 CALL_ID_START_VALUE : callId + 1);
447
448 // Keep looping if the change was not atomic OR the value is already taken.
449 // The call to containsValue() is linear, however, most devices support a
450 // maximum of 7 connections so it's not expensive.
451 } while (!mNextCallId.compareAndSet(callId, newNextCallId) ||
Santos Cordon995c8162013-07-29 09:22:22 -0700452 mCallMap.containsValue(callId));
Santos Cordon63a84242013-07-23 13:32:52 -0700453
Santos Cordon995c8162013-07-29 09:22:22 -0700454 call = new Call(callId);
Santos Cordone38b1ff2013-08-07 12:12:16 -0700455
Santos Cordon995c8162013-07-29 09:22:22 -0700456 mCallMap.put(conn, call);
Santos Cordon63a84242013-07-23 13:32:52 -0700457 }
458 }
Santos Cordon995c8162013-07-29 09:22:22 -0700459 return call;
Santos Cordon63a84242013-07-23 13:32:52 -0700460 }
461
462 /**
463 * Listener interface for changes to Calls.
464 */
465 public interface Listener {
Santos Cordon995c8162013-07-29 09:22:22 -0700466 void onDisconnect(Call call);
Christine Chenee09a492013-08-06 16:02:29 -0700467 void onIncoming(Call call, ArrayList<String> textReponses);
Santos Cordon998f42b2013-08-02 16:13:12 -0700468 void onUpdate(List<Call> calls, boolean fullUpdate);
Santos Cordon63a84242013-07-23 13:32:52 -0700469 }
Santos Cordon249efd02013-08-05 03:33:56 -0700470
471 /**
472 * Result class for accessing a call by connection.
473 */
474 public static class CallResult {
475 public Call mCall;
476 public Connection mConnection;
477
478 private CallResult(Call call, Connection connection) {
479 mCall = call;
480 mConnection = connection;
481 }
482
483 public Call getCall() {
484 return mCall;
485 }
486
487 public Connection getConnection() {
488 return mConnection;
489 }
490 }
Santos Cordon63a84242013-07-23 13:32:52 -0700491}