blob: 7c8ace72cd5b4bff48cdd9ea25645723e7fbc9f9 [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;
Santos Cordon70d38662013-10-21 17:12:23 -070059 public static final int RETRY_DELAY_LONG_MILLIS = 30 * 1000; // 30 seconds
Chiao Cheng6c6b2722013-08-22 18:35:54 -070060 private static final int BIND_RETRY_MSG = 1;
Santos Cordon70d38662013-10-21 17:12:23 -070061 private static final int MAX_SHORT_DELAY_RETRY_COUNT = 5;
Santos Cordon89647a62013-07-16 13:38:09 -070062
Santos Cordon593ab382013-08-06 21:58:23 -070063 private AudioRouter mAudioRouter;
Santos Cordoncba1b442013-07-18 12:43:58 -070064 private CallCommandService mCallCommandService;
Santos Cordon593ab382013-08-06 21:58:23 -070065 private CallModeler mCallModeler;
66 private Context mContext;
Santos Cordonf9ad0642013-09-26 10:48:07 -070067 private boolean mFullUpdateOnConnect;
Chiao Chengd38eebc2013-08-28 14:38:14 -070068
Chiao Cheng6c6b2722013-08-22 18:35:54 -070069 private ICallHandlerService mCallHandlerServiceGuarded; // Guarded by mServiceAndQueueLock
Chiao Chengd38eebc2013-08-28 14:38:14 -070070 // Single queue to guarantee ordering
71 private List<QueueParams> mQueue; // Guarded by mServiceAndQueueLock
72
Chiao Cheng6c6b2722013-08-22 18:35:54 -070073 private final Object mServiceAndQueueLock = new Object();
74 private int mBindRetryCount = 0;
75
76 @Override
77 public void handleMessage(Message msg) {
78 super.handleMessage(msg);
79
80 switch (msg.what) {
81 case BIND_RETRY_MSG:
Santos Cordon70d38662013-10-21 17:12:23 -070082 handleConnectRetry();
Chiao Cheng6c6b2722013-08-22 18:35:54 -070083 break;
84 }
85 }
Santos Cordon89647a62013-07-16 13:38:09 -070086
Santos Cordon63a84242013-07-23 13:32:52 -070087 public CallHandlerServiceProxy(Context context, CallModeler callModeler,
Santos Cordon593ab382013-08-06 21:58:23 -070088 CallCommandService callCommandService, AudioRouter audioRouter) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -070089 if (DBG) {
90 Log.d(TAG, "init CallHandlerServiceProxy");
91 }
Santos Cordon89647a62013-07-16 13:38:09 -070092 mContext = context;
Santos Cordoncba1b442013-07-18 12:43:58 -070093 mCallCommandService = callCommandService;
Santos Cordon63a84242013-07-23 13:32:52 -070094 mCallModeler = callModeler;
Santos Cordon593ab382013-08-06 21:58:23 -070095 mAudioRouter = audioRouter;
Santos Cordon89647a62013-07-16 13:38:09 -070096
Santos Cordon593ab382013-08-06 21:58:23 -070097 mAudioRouter.addAudioModeListener(this);
Christine Chendaf7bf62013-08-05 19:12:31 -070098 mCallModeler.addListener(this);
Santos Cordon63a84242013-07-23 13:32:52 -070099 }
Santos Cordon89647a62013-07-16 13:38:09 -0700100
Santos Cordon63a84242013-07-23 13:32:52 -0700101 @Override
Santos Cordon995c8162013-07-29 09:22:22 -0700102 public void onDisconnect(Call call) {
Chiao Cheng26c6e922013-09-14 16:45:38 -0700103 // Wake up in case the screen was off.
104 wakeUpScreen();
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700105 synchronized (mServiceAndQueueLock) {
106 if (mCallHandlerServiceGuarded == null) {
107 if (DBG) {
108 Log.d(TAG, "CallHandlerService not connected. Enqueue disconnect");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700109 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700110 enqueueDisconnect(call);
111 setupServiceConnection();
112 return;
Santos Cordon63a84242013-07-23 13:32:52 -0700113 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700114 }
115 processDisconnect(call);
116 }
117
Chiao Cheng26c6e922013-09-14 16:45:38 -0700118 private void wakeUpScreen() {
119 Log.d(TAG, "wakeUpScreen()");
120 final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
121 pm.wakeUp(SystemClock.uptimeMillis());
122 }
123
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700124 private void processDisconnect(Call call) {
125 try {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700126 if (DBG) {
127 Log.d(TAG, "onDisconnect: " + call);
128 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700129 synchronized (mServiceAndQueueLock) {
130 if (mCallHandlerServiceGuarded != null) {
131 mCallHandlerServiceGuarded.onDisconnect(call);
132 }
133 }
134 if (!mCallModeler.hasLiveCall()) {
135 unbind();
136 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700137 } catch (Exception e) {
138 Log.e(TAG, "Remote exception handling onDisconnect ", e);
Santos Cordon89647a62013-07-16 13:38:09 -0700139 }
140 }
141
Santos Cordona3d05142013-07-29 11:25:17 -0700142 @Override
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700143 public void onIncoming(Call call) {
Santos Cordon70d38662013-10-21 17:12:23 -0700144 // for new incoming calls, reset the retry count.
145 resetConnectRetryCount();
146
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700147 synchronized (mServiceAndQueueLock) {
148 if (mCallHandlerServiceGuarded == null) {
149 if (DBG) {
150 Log.d(TAG, "CallHandlerService not connected. Enqueue incoming.");
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700151 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700152 enqueueIncoming(call);
153 setupServiceConnection();
154 return;
Christine Chenee09a492013-08-06 16:02:29 -0700155 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700156 }
157 processIncoming(call);
158 }
159
160 private void processIncoming(Call call) {
161 if (DBG) {
162 Log.d(TAG, "onIncoming: " + call);
163 }
164 try {
Christine Chen3e0f0412013-09-18 20:33:49 -0700165 // TODO: check RespondViaSmsManager.allowRespondViaSmsForCall()
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700166 // must refactor call method to accept proper call object.
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700167 synchronized (mServiceAndQueueLock) {
168 if (mCallHandlerServiceGuarded != null) {
169 mCallHandlerServiceGuarded.onIncoming(call,
170 RejectWithTextMessageManager.loadCannedResponses());
171 }
172 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700173 } catch (Exception e) {
174 Log.e(TAG, "Remote exception handling onUpdate", e);
Christine Chenee09a492013-08-06 16:02:29 -0700175 }
176 }
177
178 @Override
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700179 public void onUpdate(List<Call> calls) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700180 synchronized (mServiceAndQueueLock) {
181 if (mCallHandlerServiceGuarded == null) {
182 if (DBG) {
183 Log.d(TAG, "CallHandlerService not connected. Enqueue update.");
184 }
185 enqueueUpdate(calls);
186 setupServiceConnection();
187 return;
188 }
189 }
190 processUpdate(calls);
191 }
192
193 private void processUpdate(List<Call> calls) {
194 if (DBG) {
195 Log.d(TAG, "onUpdate: " + calls.toString());
196 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700197 try {
198 synchronized (mServiceAndQueueLock) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700199 if (mCallHandlerServiceGuarded != null) {
200 mCallHandlerServiceGuarded.onUpdate(calls);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700201 }
Santos Cordona3d05142013-07-29 11:25:17 -0700202 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700203 if (!mCallModeler.hasLiveCall()) {
204 // TODO: unbinding happens in both onUpdate and onDisconnect because the ordering
205 // is not deterministic. Unbinding in both ensures that the service is unbound.
206 // But it also makes this in-efficient because we are unbinding twice, which leads
207 // to the CallHandlerService performing onCreate() and onDestroy() twice for each
208 // disconnect.
209 unbind();
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700210 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700211 } catch (Exception e) {
212 Log.e(TAG, "Remote exception handling onUpdate", e);
Santos Cordona3d05142013-07-29 11:25:17 -0700213 }
214 }
215
Chiao Cheng3f015c92013-09-06 15:56:27 -0700216
217 @Override
Yorke Leede41f672013-09-19 13:46:55 -0700218 public void onPostDialAction(Connection.PostDialState state, int callId, String remainingChars,
219 char currentChar) {
220 if (state != PostDialState.WAIT) return;
Chiao Cheng3f015c92013-09-06 15:56:27 -0700221 try {
222 synchronized (mServiceAndQueueLock) {
223 if (mCallHandlerServiceGuarded == null) {
224 if (DBG) {
225 Log.d(TAG, "CallHandlerService not conneccted. Skipping "
226 + "onPostDialWait().");
227 }
228 return;
229 }
230 }
231
232 mCallHandlerServiceGuarded.onPostDialWait(callId, remainingChars);
233 } catch (Exception e) {
234 Log.e(TAG, "Remote exception handling onUpdate", e);
235 }
236 }
237
Santos Cordon9b7bac72013-08-06 08:04:52 -0700238 @Override
Santos Cordoncd95f622013-08-29 03:38:52 -0700239 public void onAudioModeChange(int newMode, boolean muted) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700240 try {
241 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700242 if (mCallHandlerServiceGuarded == null) {
243 if (DBG) {
244 Log.d(TAG, "CallHandlerService not conneccted. Skipping "
245 + "onAudioModeChange().");
246 }
247 return;
248 }
Santos Cordon9b7bac72013-08-06 08:04:52 -0700249 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700250
251 // Just do a simple log for now.
252 Log.i(TAG, "Updating with new audio mode: " + AudioMode.toString(newMode) +
Santos Cordoncd95f622013-08-29 03:38:52 -0700253 " with mute " + muted);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700254
Santos Cordoncd95f622013-08-29 03:38:52 -0700255 mCallHandlerServiceGuarded.onAudioModeChange(newMode, muted);
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700256 } catch (Exception e) {
257 Log.e(TAG, "Remote exception handling onAudioModeChange", e);
Santos Cordon9b7bac72013-08-06 08:04:52 -0700258 }
259 }
260
Santos Cordon593ab382013-08-06 21:58:23 -0700261 @Override
262 public void onSupportedAudioModeChange(int modeMask) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700263 try {
264 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700265 if (mCallHandlerServiceGuarded == null) {
266 if (DBG) {
267 Log.d(TAG, "CallHandlerService not conneccted. Skipping"
268 + "onSupportedAudioModeChange().");
269 }
270 return;
271 }
272 }
Santos Cordon593ab382013-08-06 21:58:23 -0700273
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700274 if (DBG) {
275 Log.d(TAG, "onSupportAudioModeChange: " + AudioMode.toString(modeMask));
276 }
277
278 mCallHandlerServiceGuarded.onSupportedAudioModeChange(modeMask);
279 } catch (Exception e) {
280 Log.e(TAG, "Remote exception handling onAudioModeChange", e);
281 }
282
283 }
284
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700285 private ServiceConnection mConnection = null;
286
287 private class InCallServiceConnection implements ServiceConnection {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700288 @Override public void onServiceConnected (ComponentName className, IBinder service){
289 if (DBG) {
290 Log.d(TAG, "Service Connected");
291 }
292 onCallHandlerServiceConnected(ICallHandlerService.Stub.asInterface(service));
Santos Cordon70d38662013-10-21 17:12:23 -0700293 resetConnectRetryCount();
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700294 }
295
296 @Override public void onServiceDisconnected (ComponentName className){
297 Log.i(TAG, "Disconnected from UI service.");
298 synchronized (mServiceAndQueueLock) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700299 // Technically, unbindService is un-necessary since the framework will schedule and
300 // restart the crashed service. But there is a exponential backoff for the restart.
301 // Unbind explicitly and setup again to avoid the backoff since it's important to
302 // always have an in call ui.
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700303 unbind();
304
Santos Cordonf9ad0642013-09-26 10:48:07 -0700305 reconnectOnRemainingCalls();
Santos Cordon593ab382013-08-06 21:58:23 -0700306 }
307 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700308 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700309
Makoto Onukibcf20992013-09-12 17:59:30 -0700310 public void bringToForeground(boolean showDialpad) {
Santos Cordon406c0342013-08-28 00:07:47 -0700311 // only support this call if the service is already connected.
Santos Cordon19d814b2013-08-28 14:58:17 -0700312 synchronized (mServiceAndQueueLock) {
313 if (mCallHandlerServiceGuarded != null && mCallModeler.hasLiveCall()) {
314 try {
Makoto Onukibcf20992013-09-12 17:59:30 -0700315 if (DBG) Log.d(TAG, "bringToForeground: " + showDialpad);
316 mCallHandlerServiceGuarded.bringToForeground(showDialpad);
Santos Cordon19d814b2013-08-28 14:58:17 -0700317 } catch (RemoteException e) {
318 Log.e(TAG, "Exception handling bringToForeground", e);
319 }
Santos Cordon406c0342013-08-28 00:07:47 -0700320 }
321 }
322 }
323
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700324 private static Intent getInCallServiceIntent(Context context) {
325 final Intent serviceIntent = new Intent(ICallHandlerService.class.getName());
326 final ComponentName component = new ComponentName(context.getResources().getString(
Yorke Leed3105fe2013-09-25 12:44:45 -0700327 R.string.ui_default_package), context.getResources().getString(
328 R.string.incall_default_class));
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700329 serviceIntent.setComponent(component);
330 return serviceIntent;
331 }
332
Santos Cordon89647a62013-07-16 13:38:09 -0700333 /**
Santos Cordon345350e2013-07-19 17:16:14 -0700334 * Sets up the connection with ICallHandlerService
Santos Cordon89647a62013-07-16 13:38:09 -0700335 */
336 private void setupServiceConnection() {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700337 if (!PhoneGlobals.sVoiceCapable) {
338 return;
339 }
Chiao Cheng11a4b652013-09-02 01:08:19 -0700340
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700341 final Intent serviceIntent = getInCallServiceIntent(mContext);
Chiao Cheng11a4b652013-09-02 01:08:19 -0700342 if (DBG) {
343 Log.d(TAG, "binding to service " + serviceIntent);
344 }
345
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700346 synchronized (mServiceAndQueueLock) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700347 if (mConnection == null) {
348 mConnection = new InCallServiceConnection();
349
Santos Cordon70d38662013-10-21 17:12:23 -0700350 boolean failedConnection = false;
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700351 final PackageManager packageManger = mContext.getPackageManager();
352 final List<ResolveInfo> services = packageManger.queryIntentServices(serviceIntent,
353 0);
Yorke Lee79840002013-09-16 14:30:56 -0700354
355 ServiceInfo serviceInfo = null;
356
357 for (int i = 0; i < services.size(); i++) {
358 final ResolveInfo info = services.get(i);
359 if (info.serviceInfo != null) {
360 if (Manifest.permission.BIND_CALL_SERVICE.equals(
361 info.serviceInfo.permission)) {
362 serviceInfo = info.serviceInfo;
363 break;
364 }
365 }
366 }
367
368 if (serviceInfo == null) {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700369 // Service not found, retry again after some delay
370 // This can happen if the service is being installed by the package manager.
371 // Between deletes and installs, bindService could get a silent service not
372 // found error.
Santos Cordon70d38662013-10-21 17:12:23 -0700373 Log.w(TAG, "Default call handler service not found.");
374 failedConnection = true;
375 } else {
376
377 serviceIntent.setComponent(new ComponentName(serviceInfo.packageName,
378 serviceInfo.name));
379 if (DBG) {
380 Log.d(TAG, "binding to service " + serviceIntent);
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700381 }
Santos Cordon70d38662013-10-21 17:12:23 -0700382 if (!mContext.bindService(serviceIntent, mConnection,
383 Context.BIND_AUTO_CREATE)) {
384 // This happens when the in-call package is in the middle of being installed
385 Log.w(TAG, "Could not bind to default call handler service: " +
386 serviceIntent.getComponent());
387 failedConnection = true;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700388 }
389 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700390
Santos Cordon70d38662013-10-21 17:12:23 -0700391 if (failedConnection) {
392 mConnection = null;
393 enqueueConnectRetry();
394 }
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700395 } else {
396 Log.d(TAG, "Service connection to in call service already started.");
Santos Cordonaf763a12013-08-19 20:04:58 -0700397 }
398 }
399 }
400
Santos Cordon70d38662013-10-21 17:12:23 -0700401 private void resetConnectRetryCount() {
402 mBindRetryCount = 0;
403 }
404
405 private void incrementRetryCount() {
406 // Reset to the short delay retry count to avoid overflow
407 if (Integer.MAX_VALUE == mBindRetryCount) {
408 mBindRetryCount = MAX_SHORT_DELAY_RETRY_COUNT;
409 }
410
411 mBindRetryCount++;
412 }
413
414 private void handleConnectRetry() {
415 // Remove any pending messages since we're already performing the action.
416 // If the call to setupServiceConnection() fails, it will queue up another retry.
417 removeMessages(BIND_RETRY_MSG);
418
419 // Something else triggered the connection, cancel.
420 if (mConnection != null) {
421 Log.i(TAG, "Retry: already connected.");
422 return;
423 }
424
425 if (mCallModeler.hasLiveCall()) {
426 // Update the count when we are actually trying the retry instead of when the
427 // retry is queued up.
428 incrementRetryCount();
429
430 Log.i(TAG, "Retrying connection: " + mBindRetryCount);
431 setupServiceConnection();
432 } else {
433 Log.i(TAG, "Canceling connection retry since there are no calls.");
434 // We are not currently connected and there is no call so lets not bother
435 // with the retry. Also, empty the queue of pending messages to send
436 // to the UI.
437 synchronized (mServiceAndQueueLock) {
438 if (mQueue != null) {
439 mQueue.clear();
440 }
441 }
442
443 // Since we have no calls, reset retry count.
444 resetConnectRetryCount();
445 }
446 }
447
448 /**
449 * Called after the connection failed and a retry is needed.
450 * Queues up a retry to happen with a delay.
451 */
452 private void enqueueConnectRetry() {
453 final boolean isLongDelay = (mBindRetryCount > MAX_SHORT_DELAY_RETRY_COUNT);
454 final int delay = isLongDelay ? RETRY_DELAY_LONG_MILLIS : RETRY_DELAY_MILLIS;
455
456 Log.w(TAG, "InCallUI Connection failed. Enqueuing delayed retry for " + delay + " ms." +
457 " retries(" + mBindRetryCount + ")");
458
459 sendEmptyMessageDelayed(BIND_RETRY_MSG, delay);
460 }
461
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700462 private void unbind() {
463 synchronized (mServiceAndQueueLock) {
Yorke Lee362cec22013-09-18 15:20:26 -0700464 // On unbind, reenable the notification shade and navigation bar just in case the
465 // in-call UI crashed on an incoming call.
466 final StatusBarHelper statusBarHelper = PhoneGlobals.getInstance().notificationMgr.
467 statusBarHelper;
468 statusBarHelper.enableSystemBarNavigation(true);
469 statusBarHelper.enableExpandedView(true);
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700470 if (mCallHandlerServiceGuarded != null) {
471 Log.d(TAG, "Unbinding service.");
472 mCallHandlerServiceGuarded = null;
473 mContext.unbindService(mConnection);
474 }
475 mConnection = null;
476 }
477 }
478
Santos Cordonaf763a12013-08-19 20:04:58 -0700479 /**
Santos Cordoncba1b442013-07-18 12:43:58 -0700480 * Called when the in-call UI service is connected. Send command interface to in-call.
481 */
Santos Cordon63a84242013-07-23 13:32:52 -0700482 private void onCallHandlerServiceConnected(ICallHandlerService callHandlerService) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700483
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700484 synchronized (mServiceAndQueueLock) {
485 mCallHandlerServiceGuarded = callHandlerService;
486
Santos Cordonad078192013-08-28 15:14:54 -0700487 // Before we send any updates, we need to set up the initial service calls.
488 makeInitialServiceCalls();
489
Chiao Chengd38eebc2013-08-28 14:38:14 -0700490 processQueue();
Santos Cordonf9ad0642013-09-26 10:48:07 -0700491
492 if (mFullUpdateOnConnect) {
493 mFullUpdateOnConnect = false;
494 onUpdate(mCallModeler.getFullList());
495 }
496 }
497 }
498
499 /**
500 * Checks to see if there are any live calls left, and if so, try reconnecting the UI.
501 */
502 private void reconnectOnRemainingCalls() {
503 if (mCallModeler.hasLiveCall()) {
504 mFullUpdateOnConnect = true;
505 setupServiceConnection();
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700506 }
Santos Cordonad078192013-08-28 15:14:54 -0700507 }
Santos Cordoncba1b442013-07-18 12:43:58 -0700508
Santos Cordonad078192013-08-28 15:14:54 -0700509 /**
510 * Makes initial service calls to set up callcommandservice and audio modes.
511 */
512 private void makeInitialServiceCalls() {
Santos Cordoncba1b442013-07-18 12:43:58 -0700513 try {
Santos Cordon12a03aa2013-09-12 23:34:05 -0700514 mCallHandlerServiceGuarded.startCallService(mCallCommandService);
Santos Cordonad078192013-08-28 15:14:54 -0700515
516 onSupportedAudioModeChange(mAudioRouter.getSupportedAudioModes());
Santos Cordon8fd0ec72013-08-29 16:44:43 -0700517 onAudioModeChange(mAudioRouter.getAudioMode(), mAudioRouter.getMute());
Santos Cordoncba1b442013-07-18 12:43:58 -0700518 } catch (RemoteException e) {
Santos Cordon63a84242013-07-23 13:32:52 -0700519 Log.e(TAG, "Remote exception calling CallHandlerService::setCallCommandService", e);
Santos Cordon89647a62013-07-16 13:38:09 -0700520 }
521 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700522
Chiao Chengd38eebc2013-08-28 14:38:14 -0700523 private List<QueueParams> getQueue() {
524 if (mQueue == null) {
525 mQueue = Lists.newArrayList();
526 }
527 return mQueue;
528 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700529
530 private void enqueueDisconnect(Call call) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700531 getQueue().add(new QueueParams(QueueParams.METHOD_DISCONNECT, new Call(call)));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700532 }
533
534 private void enqueueIncoming(Call call) {
Chiao Chengd38eebc2013-08-28 14:38:14 -0700535 getQueue().add(new QueueParams(QueueParams.METHOD_INCOMING, new Call(call)));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700536 }
537
538 private void enqueueUpdate(List<Call> calls) {
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700539 final List<Call> copy = Lists.newArrayList();
540 for (Call call : calls) {
541 copy.add(new Call(call));
542 }
Chiao Chengc340ba92013-08-30 13:04:46 -0700543 getQueue().add(new QueueParams(QueueParams.METHOD_UPDATE, copy));
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700544 }
545
Chiao Chengd38eebc2013-08-28 14:38:14 -0700546 private void processQueue() {
Chiao Cheng3e6486e2013-09-03 19:27:46 -0700547 synchronized (mServiceAndQueueLock) {
548 if (mQueue != null) {
549 for (QueueParams params : mQueue) {
550 switch (params.mMethod) {
551 case QueueParams.METHOD_INCOMING:
552 processIncoming((Call) params.mArg);
553 break;
554 case QueueParams.METHOD_UPDATE:
555 processUpdate((List<Call>) params.mArg);
556 break;
557 case QueueParams.METHOD_DISCONNECT:
558 processDisconnect((Call) params.mArg);
559 break;
560 default:
561 throw new IllegalArgumentException("Method type " + params.mMethod +
562 " not recognized.");
563 }
564 }
565 mQueue.clear();
566 mQueue = null;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700567 }
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700568 }
569 }
570
Chiao Chengd38eebc2013-08-28 14:38:14 -0700571 /**
572 * Holds method parameters.
573 */
574 private static class QueueParams {
575 private static final int METHOD_INCOMING = 1;
576 private static final int METHOD_UPDATE = 2;
577 private static final int METHOD_DISCONNECT = 3;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700578
Chiao Chengd38eebc2013-08-28 14:38:14 -0700579 private final int mMethod;
580 private final Object mArg;
581
582 private QueueParams(int method, Object arg) {
583 mMethod = method;
584 this.mArg = arg;
Chiao Cheng6c6b2722013-08-22 18:35:54 -0700585 }
586 }
Santos Cordon89647a62013-07-16 13:38:09 -0700587}