blob: 6670ae4fc3f89a46f5355559f65fa382d33710a4 [file] [log] [blame]
Santos Cordon89647a62013-07-16 13:38:09 -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
Yorke Lee79840002013-09-16 14:30:56 -070019import android.Manifest;
Santos Cordon89647a62013-07-16 13:38:09 -070020import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.content.ServiceConnection;
Chiao Cheng11a4b652013-09-02 01:08:19 -070024import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
Yorke Lee79840002013-09-16 14:30:56 -070026import android.content.pm.ServiceInfo;
Santos Cordon89647a62013-07-16 13:38:09 -070027import android.os.Handler;
28import android.os.IBinder;
29import android.os.Message;
Chiao Cheng26c6e922013-09-14 16:45:38 -070030import android.os.PowerManager;
Santos Cordon89647a62013-07-16 13:38:09 -070031import android.os.RemoteException;
Chiao Cheng26c6e922013-09-14 16:45:38 -070032import android.os.SystemClock;
Santos Cordon89647a62013-07-16 13:38:09 -070033import android.os.SystemProperties;
Yorke Leede41f672013-09-19 13:46:55 -070034import android.text.TextUtils;
Santos Cordon89647a62013-07-16 13:38:09 -070035import android.util.Log;
36
Yorke Leede41f672013-09-19 13:46:55 -070037import com.android.internal.telephony.Connection;
38import com.android.internal.telephony.Connection.PostDialState;
Santos Cordon9b7bac72013-08-06 08:04:52 -070039import com.android.phone.AudioRouter.AudioModeListener;
Yorke Lee362cec22013-09-18 15:20:26 -070040import com.android.phone.NotificationMgr.StatusBarHelper;
Santos Cordon9b7bac72013-08-06 08:04:52 -070041import com.android.services.telephony.common.AudioMode;
Santos Cordonf4046882013-07-25 18:49:27 -070042import com.android.services.telephony.common.Call;
Santos Cordon345350e2013-07-19 17:16:14 -070043import com.android.services.telephony.common.ICallHandlerService;
Chiao Cheng6c6b2722013-08-22 18:35:54 -070044import com.google.common.collect.Lists;
Santos Cordon89647a62013-07-16 13:38:09 -070045
Santos Cordona3d05142013-07-29 11:25:17 -070046import java.util.List;
47
Santos Cordon89647a62013-07-16 13:38:09 -070048/**
Santos Cordon345350e2013-07-19 17:16:14 -070049 * This class is responsible for passing through call state changes to the CallHandlerService.
Santos Cordon89647a62013-07-16 13:38:09 -070050 */
Chiao Cheng6c6b2722013-08-22 18:35:54 -070051public class CallHandlerServiceProxy extends Handler
52 implements CallModeler.Listener, AudioModeListener {
Santos Cordon89647a62013-07-16 13:38:09 -070053
Santos Cordon345350e2013-07-19 17:16:14 -070054 private static final String TAG = CallHandlerServiceProxy.class.getSimpleName();
Santos Cordon71d5c6e2013-09-05 21:34:33 -070055 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt(
Chiao Cheng6c6b2722013-08-22 18:35:54 -070056 "ro.debuggable", 0) == 1);
Chiao Chenge41661c2013-07-23 13:28:26 -070057
Chiao Cheng6c6b2722013-08-22 18:35:54 -070058 public static final int RETRY_DELAY_MILLIS = 2000;
59 private static final int BIND_RETRY_MSG = 1;
60 private static final int MAX_RETRY_COUNT = 5;
Santos Cordon89647a62013-07-16 13:38:09 -070061
Santos Cordon593ab382013-08-06 21:58:23 -070062 private AudioRouter mAudioRouter;
Santos Cordoncba1b442013-07-18 12:43:58 -070063 private CallCommandService mCallCommandService;
Santos Cordon593ab382013-08-06 21:58:23 -070064 private CallModeler mCallModeler;
65 private Context mContext;
Santos Cordonf9ad0642013-09-26 10:48:07 -070066 private boolean mFullUpdateOnConnect;
Chiao Chengd38eebc2013-08-28 14:38:14 -070067
Chiao Cheng6c6b2722013-08-22 18:35:54 -070068 private ICallHandlerService mCallHandlerServiceGuarded; // Guarded by mServiceAndQueueLock
Chiao Chengd38eebc2013-08-28 14:38:14 -070069 // Single queue to guarantee ordering
70 private List<QueueParams> mQueue; // Guarded by mServiceAndQueueLock
71
Chiao Cheng6c6b2722013-08-22 18:35:54 -070072 private final Object mServiceAndQueueLock = new Object();
73 private int mBindRetryCount = 0;
74
75 @Override
76 public void handleMessage(Message msg) {
77 super.handleMessage(msg);
78
79 switch (msg.what) {
80 case BIND_RETRY_MSG:
81 setupServiceConnection();
82 break;
83 }
84 }
Santos Cordon89647a62013-07-16 13:38:09 -070085
Santos Cordon63a84242013-07-23 13:32:52 -070086 public CallHandlerServiceProxy(Context context, CallModeler callModeler,
Santos Cordon593ab382013-08-06 21:58:23 -070087 CallCommandService callCommandService, AudioRouter audioRouter) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -070088 if (DBG) {
89 Log.d(TAG, "init CallHandlerServiceProxy");
90 }
Santos Cordon89647a62013-07-16 13:38:09 -070091 mContext = context;
Santos Cordoncba1b442013-07-18 12:43:58 -070092 mCallCommandService = callCommandService;
Santos Cordon63a84242013-07-23 13:32:52 -070093 mCallModeler = callModeler;
Santos Cordon593ab382013-08-06 21:58:23 -070094 mAudioRouter = audioRouter;
Santos Cordon89647a62013-07-16 13:38:09 -070095
Santos Cordon593ab382013-08-06 21:58:23 -070096 mAudioRouter.addAudioModeListener(this);
Christine Chendaf7bf62013-08-05 19:12:31 -070097 mCallModeler.addListener(this);
Santos Cordon63a84242013-07-23 13:32:52 -070098 }
Santos Cordon89647a62013-07-16 13:38:09 -070099
Santos Cordon63a84242013-07-23 13:32:52 -0700100 @Override
Santos Cordon995c8162013-07-29 09:22:22 -0700101 public void onDisconnect(Call call) {
Chiao Cheng26c6e922013-09-14 16:45:38 -0700102 // Wake up in case the screen was off.
103 wakeUpScreen();
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700104 synchronized (mServiceAndQueueLock) {
105 if (mCallHandlerServiceGuarded == null) {
106 if (DBG) {
107 Log.d(TAG, "CallHandlerService not connected. Enqueue disconnect");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700108 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700109 enqueueDisconnect(call);
110 setupServiceConnection();
111 return;
Santos Cordon63a84242013-07-23 13:32:52 -0700112 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700113 }
114 processDisconnect(call);
115 }
116
Chiao Cheng26c6e922013-09-14 16:45:38 -0700117 private void wakeUpScreen() {
118 Log.d(TAG, "wakeUpScreen()");
119 final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
120 pm.wakeUp(SystemClock.uptimeMillis());
121 }
122
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700123 private void processDisconnect(Call call) {
124 try {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700125 if (DBG) {
126 Log.d(TAG, "onDisconnect: " + call);
127 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700128 synchronized (mServiceAndQueueLock) {
129 if (mCallHandlerServiceGuarded != null) {
130 mCallHandlerServiceGuarded.onDisconnect(call);
131 }
132 }
133 if (!mCallModeler.hasLiveCall()) {
134 unbind();
135 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700136 } catch (Exception e) {
137 Log.e(TAG, "Remote exception handling onDisconnect ", e);
Santos Cordon89647a62013-07-16 13:38:09 -0700138 }
139 }
140
Santos Cordona3d05142013-07-29 11:25:17 -0700141 @Override
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700142 public void onIncoming(Call call) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700143 synchronized (mServiceAndQueueLock) {
144 if (mCallHandlerServiceGuarded == null) {
145 if (DBG) {
146 Log.d(TAG, "CallHandlerService not connected. Enqueue incoming.");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700147 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700148 enqueueIncoming(call);
149 setupServiceConnection();
150 return;
Christine Chenee09a492013-08-06 16:02:29 -0700151 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700152 }
153 processIncoming(call);
154 }
155
156 private void processIncoming(Call call) {
157 if (DBG) {
158 Log.d(TAG, "onIncoming: " + call);
159 }
160 try {
Christine Chen3e0f0412013-09-18 20:33:49 -0700161 // TODO: check RespondViaSmsManager.allowRespondViaSmsForCall()
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700162 // must refactor call method to accept proper call object.
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700163 synchronized (mServiceAndQueueLock) {
164 if (mCallHandlerServiceGuarded != null) {
165 mCallHandlerServiceGuarded.onIncoming(call,
166 RejectWithTextMessageManager.loadCannedResponses());
167 }
168 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700169 } catch (Exception e) {
170 Log.e(TAG, "Remote exception handling onUpdate", e);
Christine Chenee09a492013-08-06 16:02:29 -0700171 }
172 }
173
174 @Override
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700175 public void onUpdate(List<Call> calls) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700176 synchronized (mServiceAndQueueLock) {
177 if (mCallHandlerServiceGuarded == null) {
178 if (DBG) {
179 Log.d(TAG, "CallHandlerService not connected. Enqueue update.");
180 }
181 enqueueUpdate(calls);
182 setupServiceConnection();
183 return;
184 }
185 }
186 processUpdate(calls);
187 }
188
189 private void processUpdate(List<Call> calls) {
190 if (DBG) {
191 Log.d(TAG, "onUpdate: " + calls.toString());
192 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700193 try {
194 synchronized (mServiceAndQueueLock) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700195 if (mCallHandlerServiceGuarded != null) {
196 mCallHandlerServiceGuarded.onUpdate(calls);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700197 }
Santos Cordona3d05142013-07-29 11:25:17 -0700198 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700199 if (!mCallModeler.hasLiveCall()) {
200 // TODO: unbinding happens in both onUpdate and onDisconnect because the ordering
201 // is not deterministic. Unbinding in both ensures that the service is unbound.
202 // But it also makes this in-efficient because we are unbinding twice, which leads
203 // to the CallHandlerService performing onCreate() and onDestroy() twice for each
204 // disconnect.
205 unbind();
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700206 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700207 } catch (Exception e) {
208 Log.e(TAG, "Remote exception handling onUpdate", e);
Santos Cordona3d05142013-07-29 11:25:17 -0700209 }
210 }
211
Chiao Cheng3f015c92013-09-06 15:56:27 -0700212
213 @Override
Yorke Leede41f672013-09-19 13:46:55 -0700214 public void onPostDialAction(Connection.PostDialState state, int callId, String remainingChars,
215 char currentChar) {
216 if (state != PostDialState.WAIT) return;
Chiao Cheng3f015c92013-09-06 15:56:27 -0700217 try {
218 synchronized (mServiceAndQueueLock) {
219 if (mCallHandlerServiceGuarded == null) {
220 if (DBG) {
221 Log.d(TAG, "CallHandlerService not conneccted. Skipping "
222 + "onPostDialWait().");
223 }
224 return;
225 }
226 }
227
228 mCallHandlerServiceGuarded.onPostDialWait(callId, remainingChars);
229 } catch (Exception e) {
230 Log.e(TAG, "Remote exception handling onUpdate", e);
231 }
232 }
233
Santos Cordon9b7bac72013-08-06 08:04:52 -0700234 @Override
Santos Cordoncd95f622013-08-29 03:38:52 -0700235 public void onAudioModeChange(int newMode, boolean muted) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700236 try {
237 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700238 if (mCallHandlerServiceGuarded == null) {
239 if (DBG) {
240 Log.d(TAG, "CallHandlerService not conneccted. Skipping "
241 + "onAudioModeChange().");
242 }
243 return;
244 }
Santos Cordon9b7bac72013-08-06 08:04:52 -0700245 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700246
247 // Just do a simple log for now.
248 Log.i(TAG, "Updating with new audio mode: " + AudioMode.toString(newMode) +
Santos Cordoncd95f622013-08-29 03:38:52 -0700249 " with mute " + muted);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700250
Santos Cordoncd95f622013-08-29 03:38:52 -0700251 mCallHandlerServiceGuarded.onAudioModeChange(newMode, muted);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700252 } catch (Exception e) {
253 Log.e(TAG, "Remote exception handling onAudioModeChange", e);
Santos Cordon9b7bac72013-08-06 08:04:52 -0700254 }
255 }
256
Santos Cordon593ab382013-08-06 21:58:23 -0700257 @Override
258 public void onSupportedAudioModeChange(int modeMask) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700259 try {
260 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700261 if (mCallHandlerServiceGuarded == null) {
262 if (DBG) {
263 Log.d(TAG, "CallHandlerService not conneccted. Skipping"
264 + "onSupportedAudioModeChange().");
265 }
266 return;
267 }
268 }
Santos Cordon593ab382013-08-06 21:58:23 -0700269
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700270 if (DBG) {
271 Log.d(TAG, "onSupportAudioModeChange: " + AudioMode.toString(modeMask));
272 }
273
274 mCallHandlerServiceGuarded.onSupportedAudioModeChange(modeMask);
275 } catch (Exception e) {
276 Log.e(TAG, "Remote exception handling onAudioModeChange", e);
277 }
278
279 }
280
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700281 private ServiceConnection mConnection = null;
282
283 private class InCallServiceConnection implements ServiceConnection {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700284 @Override public void onServiceConnected (ComponentName className, IBinder service){
285 if (DBG) {
286 Log.d(TAG, "Service Connected");
287 }
288 onCallHandlerServiceConnected(ICallHandlerService.Stub.asInterface(service));
Chiao Cheng11a4b652013-09-02 01:08:19 -0700289 mBindRetryCount = 0;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700290 }
291
292 @Override public void onServiceDisconnected (ComponentName className){
293 Log.i(TAG, "Disconnected from UI service.");
294 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700295 // Technically, unbindService is un-necessary since the framework will schedule and
296 // restart the crashed service. But there is a exponential backoff for the restart.
297 // Unbind explicitly and setup again to avoid the backoff since it's important to
298 // always have an in call ui.
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700299 unbind();
300
Santos Cordonf9ad0642013-09-26 10:48:07 -0700301 reconnectOnRemainingCalls();
Santos Cordon593ab382013-08-06 21:58:23 -0700302 }
303 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700304 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700305
Makoto Onukibcf20992013-09-12 17:59:30 -0700306 public void bringToForeground(boolean showDialpad) {
Santos Cordon406c0342013-08-28 00:07:47 -0700307 // only support this call if the service is already connected.
Santos Cordon19d814b2013-08-28 14:58:17 -0700308 synchronized (mServiceAndQueueLock) {
309 if (mCallHandlerServiceGuarded != null && mCallModeler.hasLiveCall()) {
310 try {
Makoto Onukibcf20992013-09-12 17:59:30 -0700311 if (DBG) Log.d(TAG, "bringToForeground: " + showDialpad);
312 mCallHandlerServiceGuarded.bringToForeground(showDialpad);
Santos Cordon19d814b2013-08-28 14:58:17 -0700313 } catch (RemoteException e) {
314 Log.e(TAG, "Exception handling bringToForeground", e);
315 }
Santos Cordon406c0342013-08-28 00:07:47 -0700316 }
317 }
318 }
319
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700320 private static Intent getInCallServiceIntent(Context context) {
321 final Intent serviceIntent = new Intent(ICallHandlerService.class.getName());
322 final ComponentName component = new ComponentName(context.getResources().getString(
Yorke Leed3105fe2013-09-25 12:44:45 -0700323 R.string.ui_default_package), context.getResources().getString(
324 R.string.incall_default_class));
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700325 serviceIntent.setComponent(component);
326 return serviceIntent;
327 }
328
Santos Cordon89647a62013-07-16 13:38:09 -0700329 /**
Santos Cordon345350e2013-07-19 17:16:14 -0700330 * Sets up the connection with ICallHandlerService
Santos Cordon89647a62013-07-16 13:38:09 -0700331 */
332 private void setupServiceConnection() {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700333 if (!PhoneGlobals.sVoiceCapable) {
334 return;
335 }
Chiao Cheng11a4b652013-09-02 01:08:19 -0700336
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700337 final Intent serviceIntent = getInCallServiceIntent(mContext);
Chiao Cheng11a4b652013-09-02 01:08:19 -0700338 if (DBG) {
339 Log.d(TAG, "binding to service " + serviceIntent);
340 }
341
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700342 synchronized (mServiceAndQueueLock) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700343 if (mConnection == null) {
344 mConnection = new InCallServiceConnection();
345
346 final PackageManager packageManger = mContext.getPackageManager();
347 final List<ResolveInfo> services = packageManger.queryIntentServices(serviceIntent,
348 0);
Yorke Lee79840002013-09-16 14:30:56 -0700349
350 ServiceInfo serviceInfo = null;
351
352 for (int i = 0; i < services.size(); i++) {
353 final ResolveInfo info = services.get(i);
354 if (info.serviceInfo != null) {
355 if (Manifest.permission.BIND_CALL_SERVICE.equals(
356 info.serviceInfo.permission)) {
357 serviceInfo = info.serviceInfo;
358 break;
359 }
360 }
361 }
362
363 if (serviceInfo == null) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700364 // Service not found, retry again after some delay
365 // This can happen if the service is being installed by the package manager.
366 // Between deletes and installs, bindService could get a silent service not
367 // found error.
368 mBindRetryCount++;
369 if (mBindRetryCount < MAX_RETRY_COUNT) {
370 Log.w(TAG, "InCallUI service not found. " + serviceIntent
371 + ". This happens if the service is being installed and should be"
372 + " transient. Retrying" + RETRY_DELAY_MILLIS + " ms.");
373 sendMessageDelayed(Message.obtain(this, BIND_RETRY_MSG),
374 RETRY_DELAY_MILLIS);
375 } else {
376 Log.e(TAG, "Tried to bind to in-call UI " + MAX_RETRY_COUNT + " times."
377 + " Giving up.");
378 }
379 return;
380 }
381
Yorke Lee79840002013-09-16 14:30:56 -0700382 // Bind to the first service that has a permission
383 // TODO: Add UI to allow us to select between services
384
385 serviceIntent.setComponent(new ComponentName(serviceInfo.packageName,
386 serviceInfo.name));
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700387 if (DBG) {
388 Log.d(TAG, "binding to service " + serviceIntent);
389 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700390 if (!mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700391 // This happens when the in-call package is in the middle of being
392 // installed.
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700393 // Delay the retry.
394 mBindRetryCount++;
395 if (mBindRetryCount < MAX_RETRY_COUNT) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700396 Log.e(TAG, "bindService failed on " + serviceIntent + ". Retrying in "
397 + RETRY_DELAY_MILLIS + " ms.");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700398 sendMessageDelayed(Message.obtain(this, BIND_RETRY_MSG),
399 RETRY_DELAY_MILLIS);
400 } else {
401 Log.wtf(TAG, "Tried to bind to in-call UI " + MAX_RETRY_COUNT + " times."
402 + " Giving up.");
403 }
404 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700405
406 } else {
407 Log.d(TAG, "Service connection to in call service already started.");
Santos Cordonaf763a12013-08-19 20:04:58 -0700408 }
409 }
410 }
411
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700412 private void unbind() {
413 synchronized (mServiceAndQueueLock) {
Yorke Lee362cec22013-09-18 15:20:26 -0700414 // On unbind, reenable the notification shade and navigation bar just in case the
415 // in-call UI crashed on an incoming call.
416 final StatusBarHelper statusBarHelper = PhoneGlobals.getInstance().notificationMgr.
417 statusBarHelper;
418 statusBarHelper.enableSystemBarNavigation(true);
419 statusBarHelper.enableExpandedView(true);
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700420 if (mCallHandlerServiceGuarded != null) {
421 Log.d(TAG, "Unbinding service.");
422 mCallHandlerServiceGuarded = null;
423 mContext.unbindService(mConnection);
424 }
425 mConnection = null;
426 }
427 }
428
Santos Cordonaf763a12013-08-19 20:04:58 -0700429 /**
Santos Cordoncba1b442013-07-18 12:43:58 -0700430 * Called when the in-call UI service is connected. Send command interface to in-call.
431 */
Santos Cordon63a84242013-07-23 13:32:52 -0700432 private void onCallHandlerServiceConnected(ICallHandlerService callHandlerService) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700433
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700434 synchronized (mServiceAndQueueLock) {
435 mCallHandlerServiceGuarded = callHandlerService;
436
Santos Cordonad078192013-08-28 15:14:54 -0700437 // Before we send any updates, we need to set up the initial service calls.
438 makeInitialServiceCalls();
439
Chiao Chengd38eebc2013-08-28 14:38:14 -0700440 processQueue();
Santos Cordonf9ad0642013-09-26 10:48:07 -0700441
442 if (mFullUpdateOnConnect) {
443 mFullUpdateOnConnect = false;
444 onUpdate(mCallModeler.getFullList());
445 }
446 }
447 }
448
449 /**
450 * Checks to see if there are any live calls left, and if so, try reconnecting the UI.
451 */
452 private void reconnectOnRemainingCalls() {
453 if (mCallModeler.hasLiveCall()) {
454 mFullUpdateOnConnect = true;
455 setupServiceConnection();
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700456 }
Santos Cordonad078192013-08-28 15:14:54 -0700457 }
Santos Cordoncba1b442013-07-18 12:43:58 -0700458
Santos Cordonad078192013-08-28 15:14:54 -0700459 /**
460 * Makes initial service calls to set up callcommandservice and audio modes.
461 */
462 private void makeInitialServiceCalls() {
Santos Cordoncba1b442013-07-18 12:43:58 -0700463 try {
Santos Cordon12a03aa2013-09-12 23:34:05 -0700464 mCallHandlerServiceGuarded.startCallService(mCallCommandService);
Santos Cordonad078192013-08-28 15:14:54 -0700465
466 onSupportedAudioModeChange(mAudioRouter.getSupportedAudioModes());
Santos Cordon8fd0ec72013-08-29 16:44:43 -0700467 onAudioModeChange(mAudioRouter.getAudioMode(), mAudioRouter.getMute());
Santos Cordoncba1b442013-07-18 12:43:58 -0700468 } catch (RemoteException e) {
Santos Cordon63a84242013-07-23 13:32:52 -0700469 Log.e(TAG, "Remote exception calling CallHandlerService::setCallCommandService", e);
Santos Cordon89647a62013-07-16 13:38:09 -0700470 }
471 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700472
Chiao Chengd38eebc2013-08-28 14:38:14 -0700473 private List<QueueParams> getQueue() {
474 if (mQueue == null) {
475 mQueue = Lists.newArrayList();
476 }
477 return mQueue;
478 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700479
480 private void enqueueDisconnect(Call call) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700481 getQueue().add(new QueueParams(QueueParams.METHOD_DISCONNECT, new Call(call)));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700482 }
483
484 private void enqueueIncoming(Call call) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700485 getQueue().add(new QueueParams(QueueParams.METHOD_INCOMING, new Call(call)));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700486 }
487
488 private void enqueueUpdate(List<Call> calls) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700489 final List<Call> copy = Lists.newArrayList();
490 for (Call call : calls) {
491 copy.add(new Call(call));
492 }
Chiao Chengc340ba92013-08-30 13:04:46 -0700493 getQueue().add(new QueueParams(QueueParams.METHOD_UPDATE, copy));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700494 }
495
Chiao Chengd38eebc2013-08-28 14:38:14 -0700496 private void processQueue() {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700497 synchronized (mServiceAndQueueLock) {
498 if (mQueue != null) {
499 for (QueueParams params : mQueue) {
500 switch (params.mMethod) {
501 case QueueParams.METHOD_INCOMING:
502 processIncoming((Call) params.mArg);
503 break;
504 case QueueParams.METHOD_UPDATE:
505 processUpdate((List<Call>) params.mArg);
506 break;
507 case QueueParams.METHOD_DISCONNECT:
508 processDisconnect((Call) params.mArg);
509 break;
510 default:
511 throw new IllegalArgumentException("Method type " + params.mMethod +
512 " not recognized.");
513 }
514 }
515 mQueue.clear();
516 mQueue = null;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700517 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700518 }
519 }
520
Chiao Chengd38eebc2013-08-28 14:38:14 -0700521 /**
522 * Holds method parameters.
523 */
524 private static class QueueParams {
525 private static final int METHOD_INCOMING = 1;
526 private static final int METHOD_UPDATE = 2;
527 private static final int METHOD_DISCONNECT = 3;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700528
Chiao Chengd38eebc2013-08-28 14:38:14 -0700529 private final int mMethod;
530 private final Object mArg;
531
532 private QueueParams(int method, Object arg) {
533 mMethod = method;
534 this.mArg = arg;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700535 }
536 }
Santos Cordon89647a62013-07-16 13:38:09 -0700537}