blob: 1d43fe86a84e340ce38f677f746446114631d1c1 [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
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
Chiao Cheng11a4b652013-09-02 01:08:19 -070023import android.content.pm.PackageManager;
24import android.content.pm.ResolveInfo;
Santos Cordon89647a62013-07-16 13:38:09 -070025import android.os.Handler;
26import android.os.IBinder;
27import android.os.Message;
Chiao Cheng26c6e922013-09-14 16:45:38 -070028import android.os.PowerManager;
Santos Cordon89647a62013-07-16 13:38:09 -070029import android.os.RemoteException;
Chiao Cheng26c6e922013-09-14 16:45:38 -070030import android.os.SystemClock;
Santos Cordon89647a62013-07-16 13:38:09 -070031import android.os.SystemProperties;
32import android.util.Log;
33
Santos Cordon9b7bac72013-08-06 08:04:52 -070034import com.android.phone.AudioRouter.AudioModeListener;
35import com.android.services.telephony.common.AudioMode;
Santos Cordonf4046882013-07-25 18:49:27 -070036import com.android.services.telephony.common.Call;
Santos Cordon345350e2013-07-19 17:16:14 -070037import com.android.services.telephony.common.ICallHandlerService;
Chiao Cheng6c6b2722013-08-22 18:35:54 -070038import com.google.common.collect.Lists;
Santos Cordon89647a62013-07-16 13:38:09 -070039
Santos Cordona3d05142013-07-29 11:25:17 -070040import java.util.List;
41
Santos Cordon89647a62013-07-16 13:38:09 -070042/**
Santos Cordon345350e2013-07-19 17:16:14 -070043 * This class is responsible for passing through call state changes to the CallHandlerService.
Santos Cordon89647a62013-07-16 13:38:09 -070044 */
Chiao Cheng6c6b2722013-08-22 18:35:54 -070045public class CallHandlerServiceProxy extends Handler
46 implements CallModeler.Listener, AudioModeListener {
Santos Cordon89647a62013-07-16 13:38:09 -070047
Santos Cordon345350e2013-07-19 17:16:14 -070048 private static final String TAG = CallHandlerServiceProxy.class.getSimpleName();
Santos Cordon71d5c6e2013-09-05 21:34:33 -070049 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt(
Chiao Cheng6c6b2722013-08-22 18:35:54 -070050 "ro.debuggable", 0) == 1);
Chiao Chenge41661c2013-07-23 13:28:26 -070051
Chiao Cheng6c6b2722013-08-22 18:35:54 -070052 public static final int RETRY_DELAY_MILLIS = 2000;
53 private static final int BIND_RETRY_MSG = 1;
54 private static final int MAX_RETRY_COUNT = 5;
Santos Cordon89647a62013-07-16 13:38:09 -070055
Santos Cordon593ab382013-08-06 21:58:23 -070056 private AudioRouter mAudioRouter;
Santos Cordoncba1b442013-07-18 12:43:58 -070057 private CallCommandService mCallCommandService;
Santos Cordon593ab382013-08-06 21:58:23 -070058 private CallModeler mCallModeler;
59 private Context mContext;
Chiao Chengd38eebc2013-08-28 14:38:14 -070060
Chiao Cheng6c6b2722013-08-22 18:35:54 -070061 private ICallHandlerService mCallHandlerServiceGuarded; // Guarded by mServiceAndQueueLock
Chiao Chengd38eebc2013-08-28 14:38:14 -070062 // Single queue to guarantee ordering
63 private List<QueueParams> mQueue; // Guarded by mServiceAndQueueLock
64
Chiao Cheng6c6b2722013-08-22 18:35:54 -070065 private final Object mServiceAndQueueLock = new Object();
66 private int mBindRetryCount = 0;
67
68 @Override
69 public void handleMessage(Message msg) {
70 super.handleMessage(msg);
71
72 switch (msg.what) {
73 case BIND_RETRY_MSG:
74 setupServiceConnection();
75 break;
76 }
77 }
Santos Cordon89647a62013-07-16 13:38:09 -070078
Santos Cordon63a84242013-07-23 13:32:52 -070079 public CallHandlerServiceProxy(Context context, CallModeler callModeler,
Santos Cordon593ab382013-08-06 21:58:23 -070080 CallCommandService callCommandService, AudioRouter audioRouter) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -070081 if (DBG) {
82 Log.d(TAG, "init CallHandlerServiceProxy");
83 }
Santos Cordon89647a62013-07-16 13:38:09 -070084 mContext = context;
Santos Cordoncba1b442013-07-18 12:43:58 -070085 mCallCommandService = callCommandService;
Santos Cordon63a84242013-07-23 13:32:52 -070086 mCallModeler = callModeler;
Santos Cordon593ab382013-08-06 21:58:23 -070087 mAudioRouter = audioRouter;
Santos Cordon89647a62013-07-16 13:38:09 -070088
Santos Cordon593ab382013-08-06 21:58:23 -070089 mAudioRouter.addAudioModeListener(this);
Christine Chendaf7bf62013-08-05 19:12:31 -070090 mCallModeler.addListener(this);
Santos Cordon63a84242013-07-23 13:32:52 -070091 }
Santos Cordon89647a62013-07-16 13:38:09 -070092
Santos Cordon63a84242013-07-23 13:32:52 -070093 @Override
Santos Cordon995c8162013-07-29 09:22:22 -070094 public void onDisconnect(Call call) {
Chiao Cheng26c6e922013-09-14 16:45:38 -070095 // Wake up in case the screen was off.
96 wakeUpScreen();
Chiao Cheng3e6486e2013-09-03 19:27:46 -070097 synchronized (mServiceAndQueueLock) {
98 if (mCallHandlerServiceGuarded == null) {
99 if (DBG) {
100 Log.d(TAG, "CallHandlerService not connected. Enqueue disconnect");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700101 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700102 enqueueDisconnect(call);
103 setupServiceConnection();
104 return;
Santos Cordon63a84242013-07-23 13:32:52 -0700105 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700106 }
107 processDisconnect(call);
108 }
109
Chiao Cheng26c6e922013-09-14 16:45:38 -0700110 private void wakeUpScreen() {
111 Log.d(TAG, "wakeUpScreen()");
112 final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
113 pm.wakeUp(SystemClock.uptimeMillis());
114 }
115
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700116 private void processDisconnect(Call call) {
117 try {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700118 if (DBG) {
119 Log.d(TAG, "onDisconnect: " + call);
120 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700121 synchronized (mServiceAndQueueLock) {
122 if (mCallHandlerServiceGuarded != null) {
123 mCallHandlerServiceGuarded.onDisconnect(call);
124 }
125 }
126 if (!mCallModeler.hasLiveCall()) {
127 unbind();
128 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700129 } catch (Exception e) {
130 Log.e(TAG, "Remote exception handling onDisconnect ", e);
Santos Cordon89647a62013-07-16 13:38:09 -0700131 }
132 }
133
Santos Cordona3d05142013-07-29 11:25:17 -0700134 @Override
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700135 public void onIncoming(Call call) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700136 synchronized (mServiceAndQueueLock) {
137 if (mCallHandlerServiceGuarded == null) {
138 if (DBG) {
139 Log.d(TAG, "CallHandlerService not connected. Enqueue incoming.");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700140 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700141 enqueueIncoming(call);
142 setupServiceConnection();
143 return;
Christine Chenee09a492013-08-06 16:02:29 -0700144 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700145 }
146 processIncoming(call);
147 }
148
149 private void processIncoming(Call call) {
150 if (DBG) {
151 Log.d(TAG, "onIncoming: " + call);
152 }
153 try {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700154 // TODO(klp): check RespondViaSmsManager.allowRespondViaSmsForCall()
155 // must refactor call method to accept proper call object.
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700156 synchronized (mServiceAndQueueLock) {
157 if (mCallHandlerServiceGuarded != null) {
158 mCallHandlerServiceGuarded.onIncoming(call,
159 RejectWithTextMessageManager.loadCannedResponses());
160 }
161 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700162 } catch (Exception e) {
163 Log.e(TAG, "Remote exception handling onUpdate", e);
Christine Chenee09a492013-08-06 16:02:29 -0700164 }
165 }
166
167 @Override
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700168 public void onUpdate(List<Call> calls) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700169 synchronized (mServiceAndQueueLock) {
170 if (mCallHandlerServiceGuarded == null) {
171 if (DBG) {
172 Log.d(TAG, "CallHandlerService not connected. Enqueue update.");
173 }
174 enqueueUpdate(calls);
175 setupServiceConnection();
176 return;
177 }
178 }
179 processUpdate(calls);
180 }
181
182 private void processUpdate(List<Call> calls) {
183 if (DBG) {
184 Log.d(TAG, "onUpdate: " + calls.toString());
185 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700186 try {
187 synchronized (mServiceAndQueueLock) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700188 if (mCallHandlerServiceGuarded != null) {
189 mCallHandlerServiceGuarded.onUpdate(calls);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700190 }
Santos Cordona3d05142013-07-29 11:25:17 -0700191 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700192 if (!mCallModeler.hasLiveCall()) {
193 // TODO: unbinding happens in both onUpdate and onDisconnect because the ordering
194 // is not deterministic. Unbinding in both ensures that the service is unbound.
195 // But it also makes this in-efficient because we are unbinding twice, which leads
196 // to the CallHandlerService performing onCreate() and onDestroy() twice for each
197 // disconnect.
198 unbind();
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700199 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700200 } catch (Exception e) {
201 Log.e(TAG, "Remote exception handling onUpdate", e);
Santos Cordona3d05142013-07-29 11:25:17 -0700202 }
203 }
204
Chiao Cheng3f015c92013-09-06 15:56:27 -0700205
206 @Override
207 public void onPostDialWait(int callId, String remainingChars) {
208 try {
209 synchronized (mServiceAndQueueLock) {
210 if (mCallHandlerServiceGuarded == null) {
211 if (DBG) {
212 Log.d(TAG, "CallHandlerService not conneccted. Skipping "
213 + "onPostDialWait().");
214 }
215 return;
216 }
217 }
218
219 mCallHandlerServiceGuarded.onPostDialWait(callId, remainingChars);
220 } catch (Exception e) {
221 Log.e(TAG, "Remote exception handling onUpdate", e);
222 }
223 }
224
Santos Cordon9b7bac72013-08-06 08:04:52 -0700225 @Override
Santos Cordoncd95f622013-08-29 03:38:52 -0700226 public void onAudioModeChange(int newMode, boolean muted) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700227 try {
228 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700229 if (mCallHandlerServiceGuarded == null) {
230 if (DBG) {
231 Log.d(TAG, "CallHandlerService not conneccted. Skipping "
232 + "onAudioModeChange().");
233 }
234 return;
235 }
Santos Cordon9b7bac72013-08-06 08:04:52 -0700236 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700237
238 // Just do a simple log for now.
239 Log.i(TAG, "Updating with new audio mode: " + AudioMode.toString(newMode) +
Santos Cordoncd95f622013-08-29 03:38:52 -0700240 " with mute " + muted);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700241
Santos Cordoncd95f622013-08-29 03:38:52 -0700242 mCallHandlerServiceGuarded.onAudioModeChange(newMode, muted);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700243 } catch (Exception e) {
244 Log.e(TAG, "Remote exception handling onAudioModeChange", e);
Santos Cordon9b7bac72013-08-06 08:04:52 -0700245 }
246 }
247
Santos Cordon593ab382013-08-06 21:58:23 -0700248 @Override
249 public void onSupportedAudioModeChange(int modeMask) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700250 try {
251 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700252 if (mCallHandlerServiceGuarded == null) {
253 if (DBG) {
254 Log.d(TAG, "CallHandlerService not conneccted. Skipping"
255 + "onSupportedAudioModeChange().");
256 }
257 return;
258 }
259 }
Santos Cordon593ab382013-08-06 21:58:23 -0700260
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700261 if (DBG) {
262 Log.d(TAG, "onSupportAudioModeChange: " + AudioMode.toString(modeMask));
263 }
264
265 mCallHandlerServiceGuarded.onSupportedAudioModeChange(modeMask);
266 } catch (Exception e) {
267 Log.e(TAG, "Remote exception handling onAudioModeChange", e);
268 }
269
270 }
271
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700272 private ServiceConnection mConnection = null;
273
274 private class InCallServiceConnection implements ServiceConnection {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700275 @Override public void onServiceConnected (ComponentName className, IBinder service){
276 if (DBG) {
277 Log.d(TAG, "Service Connected");
278 }
279 onCallHandlerServiceConnected(ICallHandlerService.Stub.asInterface(service));
Chiao Cheng11a4b652013-09-02 01:08:19 -0700280 mBindRetryCount = 0;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700281 }
282
283 @Override public void onServiceDisconnected (ComponentName className){
284 Log.i(TAG, "Disconnected from UI service.");
285 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700286 // Technically, unbindService is un-necessary since the framework will schedule and
287 // restart the crashed service. But there is a exponential backoff for the restart.
288 // Unbind explicitly and setup again to avoid the backoff since it's important to
289 // always have an in call ui.
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700290 unbind();
291
292 // TODO(klp): hang up all calls.
Santos Cordon593ab382013-08-06 21:58:23 -0700293 }
294 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700295 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700296
Makoto Onukibcf20992013-09-12 17:59:30 -0700297 public void bringToForeground(boolean showDialpad) {
Santos Cordon406c0342013-08-28 00:07:47 -0700298 // only support this call if the service is already connected.
Santos Cordon19d814b2013-08-28 14:58:17 -0700299 synchronized (mServiceAndQueueLock) {
300 if (mCallHandlerServiceGuarded != null && mCallModeler.hasLiveCall()) {
301 try {
Makoto Onukibcf20992013-09-12 17:59:30 -0700302 if (DBG) Log.d(TAG, "bringToForeground: " + showDialpad);
303 mCallHandlerServiceGuarded.bringToForeground(showDialpad);
Santos Cordon19d814b2013-08-28 14:58:17 -0700304 } catch (RemoteException e) {
305 Log.e(TAG, "Exception handling bringToForeground", e);
306 }
Santos Cordon406c0342013-08-28 00:07:47 -0700307 }
308 }
309 }
310
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700311 private static Intent getInCallServiceIntent(Context context) {
312 final Intent serviceIntent = new Intent(ICallHandlerService.class.getName());
313 final ComponentName component = new ComponentName(context.getResources().getString(
314 R.string.incall_ui_default_package), context.getResources().getString(
315 R.string.incall_ui_default_class));
316 serviceIntent.setComponent(component);
317 return serviceIntent;
318 }
319
Santos Cordon89647a62013-07-16 13:38:09 -0700320 /**
Santos Cordon345350e2013-07-19 17:16:14 -0700321 * Sets up the connection with ICallHandlerService
Santos Cordon89647a62013-07-16 13:38:09 -0700322 */
323 private void setupServiceConnection() {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700324 if (!PhoneGlobals.sVoiceCapable) {
325 return;
326 }
Chiao Cheng11a4b652013-09-02 01:08:19 -0700327
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700328 final Intent serviceIntent = getInCallServiceIntent(mContext);
Chiao Cheng11a4b652013-09-02 01:08:19 -0700329 if (DBG) {
330 Log.d(TAG, "binding to service " + serviceIntent);
331 }
332
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700333 synchronized (mServiceAndQueueLock) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700334 if (mConnection == null) {
335 mConnection = new InCallServiceConnection();
336
337 final PackageManager packageManger = mContext.getPackageManager();
338 final List<ResolveInfo> services = packageManger.queryIntentServices(serviceIntent,
339 0);
340 if (services.size() == 0) {
341 // Service not found, retry again after some delay
342 // This can happen if the service is being installed by the package manager.
343 // Between deletes and installs, bindService could get a silent service not
344 // found error.
345 mBindRetryCount++;
346 if (mBindRetryCount < MAX_RETRY_COUNT) {
347 Log.w(TAG, "InCallUI service not found. " + serviceIntent
348 + ". This happens if the service is being installed and should be"
349 + " transient. Retrying" + RETRY_DELAY_MILLIS + " ms.");
350 sendMessageDelayed(Message.obtain(this, BIND_RETRY_MSG),
351 RETRY_DELAY_MILLIS);
352 } else {
353 Log.e(TAG, "Tried to bind to in-call UI " + MAX_RETRY_COUNT + " times."
354 + " Giving up.");
355 }
356 return;
357 }
358
359 if (DBG) {
360 Log.d(TAG, "binding to service " + serviceIntent);
361 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700362 if (!mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700363 // This happens when the in-call package is in the middle of being
364 // installed.
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700365 // Delay the retry.
366 mBindRetryCount++;
367 if (mBindRetryCount < MAX_RETRY_COUNT) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700368 Log.e(TAG, "bindService failed on " + serviceIntent + ". Retrying in "
369 + RETRY_DELAY_MILLIS + " ms.");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700370 sendMessageDelayed(Message.obtain(this, BIND_RETRY_MSG),
371 RETRY_DELAY_MILLIS);
372 } else {
373 Log.wtf(TAG, "Tried to bind to in-call UI " + MAX_RETRY_COUNT + " times."
374 + " Giving up.");
375 }
376 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700377
378 } else {
379 Log.d(TAG, "Service connection to in call service already started.");
Santos Cordonaf763a12013-08-19 20:04:58 -0700380 }
381 }
382 }
383
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700384 private void unbind() {
385 synchronized (mServiceAndQueueLock) {
386 if (mCallHandlerServiceGuarded != null) {
387 Log.d(TAG, "Unbinding service.");
388 mCallHandlerServiceGuarded = null;
389 mContext.unbindService(mConnection);
390 }
391 mConnection = null;
392 }
393 }
394
Santos Cordonaf763a12013-08-19 20:04:58 -0700395 /**
Santos Cordoncba1b442013-07-18 12:43:58 -0700396 * Called when the in-call UI service is connected. Send command interface to in-call.
397 */
Santos Cordon63a84242013-07-23 13:32:52 -0700398 private void onCallHandlerServiceConnected(ICallHandlerService callHandlerService) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700399
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700400 synchronized (mServiceAndQueueLock) {
401 mCallHandlerServiceGuarded = callHandlerService;
402
Santos Cordonad078192013-08-28 15:14:54 -0700403 // Before we send any updates, we need to set up the initial service calls.
404 makeInitialServiceCalls();
405
Chiao Chengd38eebc2013-08-28 14:38:14 -0700406 processQueue();
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700407 }
Santos Cordonad078192013-08-28 15:14:54 -0700408 }
Santos Cordoncba1b442013-07-18 12:43:58 -0700409
Santos Cordonad078192013-08-28 15:14:54 -0700410 /**
411 * Makes initial service calls to set up callcommandservice and audio modes.
412 */
413 private void makeInitialServiceCalls() {
Santos Cordoncba1b442013-07-18 12:43:58 -0700414 try {
Santos Cordon12a03aa2013-09-12 23:34:05 -0700415 mCallHandlerServiceGuarded.startCallService(mCallCommandService);
Santos Cordonad078192013-08-28 15:14:54 -0700416
417 onSupportedAudioModeChange(mAudioRouter.getSupportedAudioModes());
Santos Cordon8fd0ec72013-08-29 16:44:43 -0700418 onAudioModeChange(mAudioRouter.getAudioMode(), mAudioRouter.getMute());
Santos Cordoncba1b442013-07-18 12:43:58 -0700419 } catch (RemoteException e) {
Santos Cordon63a84242013-07-23 13:32:52 -0700420 Log.e(TAG, "Remote exception calling CallHandlerService::setCallCommandService", e);
Santos Cordon89647a62013-07-16 13:38:09 -0700421 }
422 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700423
Chiao Chengd38eebc2013-08-28 14:38:14 -0700424 private List<QueueParams> getQueue() {
425 if (mQueue == null) {
426 mQueue = Lists.newArrayList();
427 }
428 return mQueue;
429 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700430
431 private void enqueueDisconnect(Call call) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700432 getQueue().add(new QueueParams(QueueParams.METHOD_DISCONNECT, new Call(call)));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700433 }
434
435 private void enqueueIncoming(Call call) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700436 getQueue().add(new QueueParams(QueueParams.METHOD_INCOMING, new Call(call)));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700437 }
438
439 private void enqueueUpdate(List<Call> calls) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700440 final List<Call> copy = Lists.newArrayList();
441 for (Call call : calls) {
442 copy.add(new Call(call));
443 }
Chiao Chengc340ba92013-08-30 13:04:46 -0700444 getQueue().add(new QueueParams(QueueParams.METHOD_UPDATE, copy));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700445 }
446
Chiao Chengd38eebc2013-08-28 14:38:14 -0700447 private void processQueue() {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700448 synchronized (mServiceAndQueueLock) {
449 if (mQueue != null) {
450 for (QueueParams params : mQueue) {
451 switch (params.mMethod) {
452 case QueueParams.METHOD_INCOMING:
453 processIncoming((Call) params.mArg);
454 break;
455 case QueueParams.METHOD_UPDATE:
456 processUpdate((List<Call>) params.mArg);
457 break;
458 case QueueParams.METHOD_DISCONNECT:
459 processDisconnect((Call) params.mArg);
460 break;
461 default:
462 throw new IllegalArgumentException("Method type " + params.mMethod +
463 " not recognized.");
464 }
465 }
466 mQueue.clear();
467 mQueue = null;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700468 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700469 }
470 }
471
Chiao Chengd38eebc2013-08-28 14:38:14 -0700472 /**
473 * Holds method parameters.
474 */
475 private static class QueueParams {
476 private static final int METHOD_INCOMING = 1;
477 private static final int METHOD_UPDATE = 2;
478 private static final int METHOD_DISCONNECT = 3;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700479
Chiao Chengd38eebc2013-08-28 14:38:14 -0700480 private final int mMethod;
481 private final Object mArg;
482
483 private QueueParams(int method, Object arg) {
484 mMethod = method;
485 this.mArg = arg;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700486 }
487 }
Santos Cordon89647a62013-07-16 13:38:09 -0700488}