Home | History | Annotate | Download | only in incallui
      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