blob: 06b10ab23c6fab4e6a95ceb5e03a12a4ca30d9bf [file]
/*
* 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<String> textResponses) {
try {
Log.i(TAG, "onIncomingCall: " + call);
Map.Entry<Call, List<String>> incomingCall
= new AbstractMap.SimpleEntry<Call, List<String>>(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<Call> 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<Call>) msg.obj);
break;
case ON_UPDATE_CALL_WITH_TEXT_RESPONSES:
AbstractMap.SimpleEntry<Call, List<String>> entry
= (AbstractMap.SimpleEntry<Call, List<String>>) 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;
}
}
}