/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.incallui; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Message; import com.android.services.telephony.common.AudioMode; import com.android.services.telephony.common.Call; import com.android.services.telephony.common.ICallCommandService; import com.android.services.telephony.common.ICallHandlerService; import java.util.AbstractMap; import java.util.List; import java.util.Map; /** * Service used to listen for call state changes. */ public class CallHandlerService extends Service { private final static String TAG = CallHandlerService.class.getSimpleName(); private static final int ON_UPDATE_CALL = 1; private static final int ON_UPDATE_MULTI_CALL = 2; private static final int ON_UPDATE_CALL_WITH_TEXT_RESPONSES = 3; private static final int ON_AUDIO_MODE = 4; private static final int ON_SUPPORTED_AUDIO_MODE = 5; private static final int ON_DISCONNECT_CALL = 6; private static final int ON_BRING_TO_FOREGROUND = 7; private static final int ON_POST_CHAR_WAIT = 8; private static final int ON_START = 9; private static final int ON_DESTROY = 10; private static final int LARGEST_MSG_ID = ON_DESTROY; private CallList mCallList; private Handler mMainHandler; private Object mHandlerInitLock = new Object(); private InCallPresenter mInCallPresenter; private AudioModeProvider mAudioModeProvider; private boolean mServiceStarted = false; @Override public void onCreate() { Log.i(TAG, "onCreate"); super.onCreate(); synchronized(mHandlerInitLock) { if (mMainHandler == null) { mMainHandler = new MainHandler(); } } } @Override public void onDestroy() { Log.i(TAG, "onDestroy"); // onDestroy will get called when: // 1) there are no more calls // 2) the client (TeleService) crashes. // // Because onDestroy is not sequenced with calls to CallHandlerService binder, // we cannot know which is happening. // Thats okay since in both cases we want to end all calls and let the UI know it can tear // itself down when it's ready. Start the destruction sequence. mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_DESTROY)); } @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind"); return mBinder; } @Override public boolean onUnbind(Intent intent) { Log.i(TAG, "onUnbind"); // Returning true here means we get called on rebind, which is a feature we do not need. // Return false so that all reconnections happen with a call to onBind(). return false; } private final ICallHandlerService.Stub mBinder = new ICallHandlerService.Stub() { @Override public void startCallService(ICallCommandService service) { try { Log.d(TAG, "startCallService: " + service.toString()); mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_START, service)); } catch (Exception e) { Log.e(TAG, "Error processing setCallCommandservice() call", e); } } @Override public void onDisconnect(Call call) { try { Log.i(TAG, "onDisconnected: " + call); mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_DISCONNECT_CALL, call)); } catch (Exception e) { Log.e(TAG, "Error processing onDisconnect() call.", e); } } @Override public void onIncoming(Call call, List textResponses) { try { Log.i(TAG, "onIncomingCall: " + call); Map.Entry> incomingCall = new AbstractMap.SimpleEntry>(call, textResponses); mMainHandler.sendMessage(mMainHandler.obtainMessage( ON_UPDATE_CALL_WITH_TEXT_RESPONSES, incomingCall)); } catch (Exception e) { Log.e(TAG, "Error processing onIncoming() call.", e); } } @Override public void onUpdate(List calls) { try { Log.i(TAG, "onUpdate: " + calls); mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_UPDATE_MULTI_CALL, calls)); } catch (Exception e) { Log.e(TAG, "Error processing onUpdate() call.", e); } } @Override public void onAudioModeChange(int mode, boolean muted) { try { Log.i(TAG, "onAudioModeChange : " + AudioMode.toString(mode)); mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_AUDIO_MODE, mode, muted ? 1 : 0, null)); } catch (Exception e) { Log.e(TAG, "Error processing onAudioModeChange() call.", e); } } @Override public void onSupportedAudioModeChange(int modeMask) { try { Log.i(TAG, "onSupportedAudioModeChange : " + AudioMode.toString(modeMask)); mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_SUPPORTED_AUDIO_MODE, modeMask, 0, null)); } catch (Exception e) { Log.e(TAG, "Error processing onSupportedAudioModeChange() call.", e); } } @Override public void bringToForeground(boolean showDialpad) { mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_BRING_TO_FOREGROUND, showDialpad ? 1 : 0, 0)); } @Override public void onPostDialWait(int callId, String chars) { mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_POST_CHAR_WAIT, callId, 0, chars)); } }; private void doStart(ICallCommandService service) { Log.i(TAG, "doStart"); // always setup the new callcommandservice CallCommandClient.getInstance().setService(service); // If we have a new service when one is already started, we can continue // using the service that we already have. if (mServiceStarted) { Log.i(TAG, "Starting a service before another one is completed"); doStop(); } mCallList = CallList.getInstance(); mAudioModeProvider = AudioModeProvider.getInstance(); mInCallPresenter = InCallPresenter.getInstance(); mInCallPresenter.setUp(getApplicationContext(), mCallList, mAudioModeProvider); mServiceStarted = true; } public void doStop() { Log.i(TAG, "doStop"); if (!mServiceStarted) { return; } mServiceStarted = false; // We are disconnected, clear the call list so that UI can start // tearing itself down. mCallList.clearOnDisconnect(); mCallList = null; mInCallPresenter.tearDown(); mInCallPresenter = null; mAudioModeProvider = null; } /** * Handles messages from the service so that they get executed on the main thread, where they * can interact with UI. */ private class MainHandler extends Handler { MainHandler() { super(getApplicationContext().getMainLooper(), null, true); } @Override public void handleMessage(Message msg) { executeMessage(msg); } } private void executeMessage(Message msg) { if (msg.what > LARGEST_MSG_ID) { // If you got here, you may have added a new message and forgotten to // update LARGEST_MSG_ID Log.wtf(TAG, "Cannot handle message larger than LARGEST_MSG_ID."); } // If we are not initialized, ignore all messages except start up if (!mServiceStarted && msg.what != ON_START) { Log.i(TAG, "System not initialized. Ignoring message: " + msg.what); return; } Log.d(TAG, "executeMessage " + msg.what); switch (msg.what) { case ON_UPDATE_CALL: Log.i(TAG, "ON_UPDATE_CALL: " + msg.obj); mCallList.onUpdate((Call) msg.obj); break; case ON_UPDATE_MULTI_CALL: Log.i(TAG, "ON_UPDATE_MULTI_CALL: " + msg.obj); mCallList.onUpdate((List) msg.obj); break; case ON_UPDATE_CALL_WITH_TEXT_RESPONSES: AbstractMap.SimpleEntry> entry = (AbstractMap.SimpleEntry>) msg.obj; Log.i(TAG, "ON_INCOMING_CALL: " + entry.getKey()); mCallList.onIncoming(entry.getKey(), entry.getValue()); break; case ON_DISCONNECT_CALL: Log.i(TAG, "ON_DISCONNECT_CALL: " + msg.obj); mCallList.onDisconnect((Call) msg.obj); break; case ON_POST_CHAR_WAIT: mInCallPresenter.onPostDialCharWait(msg.arg1, (String) msg.obj); break; case ON_AUDIO_MODE: Log.i(TAG, "ON_AUDIO_MODE: " + AudioMode.toString(msg.arg1) + ", muted (" + (msg.arg2 == 1) + ")"); mAudioModeProvider.onAudioModeChange(msg.arg1, msg.arg2 == 1); break; case ON_SUPPORTED_AUDIO_MODE: Log.i(TAG, "ON_SUPPORTED_AUDIO_MODE: " + AudioMode.toString( msg.arg1)); mAudioModeProvider.onSupportedAudioModeChange(msg.arg1); break; case ON_BRING_TO_FOREGROUND: Log.i(TAG, "ON_BRING_TO_FOREGROUND" + msg.arg1); if (mInCallPresenter != null) { mInCallPresenter.bringToForeground(msg.arg1 != 0); } break; case ON_START: doStart((ICallCommandService) msg.obj); break; case ON_DESTROY: doStop(); break; default: break; } } }