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 WakeLockStateMachine(String debugTag, Context context, Phone phone)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 updatePhoneObject(Phone phone)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 */ dispose()87 public final void dispose() { 88 quit(); 89 } 90 91 @Override onQuitting()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 */ dispatchSmsMessage(Object obj)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 processMessage(Message msg)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 enter()140 public void enter() { 141 sendMessageDelayed(EVENT_RELEASE_WAKE_LOCK, WAKE_LOCK_TIMEOUT); 142 } 143 144 @Override exit()145 public void exit() { 146 mWakeLock.acquire(); 147 if (DBG) log("acquired wakelock, leaving Idle state"); 148 } 149 150 @Override processMessage(Message msg)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 processMessage(Message msg)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 */ handleSmsMessage(Message message)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 log(String s)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 loge(String s)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 loge(String s, Throwable e)251 protected void loge(String s, Throwable e) { 252 Rlog.e(getName(), s, e); 253 } 254 } 255