blob: c6d4346a88b6b7dc6fe7c7e33e102dd0429fd7ce [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;
37import com.android.services.telephony.common.AudioMode;
Santos Cordonf4046882013-07-25 18:49:27 -070038import com.android.services.telephony.common.Call;
Santos Cordon345350e2013-07-19 17:16:14 -070039import com.android.services.telephony.common.ICallHandlerService;
Chiao Cheng6c6b2722013-08-22 18:35:54 -070040import com.google.common.collect.Lists;
Santos Cordon89647a62013-07-16 13:38:09 -070041
Santos Cordona3d05142013-07-29 11:25:17 -070042import java.util.List;
43
Santos Cordon89647a62013-07-16 13:38:09 -070044/**
Santos Cordon345350e2013-07-19 17:16:14 -070045 * This class is responsible for passing through call state changes to the CallHandlerService.
Santos Cordon89647a62013-07-16 13:38:09 -070046 */
Chiao Cheng6c6b2722013-08-22 18:35:54 -070047public class CallHandlerServiceProxy extends Handler
48 implements CallModeler.Listener, AudioModeListener {
Santos Cordon89647a62013-07-16 13:38:09 -070049
Santos Cordon345350e2013-07-19 17:16:14 -070050 private static final String TAG = CallHandlerServiceProxy.class.getSimpleName();
Santos Cordon71d5c6e2013-09-05 21:34:33 -070051 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt(
Chiao Cheng6c6b2722013-08-22 18:35:54 -070052 "ro.debuggable", 0) == 1);
Chiao Chenge41661c2013-07-23 13:28:26 -070053
Chiao Cheng6c6b2722013-08-22 18:35:54 -070054 public static final int RETRY_DELAY_MILLIS = 2000;
55 private static final int BIND_RETRY_MSG = 1;
56 private static final int MAX_RETRY_COUNT = 5;
Santos Cordon89647a62013-07-16 13:38:09 -070057
Santos Cordon593ab382013-08-06 21:58:23 -070058 private AudioRouter mAudioRouter;
Santos Cordoncba1b442013-07-18 12:43:58 -070059 private CallCommandService mCallCommandService;
Santos Cordon593ab382013-08-06 21:58:23 -070060 private CallModeler mCallModeler;
61 private Context mContext;
Chiao Chengd38eebc2013-08-28 14:38:14 -070062
Chiao Cheng6c6b2722013-08-22 18:35:54 -070063 private ICallHandlerService mCallHandlerServiceGuarded; // Guarded by mServiceAndQueueLock
Chiao Chengd38eebc2013-08-28 14:38:14 -070064 // Single queue to guarantee ordering
65 private List<QueueParams> mQueue; // Guarded by mServiceAndQueueLock
66
Chiao Cheng6c6b2722013-08-22 18:35:54 -070067 private final Object mServiceAndQueueLock = new Object();
68 private int mBindRetryCount = 0;
69
70 @Override
71 public void handleMessage(Message msg) {
72 super.handleMessage(msg);
73
74 switch (msg.what) {
75 case BIND_RETRY_MSG:
76 setupServiceConnection();
77 break;
78 }
79 }
Santos Cordon89647a62013-07-16 13:38:09 -070080
Santos Cordon63a84242013-07-23 13:32:52 -070081 public CallHandlerServiceProxy(Context context, CallModeler callModeler,
Santos Cordon593ab382013-08-06 21:58:23 -070082 CallCommandService callCommandService, AudioRouter audioRouter) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -070083 if (DBG) {
84 Log.d(TAG, "init CallHandlerServiceProxy");
85 }
Santos Cordon89647a62013-07-16 13:38:09 -070086 mContext = context;
Santos Cordoncba1b442013-07-18 12:43:58 -070087 mCallCommandService = callCommandService;
Santos Cordon63a84242013-07-23 13:32:52 -070088 mCallModeler = callModeler;
Santos Cordon593ab382013-08-06 21:58:23 -070089 mAudioRouter = audioRouter;
Santos Cordon89647a62013-07-16 13:38:09 -070090
Santos Cordon593ab382013-08-06 21:58:23 -070091 mAudioRouter.addAudioModeListener(this);
Christine Chendaf7bf62013-08-05 19:12:31 -070092 mCallModeler.addListener(this);
Santos Cordon63a84242013-07-23 13:32:52 -070093 }
Santos Cordon89647a62013-07-16 13:38:09 -070094
Santos Cordon63a84242013-07-23 13:32:52 -070095 @Override
Santos Cordon995c8162013-07-29 09:22:22 -070096 public void onDisconnect(Call call) {
Chiao Cheng26c6e922013-09-14 16:45:38 -070097 // Wake up in case the screen was off.
98 wakeUpScreen();
Chiao Cheng3e6486e2013-09-03 19:27:46 -070099 synchronized (mServiceAndQueueLock) {
100 if (mCallHandlerServiceGuarded == null) {
101 if (DBG) {
102 Log.d(TAG, "CallHandlerService not connected. Enqueue disconnect");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700103 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700104 enqueueDisconnect(call);
105 setupServiceConnection();
106 return;
Santos Cordon63a84242013-07-23 13:32:52 -0700107 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700108 }
109 processDisconnect(call);
110 }
111
Chiao Cheng26c6e922013-09-14 16:45:38 -0700112 private void wakeUpScreen() {
113 Log.d(TAG, "wakeUpScreen()");
114 final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
115 pm.wakeUp(SystemClock.uptimeMillis());
116 }
117
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700118 private void processDisconnect(Call call) {
119 try {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700120 if (DBG) {
121 Log.d(TAG, "onDisconnect: " + call);
122 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700123 synchronized (mServiceAndQueueLock) {
124 if (mCallHandlerServiceGuarded != null) {
125 mCallHandlerServiceGuarded.onDisconnect(call);
126 }
127 }
128 if (!mCallModeler.hasLiveCall()) {
129 unbind();
130 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700131 } catch (Exception e) {
132 Log.e(TAG, "Remote exception handling onDisconnect ", e);
Santos Cordon89647a62013-07-16 13:38:09 -0700133 }
134 }
135
Santos Cordona3d05142013-07-29 11:25:17 -0700136 @Override
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700137 public void onIncoming(Call call) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700138 synchronized (mServiceAndQueueLock) {
139 if (mCallHandlerServiceGuarded == null) {
140 if (DBG) {
141 Log.d(TAG, "CallHandlerService not connected. Enqueue incoming.");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700142 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700143 enqueueIncoming(call);
144 setupServiceConnection();
145 return;
Christine Chenee09a492013-08-06 16:02:29 -0700146 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700147 }
148 processIncoming(call);
149 }
150
151 private void processIncoming(Call call) {
152 if (DBG) {
153 Log.d(TAG, "onIncoming: " + call);
154 }
155 try {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700156 // TODO(klp): check RespondViaSmsManager.allowRespondViaSmsForCall()
157 // must refactor call method to accept proper call object.
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700158 synchronized (mServiceAndQueueLock) {
159 if (mCallHandlerServiceGuarded != null) {
160 mCallHandlerServiceGuarded.onIncoming(call,
161 RejectWithTextMessageManager.loadCannedResponses());
162 }
163 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700164 } catch (Exception e) {
165 Log.e(TAG, "Remote exception handling onUpdate", e);
Christine Chenee09a492013-08-06 16:02:29 -0700166 }
167 }
168
169 @Override
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700170 public void onUpdate(List<Call> calls) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700171 synchronized (mServiceAndQueueLock) {
172 if (mCallHandlerServiceGuarded == null) {
173 if (DBG) {
174 Log.d(TAG, "CallHandlerService not connected. Enqueue update.");
175 }
176 enqueueUpdate(calls);
177 setupServiceConnection();
178 return;
179 }
180 }
181 processUpdate(calls);
182 }
183
184 private void processUpdate(List<Call> calls) {
185 if (DBG) {
186 Log.d(TAG, "onUpdate: " + calls.toString());
187 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700188 try {
189 synchronized (mServiceAndQueueLock) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700190 if (mCallHandlerServiceGuarded != null) {
191 mCallHandlerServiceGuarded.onUpdate(calls);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700192 }
Santos Cordona3d05142013-07-29 11:25:17 -0700193 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700194 if (!mCallModeler.hasLiveCall()) {
195 // TODO: unbinding happens in both onUpdate and onDisconnect because the ordering
196 // is not deterministic. Unbinding in both ensures that the service is unbound.
197 // But it also makes this in-efficient because we are unbinding twice, which leads
198 // to the CallHandlerService performing onCreate() and onDestroy() twice for each
199 // disconnect.
200 unbind();
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700201 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700202 } catch (Exception e) {
203 Log.e(TAG, "Remote exception handling onUpdate", e);
Santos Cordona3d05142013-07-29 11:25:17 -0700204 }
205 }
206
Chiao Cheng3f015c92013-09-06 15:56:27 -0700207
208 @Override
209 public void onPostDialWait(int callId, String remainingChars) {
210 try {
211 synchronized (mServiceAndQueueLock) {
212 if (mCallHandlerServiceGuarded == null) {
213 if (DBG) {
214 Log.d(TAG, "CallHandlerService not conneccted. Skipping "
215 + "onPostDialWait().");
216 }
217 return;
218 }
219 }
220
221 mCallHandlerServiceGuarded.onPostDialWait(callId, remainingChars);
222 } catch (Exception e) {
223 Log.e(TAG, "Remote exception handling onUpdate", e);
224 }
225 }
226
Santos Cordon9b7bac72013-08-06 08:04:52 -0700227 @Override
Santos Cordoncd95f622013-08-29 03:38:52 -0700228 public void onAudioModeChange(int newMode, boolean muted) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700229 try {
230 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700231 if (mCallHandlerServiceGuarded == null) {
232 if (DBG) {
233 Log.d(TAG, "CallHandlerService not conneccted. Skipping "
234 + "onAudioModeChange().");
235 }
236 return;
237 }
Santos Cordon9b7bac72013-08-06 08:04:52 -0700238 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700239
240 // Just do a simple log for now.
241 Log.i(TAG, "Updating with new audio mode: " + AudioMode.toString(newMode) +
Santos Cordoncd95f622013-08-29 03:38:52 -0700242 " with mute " + muted);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700243
Santos Cordoncd95f622013-08-29 03:38:52 -0700244 mCallHandlerServiceGuarded.onAudioModeChange(newMode, muted);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700245 } catch (Exception e) {
246 Log.e(TAG, "Remote exception handling onAudioModeChange", e);
Santos Cordon9b7bac72013-08-06 08:04:52 -0700247 }
248 }
249
Santos Cordon593ab382013-08-06 21:58:23 -0700250 @Override
251 public void onSupportedAudioModeChange(int modeMask) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700252 try {
253 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700254 if (mCallHandlerServiceGuarded == null) {
255 if (DBG) {
256 Log.d(TAG, "CallHandlerService not conneccted. Skipping"
257 + "onSupportedAudioModeChange().");
258 }
259 return;
260 }
261 }
Santos Cordon593ab382013-08-06 21:58:23 -0700262
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700263 if (DBG) {
264 Log.d(TAG, "onSupportAudioModeChange: " + AudioMode.toString(modeMask));
265 }
266
267 mCallHandlerServiceGuarded.onSupportedAudioModeChange(modeMask);
268 } catch (Exception e) {
269 Log.e(TAG, "Remote exception handling onAudioModeChange", e);
270 }
271
272 }
273
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700274 private ServiceConnection mConnection = null;
275
276 private class InCallServiceConnection implements ServiceConnection {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700277 @Override public void onServiceConnected (ComponentName className, IBinder service){
278 if (DBG) {
279 Log.d(TAG, "Service Connected");
280 }
281 onCallHandlerServiceConnected(ICallHandlerService.Stub.asInterface(service));
Chiao Cheng11a4b652013-09-02 01:08:19 -0700282 mBindRetryCount = 0;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700283 }
284
285 @Override public void onServiceDisconnected (ComponentName className){
286 Log.i(TAG, "Disconnected from UI service.");
287 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700288 // Technically, unbindService is un-necessary since the framework will schedule and
289 // restart the crashed service. But there is a exponential backoff for the restart.
290 // Unbind explicitly and setup again to avoid the backoff since it's important to
291 // always have an in call ui.
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700292 unbind();
293
294 // TODO(klp): hang up all calls.
Santos Cordon593ab382013-08-06 21:58:23 -0700295 }
296 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700297 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700298
Makoto Onukibcf20992013-09-12 17:59:30 -0700299 public void bringToForeground(boolean showDialpad) {
Santos Cordon406c0342013-08-28 00:07:47 -0700300 // only support this call if the service is already connected.
Santos Cordon19d814b2013-08-28 14:58:17 -0700301 synchronized (mServiceAndQueueLock) {
302 if (mCallHandlerServiceGuarded != null && mCallModeler.hasLiveCall()) {
303 try {
Makoto Onukibcf20992013-09-12 17:59:30 -0700304 if (DBG) Log.d(TAG, "bringToForeground: " + showDialpad);
305 mCallHandlerServiceGuarded.bringToForeground(showDialpad);
Santos Cordon19d814b2013-08-28 14:58:17 -0700306 } catch (RemoteException e) {
307 Log.e(TAG, "Exception handling bringToForeground", e);
308 }
Santos Cordon406c0342013-08-28 00:07:47 -0700309 }
310 }
311 }
312
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700313 private static Intent getInCallServiceIntent(Context context) {
314 final Intent serviceIntent = new Intent(ICallHandlerService.class.getName());
315 final ComponentName component = new ComponentName(context.getResources().getString(
316 R.string.incall_ui_default_package), context.getResources().getString(
317 R.string.incall_ui_default_class));
318 serviceIntent.setComponent(component);
319 return serviceIntent;
320 }
321
Santos Cordon89647a62013-07-16 13:38:09 -0700322 /**
Santos Cordon345350e2013-07-19 17:16:14 -0700323 * Sets up the connection with ICallHandlerService
Santos Cordon89647a62013-07-16 13:38:09 -0700324 */
325 private void setupServiceConnection() {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700326 if (!PhoneGlobals.sVoiceCapable) {
327 return;
328 }
Chiao Cheng11a4b652013-09-02 01:08:19 -0700329
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700330 final Intent serviceIntent = getInCallServiceIntent(mContext);
Chiao Cheng11a4b652013-09-02 01:08:19 -0700331 if (DBG) {
332 Log.d(TAG, "binding to service " + serviceIntent);
333 }
334
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700335 synchronized (mServiceAndQueueLock) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700336 if (mConnection == null) {
337 mConnection = new InCallServiceConnection();
338
339 final PackageManager packageManger = mContext.getPackageManager();
340 final List<ResolveInfo> services = packageManger.queryIntentServices(serviceIntent,
341 0);
Yorke Lee79840002013-09-16 14:30:56 -0700342
343 ServiceInfo serviceInfo = null;
344
345 for (int i = 0; i < services.size(); i++) {
346 final ResolveInfo info = services.get(i);
347 if (info.serviceInfo != null) {
348 if (Manifest.permission.BIND_CALL_SERVICE.equals(
349 info.serviceInfo.permission)) {
350 serviceInfo = info.serviceInfo;
351 break;
352 }
353 }
354 }
355
356 if (serviceInfo == null) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700357 // Service not found, retry again after some delay
358 // This can happen if the service is being installed by the package manager.
359 // Between deletes and installs, bindService could get a silent service not
360 // found error.
361 mBindRetryCount++;
362 if (mBindRetryCount < MAX_RETRY_COUNT) {
363 Log.w(TAG, "InCallUI service not found. " + serviceIntent
364 + ". This happens if the service is being installed and should be"
365 + " transient. Retrying" + RETRY_DELAY_MILLIS + " ms.");
366 sendMessageDelayed(Message.obtain(this, BIND_RETRY_MSG),
367 RETRY_DELAY_MILLIS);
368 } else {
369 Log.e(TAG, "Tried to bind to in-call UI " + MAX_RETRY_COUNT + " times."
370 + " Giving up.");
371 }
372 return;
373 }
374
Yorke Lee79840002013-09-16 14:30:56 -0700375 // Bind to the first service that has a permission
376 // TODO: Add UI to allow us to select between services
377
378 serviceIntent.setComponent(new ComponentName(serviceInfo.packageName,
379 serviceInfo.name));
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700380 if (DBG) {
381 Log.d(TAG, "binding to service " + serviceIntent);
382 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700383 if (!mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700384 // This happens when the in-call package is in the middle of being
385 // installed.
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700386 // Delay the retry.
387 mBindRetryCount++;
388 if (mBindRetryCount < MAX_RETRY_COUNT) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700389 Log.e(TAG, "bindService failed on " + serviceIntent + ". Retrying in "
390 + RETRY_DELAY_MILLIS + " ms.");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700391 sendMessageDelayed(Message.obtain(this, BIND_RETRY_MSG),
392 RETRY_DELAY_MILLIS);
393 } else {
394 Log.wtf(TAG, "Tried to bind to in-call UI " + MAX_RETRY_COUNT + " times."
395 + " Giving up.");
396 }
397 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700398
399 } else {
400 Log.d(TAG, "Service connection to in call service already started.");
Santos Cordonaf763a12013-08-19 20:04:58 -0700401 }
402 }
403 }
404
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700405 private void unbind() {
406 synchronized (mServiceAndQueueLock) {
407 if (mCallHandlerServiceGuarded != null) {
408 Log.d(TAG, "Unbinding service.");
409 mCallHandlerServiceGuarded = null;
410 mContext.unbindService(mConnection);
411 }
412 mConnection = null;
413 }
414 }
415
Santos Cordonaf763a12013-08-19 20:04:58 -0700416 /**
Santos Cordoncba1b442013-07-18 12:43:58 -0700417 * Called when the in-call UI service is connected. Send command interface to in-call.
418 */
Santos Cordon63a84242013-07-23 13:32:52 -0700419 private void onCallHandlerServiceConnected(ICallHandlerService callHandlerService) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700420
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700421 synchronized (mServiceAndQueueLock) {
422 mCallHandlerServiceGuarded = callHandlerService;
423
Santos Cordonad078192013-08-28 15:14:54 -0700424 // Before we send any updates, we need to set up the initial service calls.
425 makeInitialServiceCalls();
426
Chiao Chengd38eebc2013-08-28 14:38:14 -0700427 processQueue();
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700428 }
Santos Cordonad078192013-08-28 15:14:54 -0700429 }
Santos Cordoncba1b442013-07-18 12:43:58 -0700430
Santos Cordonad078192013-08-28 15:14:54 -0700431 /**
432 * Makes initial service calls to set up callcommandservice and audio modes.
433 */
434 private void makeInitialServiceCalls() {
Santos Cordoncba1b442013-07-18 12:43:58 -0700435 try {
Santos Cordon12a03aa2013-09-12 23:34:05 -0700436 mCallHandlerServiceGuarded.startCallService(mCallCommandService);
Santos Cordonad078192013-08-28 15:14:54 -0700437
438 onSupportedAudioModeChange(mAudioRouter.getSupportedAudioModes());
Santos Cordon8fd0ec72013-08-29 16:44:43 -0700439 onAudioModeChange(mAudioRouter.getAudioMode(), mAudioRouter.getMute());
Santos Cordoncba1b442013-07-18 12:43:58 -0700440 } catch (RemoteException e) {
Santos Cordon63a84242013-07-23 13:32:52 -0700441 Log.e(TAG, "Remote exception calling CallHandlerService::setCallCommandService", e);
Santos Cordon89647a62013-07-16 13:38:09 -0700442 }
443 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700444
Chiao Chengd38eebc2013-08-28 14:38:14 -0700445 private List<QueueParams> getQueue() {
446 if (mQueue == null) {
447 mQueue = Lists.newArrayList();
448 }
449 return mQueue;
450 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700451
452 private void enqueueDisconnect(Call call) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700453 getQueue().add(new QueueParams(QueueParams.METHOD_DISCONNECT, new Call(call)));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700454 }
455
456 private void enqueueIncoming(Call call) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700457 getQueue().add(new QueueParams(QueueParams.METHOD_INCOMING, new Call(call)));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700458 }
459
460 private void enqueueUpdate(List<Call> calls) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700461 final List<Call> copy = Lists.newArrayList();
462 for (Call call : calls) {
463 copy.add(new Call(call));
464 }
Chiao Chengc340ba92013-08-30 13:04:46 -0700465 getQueue().add(new QueueParams(QueueParams.METHOD_UPDATE, copy));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700466 }
467
Chiao Chengd38eebc2013-08-28 14:38:14 -0700468 private void processQueue() {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700469 synchronized (mServiceAndQueueLock) {
470 if (mQueue != null) {
471 for (QueueParams params : mQueue) {
472 switch (params.mMethod) {
473 case QueueParams.METHOD_INCOMING:
474 processIncoming((Call) params.mArg);
475 break;
476 case QueueParams.METHOD_UPDATE:
477 processUpdate((List<Call>) params.mArg);
478 break;
479 case QueueParams.METHOD_DISCONNECT:
480 processDisconnect((Call) params.mArg);
481 break;
482 default:
483 throw new IllegalArgumentException("Method type " + params.mMethod +
484 " not recognized.");
485 }
486 }
487 mQueue.clear();
488 mQueue = null;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700489 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700490 }
491 }
492
Chiao Chengd38eebc2013-08-28 14:38:14 -0700493 /**
494 * Holds method parameters.
495 */
496 private static class QueueParams {
497 private static final int METHOD_INCOMING = 1;
498 private static final int METHOD_UPDATE = 2;
499 private static final int METHOD_DISCONNECT = 3;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700500
Chiao Chengd38eebc2013-08-28 14:38:14 -0700501 private final int mMethod;
502 private final Object mArg;
503
504 private QueueParams(int method, Object arg) {
505 mMethod = method;
506 this.mArg = arg;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700507 }
508 }
Santos Cordon89647a62013-07-16 13:38:09 -0700509}