1 /* 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 17 package com.android.incallui; 18 19 import android.app.Service; 20 import android.content.Intent; 21 import android.os.Handler; 22 import android.os.IBinder; 23 import android.os.Message; 24 25 import com.android.services.telephony.common.AudioMode; 26 import com.android.services.telephony.common.Call; 27 import com.android.services.telephony.common.ICallCommandService; 28 import com.android.services.telephony.common.ICallHandlerService; 29 30 import java.util.AbstractMap; 31 import java.util.List; 32 import java.util.Map; 33 34 /** 35 * Service used to listen for call state changes. 36 */ 37 public class CallHandlerService extends Service { 38 39 private final static String TAG = CallHandlerService.class.getSimpleName(); 40 41 private static final int ON_UPDATE_CALL = 1; 42 private static final int ON_UPDATE_MULTI_CALL = 2; 43 private static final int ON_UPDATE_CALL_WITH_TEXT_RESPONSES = 3; 44 private static final int ON_AUDIO_MODE = 4; 45 private static final int ON_SUPPORTED_AUDIO_MODE = 5; 46 private static final int ON_DISCONNECT_CALL = 6; 47 private static final int ON_BRING_TO_FOREGROUND = 7; 48 private static final int ON_POST_CHAR_WAIT = 8; 49 private static final int ON_START = 9; 50 private static final int ON_DESTROY = 10; 51 52 private static final int LARGEST_MSG_ID = ON_DESTROY; 53 54 55 private CallList mCallList; 56 private Handler mMainHandler; 57 private Object mHandlerInitLock = new Object(); 58 private InCallPresenter mInCallPresenter; 59 private AudioModeProvider mAudioModeProvider; 60 private boolean mServiceStarted = false; 61 62 @Override 63 public void onCreate() { 64 Log.i(TAG, "onCreate"); 65 super.onCreate(); 66 67 synchronized(mHandlerInitLock) { 68 if (mMainHandler == null) { 69 mMainHandler = new MainHandler(); 70 } 71 } 72 73 } 74 75 @Override 76 public void onDestroy() { 77 Log.i(TAG, "onDestroy"); 78 79 // onDestroy will get called when: 80 // 1) there are no more calls 81 // 2) the client (TeleService) crashes. 82 // 83 // Because onDestroy is not sequenced with calls to CallHandlerService binder, 84 // we cannot know which is happening. 85 // Thats okay since in both cases we want to end all calls and let the UI know it can tear 86 // itself down when it's ready. Start the destruction sequence. 87 mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_DESTROY)); 88 } 89 90 91 @Override 92 public IBinder onBind(Intent intent) { 93 Log.i(TAG, "onBind"); 94 return mBinder; 95 } 96 97 @Override 98 public boolean onUnbind(Intent intent) { 99 Log.i(TAG, "onUnbind"); 100 101 // Returning true here means we get called on rebind, which is a feature we do not need. 102 // Return false so that all reconnections happen with a call to onBind(). 103 return false; 104 } 105 106 private final ICallHandlerService.Stub mBinder = new ICallHandlerService.Stub() { 107 108 @Override 109 public void startCallService(ICallCommandService service) { 110 try { 111 Log.d(TAG, "startCallService: " + service.toString()); 112 113 mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_START, service)); 114 } catch (Exception e) { 115 Log.e(TAG, "Error processing setCallCommandservice() call", e); 116 } 117 } 118 119 @Override 120 public void onDisconnect(Call call) { 121 try { 122 Log.i(TAG, "onDisconnected: " + call); 123 mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_DISCONNECT_CALL, call)); 124 } catch (Exception e) { 125 Log.e(TAG, "Error processing onDisconnect() call.", e); 126 } 127 } 128 129 @Override 130 public void onIncoming(Call call, List<String> textResponses) { 131 try { 132 Log.i(TAG, "onIncomingCall: " + call); 133 Map.Entry<Call, List<String>> incomingCall 134 = new AbstractMap.SimpleEntry<Call, List<String>>(call, textResponses); 135 mMainHandler.sendMessage(mMainHandler.obtainMessage( 136 ON_UPDATE_CALL_WITH_TEXT_RESPONSES, incomingCall)); 137 } catch (Exception e) { 138 Log.e(TAG, "Error processing onIncoming() call.", e); 139 } 140 } 141 142 @Override 143 public void onUpdate(List<Call> calls) { 144 try { 145 Log.i(TAG, "onUpdate: " + calls); 146 mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_UPDATE_MULTI_CALL, calls)); 147 } catch (Exception e) { 148 Log.e(TAG, "Error processing onUpdate() call.", e); 149 } 150 } 151 152 @Override 153 public void onAudioModeChange(int mode, boolean muted) { 154 try { 155 Log.i(TAG, "onAudioModeChange : " + 156 AudioMode.toString(mode)); 157 mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_AUDIO_MODE, mode, 158 muted ? 1 : 0, null)); 159 } catch (Exception e) { 160 Log.e(TAG, "Error processing onAudioModeChange() call.", e); 161 } 162 } 163 164 @Override 165 public void onSupportedAudioModeChange(int modeMask) { 166 try { 167 Log.i(TAG, "onSupportedAudioModeChange : " + 168 AudioMode.toString(modeMask)); 169 mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_SUPPORTED_AUDIO_MODE, 170 modeMask, 0, null)); 171 } catch (Exception e) { 172 Log.e(TAG, "Error processing onSupportedAudioModeChange() call.", e); 173 } 174 } 175 176 @Override 177 public void bringToForeground(boolean showDialpad) { 178 mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_BRING_TO_FOREGROUND, 179 showDialpad ? 1 : 0, 0)); 180 } 181 182 @Override 183 public void onPostDialWait(int callId, String chars) { 184 mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_POST_CHAR_WAIT, callId, 0, 185 chars)); 186 } 187 }; 188 189 private void doStart(ICallCommandService service) { 190 Log.i(TAG, "doStart"); 191 192 // always setup the new callcommandservice 193 CallCommandClient.getInstance().setService(service); 194 195 // If we have a new service when one is already started, we can continue 196 // using the service that we already have. 197 if (mServiceStarted) { 198 Log.i(TAG, "Starting a service before another one is completed"); 199 doStop(); 200 } 201 202 mCallList = CallList.getInstance(); 203 mAudioModeProvider = AudioModeProvider.getInstance(); 204 mInCallPresenter = InCallPresenter.getInstance(); 205 206 mInCallPresenter.setUp(getApplicationContext(), mCallList, mAudioModeProvider); 207 208 mServiceStarted = true; 209 } 210 211 public void doStop() { 212 Log.i(TAG, "doStop"); 213 214 if (!mServiceStarted) { 215 return; 216 } 217 218 mServiceStarted = false; 219 220 // We are disconnected, clear the call list so that UI can start 221 // tearing itself down. 222 mCallList.clearOnDisconnect(); 223 mCallList = null; 224 225 mInCallPresenter.tearDown(); 226 mInCallPresenter = null; 227 mAudioModeProvider = null; 228 } 229 230 /** 231 * Handles messages from the service so that they get executed on the main thread, where they 232 * can interact with UI. 233 */ 234 private class MainHandler extends Handler { 235 MainHandler() { 236 super(getApplicationContext().getMainLooper(), null, true); 237 } 238 239 @Override 240 public void handleMessage(Message msg) { 241 executeMessage(msg); 242 } 243 } 244 245 private void executeMessage(Message msg) { 246 if (msg.what > LARGEST_MSG_ID) { 247 // If you got here, you may have added a new message and forgotten to 248 // update LARGEST_MSG_ID 249 Log.wtf(TAG, "Cannot handle message larger than LARGEST_MSG_ID."); 250 } 251 252 // If we are not initialized, ignore all messages except start up 253 if (!mServiceStarted && msg.what != ON_START) { 254 Log.i(TAG, "System not initialized. Ignoring message: " + msg.what); 255 return; 256 } 257 258 Log.d(TAG, "executeMessage " + msg.what); 259 260 switch (msg.what) { 261 case ON_UPDATE_CALL: 262 Log.i(TAG, "ON_UPDATE_CALL: " + msg.obj); 263 mCallList.onUpdate((Call) msg.obj); 264 break; 265 case ON_UPDATE_MULTI_CALL: 266 Log.i(TAG, "ON_UPDATE_MULTI_CALL: " + msg.obj); 267 mCallList.onUpdate((List<Call>) msg.obj); 268 break; 269 case ON_UPDATE_CALL_WITH_TEXT_RESPONSES: 270 AbstractMap.SimpleEntry<Call, List<String>> entry 271 = (AbstractMap.SimpleEntry<Call, List<String>>) msg.obj; 272 Log.i(TAG, "ON_INCOMING_CALL: " + entry.getKey()); 273 mCallList.onIncoming(entry.getKey(), entry.getValue()); 274 break; 275 case ON_DISCONNECT_CALL: 276 Log.i(TAG, "ON_DISCONNECT_CALL: " + msg.obj); 277 mCallList.onDisconnect((Call) msg.obj); 278 break; 279 case ON_POST_CHAR_WAIT: 280 mInCallPresenter.onPostDialCharWait(msg.arg1, (String) msg.obj); 281 break; 282 case ON_AUDIO_MODE: 283 Log.i(TAG, "ON_AUDIO_MODE: " + 284 AudioMode.toString(msg.arg1) + ", muted (" + (msg.arg2 == 1) + ")"); 285 mAudioModeProvider.onAudioModeChange(msg.arg1, msg.arg2 == 1); 286 break; 287 case ON_SUPPORTED_AUDIO_MODE: 288 Log.i(TAG, "ON_SUPPORTED_AUDIO_MODE: " + AudioMode.toString( 289 msg.arg1)); 290 291 mAudioModeProvider.onSupportedAudioModeChange(msg.arg1); 292 break; 293 case ON_BRING_TO_FOREGROUND: 294 Log.i(TAG, "ON_BRING_TO_FOREGROUND" + msg.arg1); 295 if (mInCallPresenter != null) { 296 mInCallPresenter.bringToForeground(msg.arg1 != 0); 297 } 298 break; 299 case ON_START: 300 doStart((ICallCommandService) msg.obj); 301 break; 302 case ON_DESTROY: 303 doStop(); 304 break; 305 default: 306 break; 307 } 308 } 309 } 310