blob: b6504eb9dc591cd69094195272388528847273bc [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;
22
23import android.os.AsyncResult;
24import android.os.Handler;
25import android.os.Message;
26import android.util.Log;
27
28import com.android.internal.telephony.Connection;
Santos Cordon995c8162013-07-29 09:22:22 -070029import com.android.services.telephony.common.Call;
Santos Cordon63a84242013-07-23 13:32:52 -070030
31import java.util.ArrayList;
32import java.util.HashMap;
33import java.util.List;
34import java.util.concurrent.atomic.AtomicInteger;
35
36/**
37 * Creates a Call model from Call state and data received from the telephony
38 * layer. The telephony layer maintains 3 conceptual objects: Phone, Call,
39 * Connection.
40 *
41 * Phone represents the radio and there is an implementation per technology
42 * type such as GSMPhone, SipPhone, CDMAPhone, etc. Generally, we will only ever
43 * deal with one instance of this object for the lifetime of this class.
44 *
45 * There are 3 Call instances that exist for the lifetime of this class which
46 * are created by CallTracker. The three are RingingCall, ForegroundCall, and
47 * BackgroundCall.
48 *
49 * A Connection most closely resembles what the layperson would consider a call.
50 * A Connection is created when a user dials and it is "owned" by one of the
51 * three Call instances. Which of the three Calls owns the Connection changes
52 * as the Connection goes between ACTIVE, HOLD, RINGING, and other states.
53 *
54 * This class models a new Call class from Connection objects received from
55 * the telephony layer. We use Connection references as identifiers for a call;
56 * new reference = new call.
57 *
58 * TODO(klp): Create a new Call class to replace the simple call Id ints
59 * being used currently.
60 *
61 * The new Call models are parcellable for transfer via the CallHandlerService
62 * API.
63 */
64public class CallModeler extends Handler {
65
66 private static final String TAG = CallModeler.class.getSimpleName();
67
68 private static final int CALL_ID_START_VALUE = 1;
Santos Cordon63a84242013-07-23 13:32:52 -070069
70 private CallStateMonitor mCallStateMonitor;
Santos Cordon995c8162013-07-29 09:22:22 -070071 private HashMap<Connection, Call> mCallMap = Maps.newHashMap();
Santos Cordon63a84242013-07-23 13:32:52 -070072 private List<Listener> mListeners = Lists.newArrayList();
73 private AtomicInteger mNextCallId = new AtomicInteger(CALL_ID_START_VALUE);
74
75 public CallModeler(CallStateMonitor callStateMonitor) {
76 mCallStateMonitor = callStateMonitor;
77
78 mCallStateMonitor.addListener(this);
79 }
80
81 @Override
82 public void handleMessage(Message msg) {
83 switch(msg.what) {
84 case CallStateMonitor.PHONE_NEW_RINGING_CONNECTION:
85 onNewRingingConnection((AsyncResult) msg.obj);
86 break;
87 case CallStateMonitor.PHONE_DISCONNECT:
88 onDisconnect((AsyncResult) msg.obj);
Santos Cordon995c8162013-07-29 09:22:22 -070089 break;
90 case CallStateMonitor.PHONE_STATE_CHANGED:
91 onPhoneStateChanged((AsyncResult) msg.obj);
92 break;
Santos Cordon63a84242013-07-23 13:32:52 -070093 default:
94 break;
95 }
96 }
97
98 public void addListener(Listener listener) {
99 Preconditions.checkNotNull(listener);
100
101 mListeners.add(listener);
102 }
103
104 private void onNewRingingConnection(AsyncResult r) {
105 final Connection conn = (Connection) r.result;
Santos Cordon995c8162013-07-29 09:22:22 -0700106 final Call call = getCallFromConnection(conn, true);
Santos Cordon63a84242013-07-23 13:32:52 -0700107
Santos Cordon995c8162013-07-29 09:22:22 -0700108 if (call != null) {
109 for (Listener l : mListeners) {
110 l.onNewCall(call);
111 }
Santos Cordon63a84242013-07-23 13:32:52 -0700112 }
113 }
114
115 private void onDisconnect(AsyncResult r) {
116 final Connection conn = (Connection) r.result;
Santos Cordon995c8162013-07-29 09:22:22 -0700117 final Call call = getCallFromConnection(conn, false);
Santos Cordon63a84242013-07-23 13:32:52 -0700118
Santos Cordon995c8162013-07-29 09:22:22 -0700119 if (call != null) {
120 mCallMap.remove(conn);
Santos Cordon63a84242013-07-23 13:32:52 -0700121
122 for (Listener l : mListeners) {
Santos Cordon995c8162013-07-29 09:22:22 -0700123 l.onDisconnect(call);
Santos Cordon63a84242013-07-23 13:32:52 -0700124 }
125 }
126 }
127
Santos Cordon995c8162013-07-29 09:22:22 -0700128 private void onPhoneStateChanged(AsyncResult r) {
129 }
130
Santos Cordon63a84242013-07-23 13:32:52 -0700131 /**
132 * Gets an existing callId for a connection, or creates one
133 * if none exists.
134 */
Santos Cordon995c8162013-07-29 09:22:22 -0700135 private Call getCallFromConnection(Connection conn, boolean createIfMissing) {
136 Call call = null;
Santos Cordon63a84242013-07-23 13:32:52 -0700137
138 // Find the call id or create if missing and requested.
139 if (conn != null) {
Santos Cordon995c8162013-07-29 09:22:22 -0700140 if (mCallMap.containsKey(conn)) {
141 call = mCallMap.get(conn);
Santos Cordon63a84242013-07-23 13:32:52 -0700142 } else if (createIfMissing) {
Santos Cordon995c8162013-07-29 09:22:22 -0700143 int callId;
Santos Cordon63a84242013-07-23 13:32:52 -0700144 int newNextCallId;
145 do {
146 callId = mNextCallId.get();
147
148 // protect against overflow
149 newNextCallId = (callId == Integer.MAX_VALUE ?
150 CALL_ID_START_VALUE : callId + 1);
151
152 // Keep looping if the change was not atomic OR the value is already taken.
153 // The call to containsValue() is linear, however, most devices support a
154 // maximum of 7 connections so it's not expensive.
155 } while (!mNextCallId.compareAndSet(callId, newNextCallId) ||
Santos Cordon995c8162013-07-29 09:22:22 -0700156 mCallMap.containsValue(callId));
Santos Cordon63a84242013-07-23 13:32:52 -0700157
Santos Cordon995c8162013-07-29 09:22:22 -0700158 call = new Call(callId);
159 mCallMap.put(conn, call);
Santos Cordon63a84242013-07-23 13:32:52 -0700160 }
161 }
Santos Cordon995c8162013-07-29 09:22:22 -0700162 return call;
Santos Cordon63a84242013-07-23 13:32:52 -0700163 }
164
165 /**
166 * Listener interface for changes to Calls.
167 */
168 public interface Listener {
Santos Cordon995c8162013-07-29 09:22:22 -0700169 void onNewCall(Call call);
170 void onDisconnect(Call call);
Santos Cordon63a84242013-07-23 13:32:52 -0700171 }
172}