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