Home | History | Annotate | Download | only in telephony
      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.internal.telephony;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.os.Build;
     23 import android.os.Message;
     24 import android.os.PowerManager;
     25 import android.telephony.Rlog;
     26 
     27 import com.android.internal.util.State;
     28 import com.android.internal.util.StateMachine;
     29 
     30 /**
     31  * Generic state machine for handling messages and waiting for ordered broadcasts to complete.
     32  * Subclasses implement {@link #handleSmsMessage}, which returns true to transition into waiting
     33  * state, or false to remain in idle state. The wakelock is acquired on exit from idle state,
     34  * and is released a few seconds after returning to idle state, or immediately upon calling
     35  * {@link #quit}.
     36  */
     37 public abstract class WakeLockStateMachine extends StateMachine {
     38     protected static final boolean DBG = true;    // TODO: change to false
     39 
     40     private final PowerManager.WakeLock mWakeLock;
     41 
     42     /** New message to process. */
     43     public static final int EVENT_NEW_SMS_MESSAGE = 1;
     44 
     45     /** Result receiver called for current cell broadcast. */
     46     protected static final int EVENT_BROADCAST_COMPLETE = 2;
     47 
     48     /** Release wakelock after a short timeout when returning to idle state. */
     49     static final int EVENT_RELEASE_WAKE_LOCK = 3;
     50 
     51     static final int EVENT_UPDATE_PHONE_OBJECT = 4;
     52 
     53     protected Phone mPhone;
     54 
     55     protected Context mContext;
     56 
     57     /** Wakelock release delay when returning to idle state. */
     58     private static final int WAKE_LOCK_TIMEOUT = 3000;
     59 
     60     private final DefaultState mDefaultState = new DefaultState();
     61     private final IdleState mIdleState = new IdleState();
     62     private final WaitingState mWaitingState = new WaitingState();
     63 
     64     protected WakeLockStateMachine(String debugTag, Context context, Phone phone) {
     65         super(debugTag);
     66 
     67         mContext = context;
     68         mPhone = phone;
     69 
     70         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
     71         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, debugTag);
     72         mWakeLock.acquire();    // wake lock released after we enter idle state
     73 
     74         addState(mDefaultState);
     75         addState(mIdleState, mDefaultState);
     76         addState(mWaitingState, mDefaultState);
     77         setInitialState(mIdleState);
     78     }
     79 
     80     public void updatePhoneObject(Phone phone) {
     81         sendMessage(EVENT_UPDATE_PHONE_OBJECT, phone);
     82     }
     83 
     84     /**
     85      * Tell the state machine to quit after processing all messages.
     86      */
     87     public final void dispose() {
     88         quit();
     89     }
     90 
     91     @Override
     92     protected void onQuitting() {
     93         // fully release the wakelock
     94         while (mWakeLock.isHeld()) {
     95             mWakeLock.release();
     96         }
     97     }
     98 
     99     /**
    100      * Send a message with the specified object for {@link #handleSmsMessage}.
    101      * @param obj the object to pass in the msg.obj field
    102      */
    103     public final void dispatchSmsMessage(Object obj) {
    104         sendMessage(EVENT_NEW_SMS_MESSAGE, obj);
    105     }
    106 
    107     /**
    108      * This parent state throws an exception (for debug builds) or prints an error for unhandled
    109      * message types.
    110      */
    111     class DefaultState extends State {
    112         @Override
    113         public boolean processMessage(Message msg) {
    114             switch (msg.what) {
    115                 case EVENT_UPDATE_PHONE_OBJECT: {
    116                     mPhone = (Phone) msg.obj;
    117                     log("updatePhoneObject: phone=" + mPhone.getClass().getSimpleName());
    118                     break;
    119                 }
    120                 default: {
    121                     String errorText = "processMessage: unhandled message type " + msg.what;
    122                     if (Build.IS_DEBUGGABLE) {
    123                         throw new RuntimeException(errorText);
    124                     } else {
    125                         loge(errorText);
    126                     }
    127                     break;
    128                 }
    129             }
    130             return HANDLED;
    131         }
    132     }
    133 
    134     /**
    135      * Idle state delivers Cell Broadcasts to receivers. It acquires the wakelock, which is
    136      * released when the broadcast completes.
    137      */
    138     class IdleState extends State {
    139         @Override
    140         public void enter() {
    141             sendMessageDelayed(EVENT_RELEASE_WAKE_LOCK, WAKE_LOCK_TIMEOUT);
    142         }
    143 
    144         @Override
    145         public void exit() {
    146             mWakeLock.acquire();
    147             if (DBG) log("acquired wakelock, leaving Idle state");
    148         }
    149 
    150         @Override
    151         public boolean processMessage(Message msg) {
    152             switch (msg.what) {
    153                 case EVENT_NEW_SMS_MESSAGE:
    154                     // transition to waiting state if we sent a broadcast
    155                     if (handleSmsMessage(msg)) {
    156                         transitionTo(mWaitingState);
    157                     }
    158                     return HANDLED;
    159 
    160                 case EVENT_RELEASE_WAKE_LOCK:
    161                     mWakeLock.release();
    162                     if (DBG) {
    163                         if (mWakeLock.isHeld()) {
    164                             // this is okay as long as we call release() for every acquire()
    165                             log("mWakeLock is still held after release");
    166                         } else {
    167                             log("mWakeLock released");
    168                         }
    169                     }
    170                     return HANDLED;
    171 
    172                 default:
    173                     return NOT_HANDLED;
    174             }
    175         }
    176     }
    177 
    178     /**
    179      * Waiting state waits for the result receiver to be called for the current cell broadcast.
    180      * In this state, any new cell broadcasts are deferred until we return to Idle state.
    181      */
    182     class WaitingState extends State {
    183         @Override
    184         public boolean processMessage(Message msg) {
    185             switch (msg.what) {
    186                 case EVENT_NEW_SMS_MESSAGE:
    187                     log("deferring message until return to idle");
    188                     deferMessage(msg);
    189                     return HANDLED;
    190 
    191                 case EVENT_BROADCAST_COMPLETE:
    192                     log("broadcast complete, returning to idle");
    193                     transitionTo(mIdleState);
    194                     return HANDLED;
    195 
    196                 case EVENT_RELEASE_WAKE_LOCK:
    197                     mWakeLock.release();    // decrement wakelock from previous entry to Idle
    198                     if (!mWakeLock.isHeld()) {
    199                         // wakelock should still be held until 3 seconds after we enter Idle
    200                         loge("mWakeLock released while still in WaitingState!");
    201                     }
    202                     return HANDLED;
    203 
    204                 default:
    205                     return NOT_HANDLED;
    206             }
    207         }
    208     }
    209 
    210     /**
    211      * Implemented by subclass to handle messages in {@link IdleState}.
    212      * @param message the message to process
    213      * @return true to transition to {@link WaitingState}; false to stay in {@link IdleState}
    214      */
    215     protected abstract boolean handleSmsMessage(Message message);
    216 
    217     /**
    218      * BroadcastReceiver to send message to return to idle state.
    219      */
    220     protected final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    221         @Override
    222         public void onReceive(Context context, Intent intent) {
    223             sendMessage(EVENT_BROADCAST_COMPLETE);
    224         }
    225     };
    226 
    227     /**
    228      * Log with debug level.
    229      * @param s the string to log
    230      */
    231     @Override
    232     protected void log(String s) {
    233         Rlog.d(getName(), s);
    234     }
    235 
    236     /**
    237      * Log with error level.
    238      * @param s the string to log
    239      */
    240     @Override
    241     protected void loge(String s) {
    242         Rlog.e(getName(), s);
    243     }
    244 
    245     /**
    246      * Log with error level.
    247      * @param s the string to log
    248      * @param e is a Throwable which logs additional information.
    249      */
    250     @Override
    251     protected void loge(String s, Throwable e) {
    252         Rlog.e(getName(), s, e);
    253     }
    254 }
    255