blob: 5b8d25d4d6aea875a1909bdaa02953f942444a33 [file] [log] [blame]
Santos Cordon8e8b8d22013-12-19 14:14:05 -08001/*
2 * Copyright 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
Ben Gilad9f2bed32013-12-12 17:43:26 -080017package com.android.telecomm;
18
Santos Cordon5b7b9b32014-03-26 14:00:22 -070019import android.content.ComponentName;
Evan Charltona05805b2014-03-05 08:21:46 -080020import android.os.Bundle;
Ben Gilad2313e622014-02-06 12:02:25 -080021import android.os.Handler;
Santos Cordonc499c1c2014-04-14 17:13:14 -070022import android.os.Message;
Sailesh Nepal810735e2014-03-18 18:15:46 -070023import android.telecomm.CallInfo;
Ben Giladc5b22692014-02-18 20:03:22 -080024import android.telecomm.CallServiceDescriptor;
Evan Charltona05805b2014-03-05 08:21:46 -080025import android.telecomm.TelecommConstants;
Santos Cordon8e8b8d22013-12-19 14:14:05 -080026
Santos Cordon74d420b2014-05-07 14:38:47 -070027import com.android.telecomm.BaseRepository.LookupCallback;
Santos Cordon5b7b9b32014-03-26 14:00:22 -070028import com.google.common.base.Preconditions;
29import com.google.common.collect.ImmutableCollection;
30import com.google.common.collect.ImmutableList;
31import com.google.common.collect.Sets;
32
33import java.util.Collection;
Ben Gilad0407fb22014-01-09 16:18:41 -080034import java.util.Set;
Ben Gilad9f2bed32013-12-12 17:43:26 -080035
Santos Cordon8e8b8d22013-12-19 14:14:05 -080036/**
Sailesh Nepal18386a82014-03-19 10:22:40 -070037 * Switchboard is responsible for:
Santos Cordon5b7b9b32014-03-26 14:00:22 -070038 * - gathering the {@link CallServiceWrapper}s and {@link CallServiceSelectorWrapper}s through
39 * which to place outgoing calls
40 * - starting outgoing calls (via {@link OutgoingCallsManager}
Santos Cordon8e8b8d22013-12-19 14:14:05 -080041 */
Ben Giladd17443c2014-01-06 11:04:15 -080042final class Switchboard {
Santos Cordon74d420b2014-05-07 14:38:47 -070043 /**
44 * Encapsulates a request to place an outgoing call.
45 * TODO(santoscordon): Move this state into Call and remove this class.
46 */
47 private final class OutgoingCallEntry {
48 final Call call;
49
50 private Collection<CallServiceWrapper> mCallServices;
51 private Collection<CallServiceSelectorWrapper> mSelectors;
52 private boolean mIsCallPending = true;
53
54 OutgoingCallEntry(Call call) {
55 this.call = call;
56 }
57
58 /**
59 * Sets the call services to attempt for this outgoing call.
60 *
61 * @param callServices The call services.
62 */
63 void setCallServices(Collection<CallServiceWrapper> callServices) {
64 mCallServices = callServices;
65 onLookupComplete();
66 }
67
68 Collection<CallServiceWrapper> getCallServices() {
69 return mCallServices;
70 }
71
72 /**
73 * Sets the selectors to attemnpt for this outgoing call.
74 *
75 * @param selectors The call-service selectors.
76 */
77 void setSelectors(Collection<CallServiceSelectorWrapper> selectors) {
78 mSelectors = selectors;
79 onLookupComplete();
80 }
81
82 Collection<CallServiceSelectorWrapper> getSelectors() {
83 return mSelectors;
84 }
85
86 /** Expires the pending outgoing call and stops it from being made. */
87 void expire() {
88 // This can be executed in three states:
89 // 1) We are still waiting for the list of CSs (Call Services)
90 // 2) We told outgoing calls manager to place the call using the CSs
91 // 3) Outgoing calls manager already successfully placed the call.
92 if (mIsCallPending) {
93 // Handle state (1), tell the call to clean itself up and shut everything down.
94 mIsCallPending = false;
95 call.handleFailedOutgoing(true /* isAborted */);
96 } else {
97 // Handle states (2) & (3). We can safely call abort() in either case. If the call
98 // is not yet successful, then it will abort. If the call was already placed, then
99 // outgoing calls manager will do nothing (and return false which we ignore).
100 boolean isAborted = mOutgoingCallsManager.abort(call);
101 Log.v(this, "expire() caused abort: %b", isAborted);
102 }
103 }
104
105 /** Initiates processing of the call once call-services and selectors are set. */
106 private void onLookupComplete() {
107 if (mIsCallPending) {
108 if (mSelectors != null && mCallServices != null) {
109 mIsCallPending = false;
110 processNewOutgoingCall(this);
111 }
112 }
113 }
114 }
115
Santos Cordonc499c1c2014-04-14 17:13:14 -0700116 private final static int MSG_EXPIRE_STALE_CALL = 1;
Ben Gilad2495d572014-01-09 17:26:19 -0800117
Santos Cordon766d04f2014-05-06 10:28:25 -0700118 private final static Switchboard sInstance = new Switchboard();
Ben Gilad2313e622014-02-06 12:02:25 -0800119
Santos Cordon681663d2014-01-30 04:32:15 -0800120 /** Used to place outgoing calls. */
121 private final OutgoingCallsManager mOutgoingCallsManager;
122
Santos Cordon493e8f22014-02-19 03:15:12 -0800123 /** Used to retrieve incoming call details. */
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800124 private final IncomingCallsManager mIncomingCallsManager;
125
Santos Cordonc195e362014-02-11 17:05:31 -0800126 private final CallServiceRepository mCallServiceRepository;
Santos Cordon681663d2014-01-30 04:32:15 -0800127
Santos Cordonc195e362014-02-11 17:05:31 -0800128 private final CallServiceSelectorRepository mSelectorRepository;
Ben Gilad2313e622014-02-06 12:02:25 -0800129
130 /** Used to schedule tasks on the main (UI) thread. */
Santos Cordonc499c1c2014-04-14 17:13:14 -0700131 private final Handler mHandler = new Handler() {
Ben Gilad2313e622014-02-06 12:02:25 -0800132 @Override
Santos Cordonc499c1c2014-04-14 17:13:14 -0700133 public void handleMessage(Message msg) {
134 switch(msg.what) {
135 case MSG_EXPIRE_STALE_CALL:
Santos Cordon74d420b2014-05-07 14:38:47 -0700136 ((OutgoingCallEntry) msg.obj).expire();
Santos Cordonc499c1c2014-04-14 17:13:14 -0700137 break;
138 default:
139 Log.wtf(Switchboard.this, "Unexpected message %d.", msg.what);
Ben Gilad2313e622014-02-06 12:02:25 -0800140 }
141 }
142 };
143
Santos Cordon766d04f2014-05-06 10:28:25 -0700144 /** Singleton accessor. */
145 static Switchboard getInstance() {
146 return sInstance;
147 }
148
Santos Cordonc195e362014-02-11 17:05:31 -0800149 /**
Santos Cordon681663d2014-01-30 04:32:15 -0800150 * Persists the specified parameters and initializes Switchboard.
151 */
Santos Cordon766d04f2014-05-06 10:28:25 -0700152 private Switchboard() {
Santos Cordonc499c1c2014-04-14 17:13:14 -0700153 ThreadUtil.checkOnMainThread();
154
Santos Cordon74d420b2014-05-07 14:38:47 -0700155 mOutgoingCallsManager = new OutgoingCallsManager();
156 mIncomingCallsManager = new IncomingCallsManager();
157 mSelectorRepository = new CallServiceSelectorRepository(mOutgoingCallsManager);
Santos Cordon493e8f22014-02-19 03:15:12 -0800158 mCallServiceRepository =
Santos Cordon74d420b2014-05-07 14:38:47 -0700159 new CallServiceRepository(mOutgoingCallsManager, mIncomingCallsManager);
Santos Cordon681663d2014-01-30 04:32:15 -0800160 }
Ben Giladd17443c2014-01-06 11:04:15 -0800161
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800162 /**
Santos Cordonc195e362014-02-11 17:05:31 -0800163 * Starts the process of placing an outgoing call by searching for available call services
164 * through which the call can be placed. After a lookup for those services completes, execution
165 * returns to {@link #setCallServices} where the process of placing the call continues.
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800166 *
Ben Gilad13329fd2014-02-11 17:20:29 -0800167 * @param call The yet-to-be-connected outgoing-call object.
Santos Cordon8e8b8d22013-12-19 14:14:05 -0800168 */
Santos Cordonc195e362014-02-11 17:05:31 -0800169 void placeOutgoingCall(Call call) {
Santos Cordon74d420b2014-05-07 14:38:47 -0700170 final OutgoingCallEntry callEntry = new OutgoingCallEntry(call);
Ben Giladbb167cd2014-02-25 16:24:05 -0800171
Santos Cordon74d420b2014-05-07 14:38:47 -0700172 // Lookup call services
173 mCallServiceRepository.lookupServices(new LookupCallback<CallServiceWrapper>() {
174 @Override
175 public void onComplete(Collection<CallServiceWrapper> services) {
176 callEntry.setCallServices(services);
177 }
178 });
Ben Giladb59769e2014-01-16 11:41:10 -0800179
Santos Cordon74d420b2014-05-07 14:38:47 -0700180 // Lookup selectors
181 mSelectorRepository.lookupServices(new LookupCallback<CallServiceSelectorWrapper>() {
182 @Override
183 public void onComplete(Collection<CallServiceSelectorWrapper> selectors) {
184 callEntry.setSelectors(selectors);
185 }
186 });
Santos Cordonc499c1c2014-04-14 17:13:14 -0700187
Santos Cordon74d420b2014-05-07 14:38:47 -0700188 Message msg = mHandler.obtainMessage(MSG_EXPIRE_STALE_CALL, callEntry);
Santos Cordonc499c1c2014-04-14 17:13:14 -0700189 mHandler.sendMessageDelayed(msg, Timeouts.getNewOutgoingCallMillis());
Ben Gilad0407fb22014-01-09 16:18:41 -0800190 }
Ben Gilad9f2bed32013-12-12 17:43:26 -0800191
Ben Gilad0407fb22014-01-09 16:18:41 -0800192 /**
Santos Cordon74d420b2014-05-07 14:38:47 -0700193 * Retrieves details about the incoming call through the incoming call manager.
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800194 *
195 * @param call The call object.
Ben Giladc5b22692014-02-18 20:03:22 -0800196 * @param descriptor The relevant call-service descriptor.
Evan Charltona05805b2014-03-05 08:21:46 -0800197 * @param extras The optional extras passed via
Santos Cordon5b7b9b32014-03-26 14:00:22 -0700198 * {@link TelecommConstants#EXTRA_INCOMING_CALL_EXTRAS}
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800199 */
Evan Charltona05805b2014-03-05 08:21:46 -0800200 void retrieveIncomingCall(Call call, CallServiceDescriptor descriptor, Bundle extras) {
Sailesh Nepalf1c191d2014-03-07 18:17:39 -0800201 Log.d(this, "retrieveIncomingCall");
Santos Cordon74d420b2014-05-07 14:38:47 -0700202 CallServiceWrapper callService = mCallServiceRepository.getService(descriptor);
Santos Cordon493e8f22014-02-19 03:15:12 -0800203 call.setCallService(callService);
Evan Charltona05805b2014-03-05 08:21:46 -0800204 mIncomingCallsManager.retrieveIncomingCall(call, extras);
Santos Cordon80d9bdc2014-02-13 18:28:46 -0800205 }
206
207 /**
Ihab Awad4e0f1922014-04-14 17:35:38 -0700208 * Ensures any state regarding a call is cleaned up.
209 *
210 * @param call The call.
211 */
212 void abortCall(Call call) {
213 Log.d(this, "abortCall");
214 mOutgoingCallsManager.abort(call);
215 }
216
217 /**
Ben Giladb59769e2014-01-16 11:41:10 -0800218 * Attempts to place the specified call.
219 *
Santos Cordon74d420b2014-05-07 14:38:47 -0700220 * @param callEntry The call entry to place.
Ben Giladb59769e2014-01-16 11:41:10 -0800221 */
Santos Cordon74d420b2014-05-07 14:38:47 -0700222 private void processNewOutgoingCall(OutgoingCallEntry callEntry) {
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700223 Collection<CallServiceSelectorWrapper> selectors;
Santos Cordon74d420b2014-05-07 14:38:47 -0700224 Call call = callEntry.call;
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700225
226 // Use the call's selector if it's already tied to one. This is the case for handoff calls.
227 if (call.getCallServiceSelector() != null) {
228 selectors = ImmutableList.of(call.getCallServiceSelector());
229 } else {
Santos Cordon74d420b2014-05-07 14:38:47 -0700230 selectors = callEntry.getSelectors();
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700231 }
Santos Cordon5b7b9b32014-03-26 14:00:22 -0700232
233 boolean useEmergencySelector =
234 EmergencyCallServiceSelector.shouldUseSelector(call.getHandle());
235 Log.d(this, "processNewOutgoingCall, isEmergency=%b", useEmergencySelector);
236
237 if (useEmergencySelector) {
238 // This is potentially an emergency call so add the emergency selector before the
239 // other selectors.
240 ImmutableList.Builder<CallServiceSelectorWrapper> selectorsBuilder =
241 ImmutableList.builder();
242
243 ComponentName componentName = new ComponentName(
244 TelecommApp.getInstance(), EmergencyCallServiceSelector.class);
245 CallServiceSelectorWrapper emergencySelector =
246 new CallServiceSelectorWrapper(
247 componentName.flattenToShortString(),
248 componentName,
Santos Cordon766d04f2014-05-06 10:28:25 -0700249 CallsManager.getInstance(),
Santos Cordon5b7b9b32014-03-26 14:00:22 -0700250 mOutgoingCallsManager);
251
252 selectorsBuilder.add(emergencySelector);
Sailesh Nepal84fa5f82014-04-02 11:01:11 -0700253 selectorsBuilder.addAll(selectors);
Santos Cordon5b7b9b32014-03-26 14:00:22 -0700254 selectors = selectorsBuilder.build();
255 }
256
Santos Cordon74d420b2014-05-07 14:38:47 -0700257 mOutgoingCallsManager.placeCall(call, callEntry.getCallServices(), selectors);
Ben Gilad8e55d1d2014-02-26 16:25:56 -0800258 }
Ben Gilad9f2bed32013-12-12 17:43:26 -0800259}