blob: e54b624a41ea24f99492a76a91244c72f6c099c1 [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;
Chiao Chengd38eebc2013-08-28 14:38:14 -070066
Chiao Cheng6c6b2722013-08-22 18:35:54 -070067 private ICallHandlerService mCallHandlerServiceGuarded; // Guarded by mServiceAndQueueLock
Chiao Chengd38eebc2013-08-28 14:38:14 -070068 // Single queue to guarantee ordering
69 private List<QueueParams> mQueue; // Guarded by mServiceAndQueueLock
70
Chiao Cheng6c6b2722013-08-22 18:35:54 -070071 private final Object mServiceAndQueueLock = new Object();
72 private int mBindRetryCount = 0;
73
74 @Override
75 public void handleMessage(Message msg) {
76 super.handleMessage(msg);
77
78 switch (msg.what) {
79 case BIND_RETRY_MSG:
80 setupServiceConnection();
81 break;
82 }
83 }
Santos Cordon89647a62013-07-16 13:38:09 -070084
Santos Cordon63a84242013-07-23 13:32:52 -070085 public CallHandlerServiceProxy(Context context, CallModeler callModeler,
Santos Cordon593ab382013-08-06 21:58:23 -070086 CallCommandService callCommandService, AudioRouter audioRouter) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -070087 if (DBG) {
88 Log.d(TAG, "init CallHandlerServiceProxy");
89 }
Santos Cordon89647a62013-07-16 13:38:09 -070090 mContext = context;
Santos Cordoncba1b442013-07-18 12:43:58 -070091 mCallCommandService = callCommandService;
Santos Cordon63a84242013-07-23 13:32:52 -070092 mCallModeler = callModeler;
Santos Cordon593ab382013-08-06 21:58:23 -070093 mAudioRouter = audioRouter;
Santos Cordon89647a62013-07-16 13:38:09 -070094
Santos Cordon593ab382013-08-06 21:58:23 -070095 mAudioRouter.addAudioModeListener(this);
Christine Chendaf7bf62013-08-05 19:12:31 -070096 mCallModeler.addListener(this);
Santos Cordon63a84242013-07-23 13:32:52 -070097 }
Santos Cordon89647a62013-07-16 13:38:09 -070098
Santos Cordon63a84242013-07-23 13:32:52 -070099 @Override
Santos Cordon995c8162013-07-29 09:22:22 -0700100 public void onDisconnect(Call call) {
Chiao Cheng26c6e922013-09-14 16:45:38 -0700101 // Wake up in case the screen was off.
102 wakeUpScreen();
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700103 synchronized (mServiceAndQueueLock) {
104 if (mCallHandlerServiceGuarded == null) {
105 if (DBG) {
106 Log.d(TAG, "CallHandlerService not connected. Enqueue disconnect");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700107 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700108 enqueueDisconnect(call);
109 setupServiceConnection();
110 return;
Santos Cordon63a84242013-07-23 13:32:52 -0700111 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700112 }
113 processDisconnect(call);
114 }
115
Chiao Cheng26c6e922013-09-14 16:45:38 -0700116 private void wakeUpScreen() {
117 Log.d(TAG, "wakeUpScreen()");
118 final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
119 pm.wakeUp(SystemClock.uptimeMillis());
120 }
121
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700122 private void processDisconnect(Call call) {
123 try {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700124 if (DBG) {
125 Log.d(TAG, "onDisconnect: " + call);
126 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700127 synchronized (mServiceAndQueueLock) {
128 if (mCallHandlerServiceGuarded != null) {
129 mCallHandlerServiceGuarded.onDisconnect(call);
130 }
131 }
132 if (!mCallModeler.hasLiveCall()) {
133 unbind();
134 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700135 } catch (Exception e) {
136 Log.e(TAG, "Remote exception handling onDisconnect ", e);
Santos Cordon89647a62013-07-16 13:38:09 -0700137 }
138 }
139
Santos Cordona3d05142013-07-29 11:25:17 -0700140 @Override
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700141 public void onIncoming(Call call) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700142 synchronized (mServiceAndQueueLock) {
143 if (mCallHandlerServiceGuarded == null) {
144 if (DBG) {
145 Log.d(TAG, "CallHandlerService not connected. Enqueue incoming.");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700146 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700147 enqueueIncoming(call);
148 setupServiceConnection();
149 return;
Christine Chenee09a492013-08-06 16:02:29 -0700150 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700151 }
152 processIncoming(call);
153 }
154
155 private void processIncoming(Call call) {
156 if (DBG) {
157 Log.d(TAG, "onIncoming: " + call);
158 }
159 try {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700160 // TODO(klp): check RespondViaSmsManager.allowRespondViaSmsForCall()
161 // must refactor call method to accept proper call object.
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700162 synchronized (mServiceAndQueueLock) {
163 if (mCallHandlerServiceGuarded != null) {
164 mCallHandlerServiceGuarded.onIncoming(call,
165 RejectWithTextMessageManager.loadCannedResponses());
166 }
167 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700168 } catch (Exception e) {
169 Log.e(TAG, "Remote exception handling onUpdate", e);
Christine Chenee09a492013-08-06 16:02:29 -0700170 }
171 }
172
173 @Override
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700174 public void onUpdate(List<Call> calls) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700175 synchronized (mServiceAndQueueLock) {
176 if (mCallHandlerServiceGuarded == null) {
177 if (DBG) {
178 Log.d(TAG, "CallHandlerService not connected. Enqueue update.");
179 }
180 enqueueUpdate(calls);
181 setupServiceConnection();
182 return;
183 }
184 }
185 processUpdate(calls);
186 }
187
188 private void processUpdate(List<Call> calls) {
189 if (DBG) {
190 Log.d(TAG, "onUpdate: " + calls.toString());
191 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700192 try {
193 synchronized (mServiceAndQueueLock) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700194 if (mCallHandlerServiceGuarded != null) {
195 mCallHandlerServiceGuarded.onUpdate(calls);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700196 }
Santos Cordona3d05142013-07-29 11:25:17 -0700197 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700198 if (!mCallModeler.hasLiveCall()) {
199 // TODO: unbinding happens in both onUpdate and onDisconnect because the ordering
200 // is not deterministic. Unbinding in both ensures that the service is unbound.
201 // But it also makes this in-efficient because we are unbinding twice, which leads
202 // to the CallHandlerService performing onCreate() and onDestroy() twice for each
203 // disconnect.
204 unbind();
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700205 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700206 } catch (Exception e) {
207 Log.e(TAG, "Remote exception handling onUpdate", e);
Santos Cordona3d05142013-07-29 11:25:17 -0700208 }
209 }
210
Chiao Cheng3f015c92013-09-06 15:56:27 -0700211
212 @Override
Yorke Leede41f672013-09-19 13:46:55 -0700213 public void onPostDialAction(Connection.PostDialState state, int callId, String remainingChars,
214 char currentChar) {
215 if (state != PostDialState.WAIT) return;
Chiao Cheng3f015c92013-09-06 15:56:27 -0700216 try {
217 synchronized (mServiceAndQueueLock) {
218 if (mCallHandlerServiceGuarded == null) {
219 if (DBG) {
220 Log.d(TAG, "CallHandlerService not conneccted. Skipping "
221 + "onPostDialWait().");
222 }
223 return;
224 }
225 }
226
227 mCallHandlerServiceGuarded.onPostDialWait(callId, remainingChars);
228 } catch (Exception e) {
229 Log.e(TAG, "Remote exception handling onUpdate", e);
230 }
231 }
232
Santos Cordon9b7bac72013-08-06 08:04:52 -0700233 @Override
Santos Cordoncd95f622013-08-29 03:38:52 -0700234 public void onAudioModeChange(int newMode, boolean muted) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700235 try {
236 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700237 if (mCallHandlerServiceGuarded == null) {
238 if (DBG) {
239 Log.d(TAG, "CallHandlerService not conneccted. Skipping "
240 + "onAudioModeChange().");
241 }
242 return;
243 }
Santos Cordon9b7bac72013-08-06 08:04:52 -0700244 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700245
246 // Just do a simple log for now.
247 Log.i(TAG, "Updating with new audio mode: " + AudioMode.toString(newMode) +
Santos Cordoncd95f622013-08-29 03:38:52 -0700248 " with mute " + muted);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700249
Santos Cordoncd95f622013-08-29 03:38:52 -0700250 mCallHandlerServiceGuarded.onAudioModeChange(newMode, muted);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700251 } catch (Exception e) {
252 Log.e(TAG, "Remote exception handling onAudioModeChange", e);
Santos Cordon9b7bac72013-08-06 08:04:52 -0700253 }
254 }
255
Santos Cordon593ab382013-08-06 21:58:23 -0700256 @Override
257 public void onSupportedAudioModeChange(int modeMask) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700258 try {
259 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700260 if (mCallHandlerServiceGuarded == null) {
261 if (DBG) {
262 Log.d(TAG, "CallHandlerService not conneccted. Skipping"
263 + "onSupportedAudioModeChange().");
264 }
265 return;
266 }
267 }
Santos Cordon593ab382013-08-06 21:58:23 -0700268
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700269 if (DBG) {
270 Log.d(TAG, "onSupportAudioModeChange: " + AudioMode.toString(modeMask));
271 }
272
273 mCallHandlerServiceGuarded.onSupportedAudioModeChange(modeMask);
274 } catch (Exception e) {
275 Log.e(TAG, "Remote exception handling onAudioModeChange", e);
276 }
277
278 }
279
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700280 private ServiceConnection mConnection = null;
281
282 private class InCallServiceConnection implements ServiceConnection {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700283 @Override public void onServiceConnected (ComponentName className, IBinder service){
284 if (DBG) {
285 Log.d(TAG, "Service Connected");
286 }
287 onCallHandlerServiceConnected(ICallHandlerService.Stub.asInterface(service));
Chiao Cheng11a4b652013-09-02 01:08:19 -0700288 mBindRetryCount = 0;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700289 }
290
291 @Override public void onServiceDisconnected (ComponentName className){
292 Log.i(TAG, "Disconnected from UI service.");
293 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700294 // Technically, unbindService is un-necessary since the framework will schedule and
295 // restart the crashed service. But there is a exponential backoff for the restart.
296 // Unbind explicitly and setup again to avoid the backoff since it's important to
297 // always have an in call ui.
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700298 unbind();
299
300 // TODO(klp): hang up all calls.
Santos Cordon593ab382013-08-06 21:58:23 -0700301 }
302 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700303 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700304
Makoto Onukibcf20992013-09-12 17:59:30 -0700305 public void bringToForeground(boolean showDialpad) {
Santos Cordon406c0342013-08-28 00:07:47 -0700306 // only support this call if the service is already connected.
Santos Cordon19d814b2013-08-28 14:58:17 -0700307 synchronized (mServiceAndQueueLock) {
308 if (mCallHandlerServiceGuarded != null && mCallModeler.hasLiveCall()) {
309 try {
Makoto Onukibcf20992013-09-12 17:59:30 -0700310 if (DBG) Log.d(TAG, "bringToForeground: " + showDialpad);
311 mCallHandlerServiceGuarded.bringToForeground(showDialpad);
Santos Cordon19d814b2013-08-28 14:58:17 -0700312 } catch (RemoteException e) {
313 Log.e(TAG, "Exception handling bringToForeground", e);
314 }
Santos Cordon406c0342013-08-28 00:07:47 -0700315 }
316 }
317 }
318
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700319 private static Intent getInCallServiceIntent(Context context) {
320 final Intent serviceIntent = new Intent(ICallHandlerService.class.getName());
321 final ComponentName component = new ComponentName(context.getResources().getString(
322 R.string.incall_ui_default_package), context.getResources().getString(
323 R.string.incall_ui_default_class));
324 serviceIntent.setComponent(component);
325 return serviceIntent;
326 }
327
Santos Cordon89647a62013-07-16 13:38:09 -0700328 /**
Santos Cordon345350e2013-07-19 17:16:14 -0700329 * Sets up the connection with ICallHandlerService
Santos Cordon89647a62013-07-16 13:38:09 -0700330 */
331 private void setupServiceConnection() {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700332 if (!PhoneGlobals.sVoiceCapable) {
333 return;
334 }
Chiao Cheng11a4b652013-09-02 01:08:19 -0700335
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700336 final Intent serviceIntent = getInCallServiceIntent(mContext);
Chiao Cheng11a4b652013-09-02 01:08:19 -0700337 if (DBG) {
338 Log.d(TAG, "binding to service " + serviceIntent);
339 }
340
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700341 synchronized (mServiceAndQueueLock) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700342 if (mConnection == null) {
343 mConnection = new InCallServiceConnection();
344
345 final PackageManager packageManger = mContext.getPackageManager();
346 final List<ResolveInfo> services = packageManger.queryIntentServices(serviceIntent,
347 0);
Yorke Lee79840002013-09-16 14:30:56 -0700348
349 ServiceInfo serviceInfo = null;
350
351 for (int i = 0; i < services.size(); i++) {
352 final ResolveInfo info = services.get(i);
353 if (info.serviceInfo != null) {
354 if (Manifest.permission.BIND_CALL_SERVICE.equals(
355 info.serviceInfo.permission)) {
356 serviceInfo = info.serviceInfo;
357 break;
358 }
359 }
360 }
361
362 if (serviceInfo == null) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700363 // Service not found, retry again after some delay
364 // This can happen if the service is being installed by the package manager.
365 // Between deletes and installs, bindService could get a silent service not
366 // found error.
367 mBindRetryCount++;
368 if (mBindRetryCount < MAX_RETRY_COUNT) {
369 Log.w(TAG, "InCallUI service not found. " + serviceIntent
370 + ". This happens if the service is being installed and should be"
371 + " transient. Retrying" + RETRY_DELAY_MILLIS + " ms.");
372 sendMessageDelayed(Message.obtain(this, BIND_RETRY_MSG),
373 RETRY_DELAY_MILLIS);
374 } else {
375 Log.e(TAG, "Tried to bind to in-call UI " + MAX_RETRY_COUNT + " times."
376 + " Giving up.");
377 }
378 return;
379 }
380
Yorke Lee79840002013-09-16 14:30:56 -0700381 // Bind to the first service that has a permission
382 // TODO: Add UI to allow us to select between services
383
384 serviceIntent.setComponent(new ComponentName(serviceInfo.packageName,
385 serviceInfo.name));
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700386 if (DBG) {
387 Log.d(TAG, "binding to service " + serviceIntent);
388 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700389 if (!mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700390 // This happens when the in-call package is in the middle of being
391 // installed.
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700392 // Delay the retry.
393 mBindRetryCount++;
394 if (mBindRetryCount < MAX_RETRY_COUNT) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700395 Log.e(TAG, "bindService failed on " + serviceIntent + ". Retrying in "
396 + RETRY_DELAY_MILLIS + " ms.");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700397 sendMessageDelayed(Message.obtain(this, BIND_RETRY_MSG),
398 RETRY_DELAY_MILLIS);
399 } else {
400 Log.wtf(TAG, "Tried to bind to in-call UI " + MAX_RETRY_COUNT + " times."
401 + " Giving up.");
402 }
403 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700404
405 } else {
406 Log.d(TAG, "Service connection to in call service already started.");
Santos Cordonaf763a12013-08-19 20:04:58 -0700407 }
408 }
409 }
410
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700411 private void unbind() {
412 synchronized (mServiceAndQueueLock) {
Yorke Lee362cec22013-09-18 15:20:26 -0700413 // On unbind, reenable the notification shade and navigation bar just in case the
414 // in-call UI crashed on an incoming call.
415 final StatusBarHelper statusBarHelper = PhoneGlobals.getInstance().notificationMgr.
416 statusBarHelper;
417 statusBarHelper.enableSystemBarNavigation(true);
418 statusBarHelper.enableExpandedView(true);
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700419 if (mCallHandlerServiceGuarded != null) {
420 Log.d(TAG, "Unbinding service.");
421 mCallHandlerServiceGuarded = null;
422 mContext.unbindService(mConnection);
423 }
424 mConnection = null;
425 }
426 }
427
Santos Cordonaf763a12013-08-19 20:04:58 -0700428 /**
Santos Cordoncba1b442013-07-18 12:43:58 -0700429 * Called when the in-call UI service is connected. Send command interface to in-call.
430 */
Santos Cordon63a84242013-07-23 13:32:52 -0700431 private void onCallHandlerServiceConnected(ICallHandlerService callHandlerService) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700432
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700433 synchronized (mServiceAndQueueLock) {
434 mCallHandlerServiceGuarded = callHandlerService;
435
Santos Cordonad078192013-08-28 15:14:54 -0700436 // Before we send any updates, we need to set up the initial service calls.
437 makeInitialServiceCalls();
438
Chiao Chengd38eebc2013-08-28 14:38:14 -0700439 processQueue();
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700440 }
Santos Cordonad078192013-08-28 15:14:54 -0700441 }
Santos Cordoncba1b442013-07-18 12:43:58 -0700442
Santos Cordonad078192013-08-28 15:14:54 -0700443 /**
444 * Makes initial service calls to set up callcommandservice and audio modes.
445 */
446 private void makeInitialServiceCalls() {
Santos Cordoncba1b442013-07-18 12:43:58 -0700447 try {
Santos Cordon12a03aa2013-09-12 23:34:05 -0700448 mCallHandlerServiceGuarded.startCallService(mCallCommandService);
Santos Cordonad078192013-08-28 15:14:54 -0700449
450 onSupportedAudioModeChange(mAudioRouter.getSupportedAudioModes());
Santos Cordon8fd0ec72013-08-29 16:44:43 -0700451 onAudioModeChange(mAudioRouter.getAudioMode(), mAudioRouter.getMute());
Santos Cordoncba1b442013-07-18 12:43:58 -0700452 } catch (RemoteException e) {
Santos Cordon63a84242013-07-23 13:32:52 -0700453 Log.e(TAG, "Remote exception calling CallHandlerService::setCallCommandService", e);
Santos Cordon89647a62013-07-16 13:38:09 -0700454 }
455 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700456
Chiao Chengd38eebc2013-08-28 14:38:14 -0700457 private List<QueueParams> getQueue() {
458 if (mQueue == null) {
459 mQueue = Lists.newArrayList();
460 }
461 return mQueue;
462 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700463
464 private void enqueueDisconnect(Call call) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700465 getQueue().add(new QueueParams(QueueParams.METHOD_DISCONNECT, new Call(call)));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700466 }
467
468 private void enqueueIncoming(Call call) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700469 getQueue().add(new QueueParams(QueueParams.METHOD_INCOMING, new Call(call)));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700470 }
471
472 private void enqueueUpdate(List<Call> calls) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700473 final List<Call> copy = Lists.newArrayList();
474 for (Call call : calls) {
475 copy.add(new Call(call));
476 }
Chiao Chengc340ba92013-08-30 13:04:46 -0700477 getQueue().add(new QueueParams(QueueParams.METHOD_UPDATE, copy));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700478 }
479
Chiao Chengd38eebc2013-08-28 14:38:14 -0700480 private void processQueue() {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700481 synchronized (mServiceAndQueueLock) {
482 if (mQueue != null) {
483 for (QueueParams params : mQueue) {
484 switch (params.mMethod) {
485 case QueueParams.METHOD_INCOMING:
486 processIncoming((Call) params.mArg);
487 break;
488 case QueueParams.METHOD_UPDATE:
489 processUpdate((List<Call>) params.mArg);
490 break;
491 case QueueParams.METHOD_DISCONNECT:
492 processDisconnect((Call) params.mArg);
493 break;
494 default:
495 throw new IllegalArgumentException("Method type " + params.mMethod +
496 " not recognized.");
497 }
498 }
499 mQueue.clear();
500 mQueue = null;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700501 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700502 }
503 }
504
Chiao Chengd38eebc2013-08-28 14:38:14 -0700505 /**
506 * Holds method parameters.
507 */
508 private static class QueueParams {
509 private static final int METHOD_INCOMING = 1;
510 private static final int METHOD_UPDATE = 2;
511 private static final int METHOD_DISCONNECT = 3;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700512
Chiao Chengd38eebc2013-08-28 14:38:14 -0700513 private final int mMethod;
514 private final Object mArg;
515
516 private QueueParams(int method, Object arg) {
517 mMethod = method;
518 this.mArg = arg;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700519 }
520 }
Santos Cordon89647a62013-07-16 13:38:09 -0700521}