blob: 2f7145743f9d521cd730cafce5af9e59a09d9f6d [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;
34import android.util.Log;
35
Santos Cordon9b7bac72013-08-06 08:04:52 -070036import com.android.phone.AudioRouter.AudioModeListener;
Yorke Lee362cec22013-09-18 15:20:26 -070037import com.android.phone.NotificationMgr.StatusBarHelper;
Santos Cordon9b7bac72013-08-06 08:04:52 -070038import com.android.services.telephony.common.AudioMode;
Santos Cordonf4046882013-07-25 18:49:27 -070039import com.android.services.telephony.common.Call;
Santos Cordon345350e2013-07-19 17:16:14 -070040import com.android.services.telephony.common.ICallHandlerService;
Chiao Cheng6c6b2722013-08-22 18:35:54 -070041import com.google.common.collect.Lists;
Santos Cordon89647a62013-07-16 13:38:09 -070042
Santos Cordona3d05142013-07-29 11:25:17 -070043import java.util.List;
44
Santos Cordon89647a62013-07-16 13:38:09 -070045/**
Santos Cordon345350e2013-07-19 17:16:14 -070046 * This class is responsible for passing through call state changes to the CallHandlerService.
Santos Cordon89647a62013-07-16 13:38:09 -070047 */
Chiao Cheng6c6b2722013-08-22 18:35:54 -070048public class CallHandlerServiceProxy extends Handler
49 implements CallModeler.Listener, AudioModeListener {
Santos Cordon89647a62013-07-16 13:38:09 -070050
Santos Cordon345350e2013-07-19 17:16:14 -070051 private static final String TAG = CallHandlerServiceProxy.class.getSimpleName();
Santos Cordon71d5c6e2013-09-05 21:34:33 -070052 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt(
Chiao Cheng6c6b2722013-08-22 18:35:54 -070053 "ro.debuggable", 0) == 1);
Chiao Chenge41661c2013-07-23 13:28:26 -070054
Chiao Cheng6c6b2722013-08-22 18:35:54 -070055 public static final int RETRY_DELAY_MILLIS = 2000;
56 private static final int BIND_RETRY_MSG = 1;
57 private static final int MAX_RETRY_COUNT = 5;
Santos Cordon89647a62013-07-16 13:38:09 -070058
Santos Cordon593ab382013-08-06 21:58:23 -070059 private AudioRouter mAudioRouter;
Santos Cordoncba1b442013-07-18 12:43:58 -070060 private CallCommandService mCallCommandService;
Santos Cordon593ab382013-08-06 21:58:23 -070061 private CallModeler mCallModeler;
62 private Context mContext;
Chiao Chengd38eebc2013-08-28 14:38:14 -070063
Chiao Cheng6c6b2722013-08-22 18:35:54 -070064 private ICallHandlerService mCallHandlerServiceGuarded; // Guarded by mServiceAndQueueLock
Chiao Chengd38eebc2013-08-28 14:38:14 -070065 // Single queue to guarantee ordering
66 private List<QueueParams> mQueue; // Guarded by mServiceAndQueueLock
67
Chiao Cheng6c6b2722013-08-22 18:35:54 -070068 private final Object mServiceAndQueueLock = new Object();
69 private int mBindRetryCount = 0;
70
71 @Override
72 public void handleMessage(Message msg) {
73 super.handleMessage(msg);
74
75 switch (msg.what) {
76 case BIND_RETRY_MSG:
77 setupServiceConnection();
78 break;
79 }
80 }
Santos Cordon89647a62013-07-16 13:38:09 -070081
Santos Cordon63a84242013-07-23 13:32:52 -070082 public CallHandlerServiceProxy(Context context, CallModeler callModeler,
Santos Cordon593ab382013-08-06 21:58:23 -070083 CallCommandService callCommandService, AudioRouter audioRouter) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -070084 if (DBG) {
85 Log.d(TAG, "init CallHandlerServiceProxy");
86 }
Santos Cordon89647a62013-07-16 13:38:09 -070087 mContext = context;
Santos Cordoncba1b442013-07-18 12:43:58 -070088 mCallCommandService = callCommandService;
Santos Cordon63a84242013-07-23 13:32:52 -070089 mCallModeler = callModeler;
Santos Cordon593ab382013-08-06 21:58:23 -070090 mAudioRouter = audioRouter;
Santos Cordon89647a62013-07-16 13:38:09 -070091
Santos Cordon593ab382013-08-06 21:58:23 -070092 mAudioRouter.addAudioModeListener(this);
Christine Chendaf7bf62013-08-05 19:12:31 -070093 mCallModeler.addListener(this);
Santos Cordon63a84242013-07-23 13:32:52 -070094 }
Santos Cordon89647a62013-07-16 13:38:09 -070095
Santos Cordon63a84242013-07-23 13:32:52 -070096 @Override
Santos Cordon995c8162013-07-29 09:22:22 -070097 public void onDisconnect(Call call) {
Chiao Cheng26c6e922013-09-14 16:45:38 -070098 // Wake up in case the screen was off.
99 wakeUpScreen();
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700100 synchronized (mServiceAndQueueLock) {
101 if (mCallHandlerServiceGuarded == null) {
102 if (DBG) {
103 Log.d(TAG, "CallHandlerService not connected. Enqueue disconnect");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700104 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700105 enqueueDisconnect(call);
106 setupServiceConnection();
107 return;
Santos Cordon63a84242013-07-23 13:32:52 -0700108 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700109 }
110 processDisconnect(call);
111 }
112
Chiao Cheng26c6e922013-09-14 16:45:38 -0700113 private void wakeUpScreen() {
114 Log.d(TAG, "wakeUpScreen()");
115 final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
116 pm.wakeUp(SystemClock.uptimeMillis());
117 }
118
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700119 private void processDisconnect(Call call) {
120 try {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700121 if (DBG) {
122 Log.d(TAG, "onDisconnect: " + call);
123 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700124 synchronized (mServiceAndQueueLock) {
125 if (mCallHandlerServiceGuarded != null) {
126 mCallHandlerServiceGuarded.onDisconnect(call);
127 }
128 }
129 if (!mCallModeler.hasLiveCall()) {
130 unbind();
131 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700132 } catch (Exception e) {
133 Log.e(TAG, "Remote exception handling onDisconnect ", e);
Santos Cordon89647a62013-07-16 13:38:09 -0700134 }
135 }
136
Santos Cordona3d05142013-07-29 11:25:17 -0700137 @Override
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700138 public void onIncoming(Call call) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700139 synchronized (mServiceAndQueueLock) {
140 if (mCallHandlerServiceGuarded == null) {
141 if (DBG) {
142 Log.d(TAG, "CallHandlerService not connected. Enqueue incoming.");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700143 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700144 enqueueIncoming(call);
145 setupServiceConnection();
146 return;
Christine Chenee09a492013-08-06 16:02:29 -0700147 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700148 }
149 processIncoming(call);
150 }
151
152 private void processIncoming(Call call) {
153 if (DBG) {
154 Log.d(TAG, "onIncoming: " + call);
155 }
156 try {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700157 // TODO(klp): check RespondViaSmsManager.allowRespondViaSmsForCall()
158 // must refactor call method to accept proper call object.
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700159 synchronized (mServiceAndQueueLock) {
160 if (mCallHandlerServiceGuarded != null) {
161 mCallHandlerServiceGuarded.onIncoming(call,
162 RejectWithTextMessageManager.loadCannedResponses());
163 }
164 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700165 } catch (Exception e) {
166 Log.e(TAG, "Remote exception handling onUpdate", e);
Christine Chenee09a492013-08-06 16:02:29 -0700167 }
168 }
169
170 @Override
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700171 public void onUpdate(List<Call> calls) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700172 synchronized (mServiceAndQueueLock) {
173 if (mCallHandlerServiceGuarded == null) {
174 if (DBG) {
175 Log.d(TAG, "CallHandlerService not connected. Enqueue update.");
176 }
177 enqueueUpdate(calls);
178 setupServiceConnection();
179 return;
180 }
181 }
182 processUpdate(calls);
183 }
184
185 private void processUpdate(List<Call> calls) {
186 if (DBG) {
187 Log.d(TAG, "onUpdate: " + calls.toString());
188 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700189 try {
190 synchronized (mServiceAndQueueLock) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700191 if (mCallHandlerServiceGuarded != null) {
192 mCallHandlerServiceGuarded.onUpdate(calls);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700193 }
Santos Cordona3d05142013-07-29 11:25:17 -0700194 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700195 if (!mCallModeler.hasLiveCall()) {
196 // TODO: unbinding happens in both onUpdate and onDisconnect because the ordering
197 // is not deterministic. Unbinding in both ensures that the service is unbound.
198 // But it also makes this in-efficient because we are unbinding twice, which leads
199 // to the CallHandlerService performing onCreate() and onDestroy() twice for each
200 // disconnect.
201 unbind();
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700202 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700203 } catch (Exception e) {
204 Log.e(TAG, "Remote exception handling onUpdate", e);
Santos Cordona3d05142013-07-29 11:25:17 -0700205 }
206 }
207
Chiao Cheng3f015c92013-09-06 15:56:27 -0700208
209 @Override
210 public void onPostDialWait(int callId, String remainingChars) {
211 try {
212 synchronized (mServiceAndQueueLock) {
213 if (mCallHandlerServiceGuarded == null) {
214 if (DBG) {
215 Log.d(TAG, "CallHandlerService not conneccted. Skipping "
216 + "onPostDialWait().");
217 }
218 return;
219 }
220 }
221
222 mCallHandlerServiceGuarded.onPostDialWait(callId, remainingChars);
223 } catch (Exception e) {
224 Log.e(TAG, "Remote exception handling onUpdate", e);
225 }
226 }
227
Santos Cordon9b7bac72013-08-06 08:04:52 -0700228 @Override
Santos Cordoncd95f622013-08-29 03:38:52 -0700229 public void onAudioModeChange(int newMode, boolean muted) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700230 try {
231 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700232 if (mCallHandlerServiceGuarded == null) {
233 if (DBG) {
234 Log.d(TAG, "CallHandlerService not conneccted. Skipping "
235 + "onAudioModeChange().");
236 }
237 return;
238 }
Santos Cordon9b7bac72013-08-06 08:04:52 -0700239 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700240
241 // Just do a simple log for now.
242 Log.i(TAG, "Updating with new audio mode: " + AudioMode.toString(newMode) +
Santos Cordoncd95f622013-08-29 03:38:52 -0700243 " with mute " + muted);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700244
Santos Cordoncd95f622013-08-29 03:38:52 -0700245 mCallHandlerServiceGuarded.onAudioModeChange(newMode, muted);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700246 } catch (Exception e) {
247 Log.e(TAG, "Remote exception handling onAudioModeChange", e);
Santos Cordon9b7bac72013-08-06 08:04:52 -0700248 }
249 }
250
Santos Cordon593ab382013-08-06 21:58:23 -0700251 @Override
252 public void onSupportedAudioModeChange(int modeMask) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700253 try {
254 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700255 if (mCallHandlerServiceGuarded == null) {
256 if (DBG) {
257 Log.d(TAG, "CallHandlerService not conneccted. Skipping"
258 + "onSupportedAudioModeChange().");
259 }
260 return;
261 }
262 }
Santos Cordon593ab382013-08-06 21:58:23 -0700263
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700264 if (DBG) {
265 Log.d(TAG, "onSupportAudioModeChange: " + AudioMode.toString(modeMask));
266 }
267
268 mCallHandlerServiceGuarded.onSupportedAudioModeChange(modeMask);
269 } catch (Exception e) {
270 Log.e(TAG, "Remote exception handling onAudioModeChange", e);
271 }
272
273 }
274
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700275 private ServiceConnection mConnection = null;
276
277 private class InCallServiceConnection implements ServiceConnection {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700278 @Override public void onServiceConnected (ComponentName className, IBinder service){
279 if (DBG) {
280 Log.d(TAG, "Service Connected");
281 }
282 onCallHandlerServiceConnected(ICallHandlerService.Stub.asInterface(service));
Chiao Cheng11a4b652013-09-02 01:08:19 -0700283 mBindRetryCount = 0;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700284 }
285
286 @Override public void onServiceDisconnected (ComponentName className){
287 Log.i(TAG, "Disconnected from UI service.");
288 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700289 // Technically, unbindService is un-necessary since the framework will schedule and
290 // restart the crashed service. But there is a exponential backoff for the restart.
291 // Unbind explicitly and setup again to avoid the backoff since it's important to
292 // always have an in call ui.
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700293 unbind();
294
295 // TODO(klp): hang up all calls.
Santos Cordon593ab382013-08-06 21:58:23 -0700296 }
297 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700298 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700299
Makoto Onukibcf20992013-09-12 17:59:30 -0700300 public void bringToForeground(boolean showDialpad) {
Santos Cordon406c0342013-08-28 00:07:47 -0700301 // only support this call if the service is already connected.
Santos Cordon19d814b2013-08-28 14:58:17 -0700302 synchronized (mServiceAndQueueLock) {
303 if (mCallHandlerServiceGuarded != null && mCallModeler.hasLiveCall()) {
304 try {
Makoto Onukibcf20992013-09-12 17:59:30 -0700305 if (DBG) Log.d(TAG, "bringToForeground: " + showDialpad);
306 mCallHandlerServiceGuarded.bringToForeground(showDialpad);
Santos Cordon19d814b2013-08-28 14:58:17 -0700307 } catch (RemoteException e) {
308 Log.e(TAG, "Exception handling bringToForeground", e);
309 }
Santos Cordon406c0342013-08-28 00:07:47 -0700310 }
311 }
312 }
313
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700314 private static Intent getInCallServiceIntent(Context context) {
315 final Intent serviceIntent = new Intent(ICallHandlerService.class.getName());
316 final ComponentName component = new ComponentName(context.getResources().getString(
317 R.string.incall_ui_default_package), context.getResources().getString(
318 R.string.incall_ui_default_class));
319 serviceIntent.setComponent(component);
320 return serviceIntent;
321 }
322
Santos Cordon89647a62013-07-16 13:38:09 -0700323 /**
Santos Cordon345350e2013-07-19 17:16:14 -0700324 * Sets up the connection with ICallHandlerService
Santos Cordon89647a62013-07-16 13:38:09 -0700325 */
326 private void setupServiceConnection() {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700327 if (!PhoneGlobals.sVoiceCapable) {
328 return;
329 }
Chiao Cheng11a4b652013-09-02 01:08:19 -0700330
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700331 final Intent serviceIntent = getInCallServiceIntent(mContext);
Chiao Cheng11a4b652013-09-02 01:08:19 -0700332 if (DBG) {
333 Log.d(TAG, "binding to service " + serviceIntent);
334 }
335
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700336 synchronized (mServiceAndQueueLock) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700337 if (mConnection == null) {
338 mConnection = new InCallServiceConnection();
339
340 final PackageManager packageManger = mContext.getPackageManager();
341 final List<ResolveInfo> services = packageManger.queryIntentServices(serviceIntent,
342 0);
Yorke Lee79840002013-09-16 14:30:56 -0700343
344 ServiceInfo serviceInfo = null;
345
346 for (int i = 0; i < services.size(); i++) {
347 final ResolveInfo info = services.get(i);
348 if (info.serviceInfo != null) {
349 if (Manifest.permission.BIND_CALL_SERVICE.equals(
350 info.serviceInfo.permission)) {
351 serviceInfo = info.serviceInfo;
352 break;
353 }
354 }
355 }
356
357 if (serviceInfo == null) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700358 // Service not found, retry again after some delay
359 // This can happen if the service is being installed by the package manager.
360 // Between deletes and installs, bindService could get a silent service not
361 // found error.
362 mBindRetryCount++;
363 if (mBindRetryCount < MAX_RETRY_COUNT) {
364 Log.w(TAG, "InCallUI service not found. " + serviceIntent
365 + ". This happens if the service is being installed and should be"
366 + " transient. Retrying" + RETRY_DELAY_MILLIS + " ms.");
367 sendMessageDelayed(Message.obtain(this, BIND_RETRY_MSG),
368 RETRY_DELAY_MILLIS);
369 } else {
370 Log.e(TAG, "Tried to bind to in-call UI " + MAX_RETRY_COUNT + " times."
371 + " Giving up.");
372 }
373 return;
374 }
375
Yorke Lee79840002013-09-16 14:30:56 -0700376 // Bind to the first service that has a permission
377 // TODO: Add UI to allow us to select between services
378
379 serviceIntent.setComponent(new ComponentName(serviceInfo.packageName,
380 serviceInfo.name));
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700381 if (DBG) {
382 Log.d(TAG, "binding to service " + serviceIntent);
383 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700384 if (!mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700385 // This happens when the in-call package is in the middle of being
386 // installed.
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700387 // Delay the retry.
388 mBindRetryCount++;
389 if (mBindRetryCount < MAX_RETRY_COUNT) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700390 Log.e(TAG, "bindService failed on " + serviceIntent + ". Retrying in "
391 + RETRY_DELAY_MILLIS + " ms.");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700392 sendMessageDelayed(Message.obtain(this, BIND_RETRY_MSG),
393 RETRY_DELAY_MILLIS);
394 } else {
395 Log.wtf(TAG, "Tried to bind to in-call UI " + MAX_RETRY_COUNT + " times."
396 + " Giving up.");
397 }
398 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700399
400 } else {
401 Log.d(TAG, "Service connection to in call service already started.");
Santos Cordonaf763a12013-08-19 20:04:58 -0700402 }
403 }
404 }
405
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700406 private void unbind() {
407 synchronized (mServiceAndQueueLock) {
Yorke Lee362cec22013-09-18 15:20:26 -0700408 // On unbind, reenable the notification shade and navigation bar just in case the
409 // in-call UI crashed on an incoming call.
410 final StatusBarHelper statusBarHelper = PhoneGlobals.getInstance().notificationMgr.
411 statusBarHelper;
412 statusBarHelper.enableSystemBarNavigation(true);
413 statusBarHelper.enableExpandedView(true);
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700414 if (mCallHandlerServiceGuarded != null) {
415 Log.d(TAG, "Unbinding service.");
416 mCallHandlerServiceGuarded = null;
417 mContext.unbindService(mConnection);
418 }
419 mConnection = null;
420 }
421 }
422
Santos Cordonaf763a12013-08-19 20:04:58 -0700423 /**
Santos Cordoncba1b442013-07-18 12:43:58 -0700424 * Called when the in-call UI service is connected. Send command interface to in-call.
425 */
Santos Cordon63a84242013-07-23 13:32:52 -0700426 private void onCallHandlerServiceConnected(ICallHandlerService callHandlerService) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700427
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700428 synchronized (mServiceAndQueueLock) {
429 mCallHandlerServiceGuarded = callHandlerService;
430
Santos Cordonad078192013-08-28 15:14:54 -0700431 // Before we send any updates, we need to set up the initial service calls.
432 makeInitialServiceCalls();
433
Chiao Chengd38eebc2013-08-28 14:38:14 -0700434 processQueue();
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700435 }
Santos Cordonad078192013-08-28 15:14:54 -0700436 }
Santos Cordoncba1b442013-07-18 12:43:58 -0700437
Santos Cordonad078192013-08-28 15:14:54 -0700438 /**
439 * Makes initial service calls to set up callcommandservice and audio modes.
440 */
441 private void makeInitialServiceCalls() {
Santos Cordoncba1b442013-07-18 12:43:58 -0700442 try {
Santos Cordon12a03aa2013-09-12 23:34:05 -0700443 mCallHandlerServiceGuarded.startCallService(mCallCommandService);
Santos Cordonad078192013-08-28 15:14:54 -0700444
445 onSupportedAudioModeChange(mAudioRouter.getSupportedAudioModes());
Santos Cordon8fd0ec72013-08-29 16:44:43 -0700446 onAudioModeChange(mAudioRouter.getAudioMode(), mAudioRouter.getMute());
Santos Cordoncba1b442013-07-18 12:43:58 -0700447 } catch (RemoteException e) {
Santos Cordon63a84242013-07-23 13:32:52 -0700448 Log.e(TAG, "Remote exception calling CallHandlerService::setCallCommandService", e);
Santos Cordon89647a62013-07-16 13:38:09 -0700449 }
450 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700451
Chiao Chengd38eebc2013-08-28 14:38:14 -0700452 private List<QueueParams> getQueue() {
453 if (mQueue == null) {
454 mQueue = Lists.newArrayList();
455 }
456 return mQueue;
457 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700458
459 private void enqueueDisconnect(Call call) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700460 getQueue().add(new QueueParams(QueueParams.METHOD_DISCONNECT, new Call(call)));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700461 }
462
463 private void enqueueIncoming(Call call) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700464 getQueue().add(new QueueParams(QueueParams.METHOD_INCOMING, new Call(call)));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700465 }
466
467 private void enqueueUpdate(List<Call> calls) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700468 final List<Call> copy = Lists.newArrayList();
469 for (Call call : calls) {
470 copy.add(new Call(call));
471 }
Chiao Chengc340ba92013-08-30 13:04:46 -0700472 getQueue().add(new QueueParams(QueueParams.METHOD_UPDATE, copy));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700473 }
474
Chiao Chengd38eebc2013-08-28 14:38:14 -0700475 private void processQueue() {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700476 synchronized (mServiceAndQueueLock) {
477 if (mQueue != null) {
478 for (QueueParams params : mQueue) {
479 switch (params.mMethod) {
480 case QueueParams.METHOD_INCOMING:
481 processIncoming((Call) params.mArg);
482 break;
483 case QueueParams.METHOD_UPDATE:
484 processUpdate((List<Call>) params.mArg);
485 break;
486 case QueueParams.METHOD_DISCONNECT:
487 processDisconnect((Call) params.mArg);
488 break;
489 default:
490 throw new IllegalArgumentException("Method type " + params.mMethod +
491 " not recognized.");
492 }
493 }
494 mQueue.clear();
495 mQueue = null;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700496 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700497 }
498 }
499
Chiao Chengd38eebc2013-08-28 14:38:14 -0700500 /**
501 * Holds method parameters.
502 */
503 private static class QueueParams {
504 private static final int METHOD_INCOMING = 1;
505 private static final int METHOD_UPDATE = 2;
506 private static final int METHOD_DISCONNECT = 3;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700507
Chiao Chengd38eebc2013-08-28 14:38:14 -0700508 private final int mMethod;
509 private final Object mArg;
510
511 private QueueParams(int method, Object arg) {
512 mMethod = method;
513 this.mArg = arg;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700514 }
515 }
Santos Cordon89647a62013-07-16 13:38:09 -0700516}