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.cellbroadcastservice; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.os.Looper; 23 import android.os.Message; 24 import android.os.PowerManager; 25 import android.os.SystemProperties; 26 import android.util.Log; 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 = SystemProperties.getInt("ro.debuggable", 0) == 1 42 || SystemProperties.getInt("persist.cellbroadcast.debug", 0) == 1 43 || SystemProperties.getInt("persist.cellbroadcast.verbose", 0) == 1; 44 45 protected static final boolean VDBG = 46 SystemProperties.getInt("persist.cellbroadcast.verbose", 0) == 1; 47 48 49 private final PowerManager.WakeLock mWakeLock; 50 51 /** New message to process. */ 52 public static final int EVENT_NEW_SMS_MESSAGE = 1; 53 54 /** Result receiver called for current cell broadcast. */ 55 protected static final int EVENT_BROADCAST_COMPLETE = 2; 56 57 /** Release wakelock after a short timeout when returning to idle state. */ 58 static final int EVENT_RELEASE_WAKE_LOCK = 3; 59 60 /** Broadcast not required due to geo-fencing check */ 61 static final int EVENT_BROADCAST_NOT_REQUIRED = 4; 62 63 protected Context mContext; 64 65 protected AtomicInteger mReceiverCount = new AtomicInteger(0); 66 67 /** Wakelock release delay when returning to idle state. */ 68 private static final int WAKE_LOCK_TIMEOUT = 3000; 69 70 private final DefaultState mDefaultState = new DefaultState(); 71 private final IdleState mIdleState = new IdleState(); 72 private final WaitingState mWaitingState = new WaitingState(); 73 WakeLockStateMachine(String debugTag, Context context, Looper looper)74 protected WakeLockStateMachine(String debugTag, Context context, Looper looper) { 75 super(debugTag, looper); 76 77 mContext = context; 78 79 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 80 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, debugTag); 81 // wake lock released after we enter idle state 82 mWakeLock.acquire(); 83 84 addState(mDefaultState); 85 addState(mIdleState, mDefaultState); 86 addState(mWaitingState, mDefaultState); 87 setInitialState(mIdleState); 88 } 89 releaseWakeLock()90 private void releaseWakeLock() { 91 if (mWakeLock.isHeld()) { 92 mWakeLock.release(); 93 } 94 95 if (mWakeLock.isHeld()) { 96 loge("Wait lock is held after release."); 97 } 98 } 99 100 /** 101 * Tell the state machine to quit after processing all messages. 102 */ dispose()103 public final void dispose() { 104 quit(); 105 } 106 107 @Override onQuitting()108 protected void onQuitting() { 109 // fully release the wakelock 110 while (mWakeLock.isHeld()) { 111 mWakeLock.release(); 112 } 113 } 114 115 /** 116 * Send a message with the specified object for {@link #handleSmsMessage}. 117 * @param obj the object to pass in the msg.obj field 118 */ onCdmaCellBroadcastSms(Object obj)119 public final void onCdmaCellBroadcastSms(Object obj) { 120 sendMessage(EVENT_NEW_SMS_MESSAGE, obj); 121 } 122 123 /** 124 * This parent state throws an exception (for debug builds) or prints an error for unhandled 125 * message types. 126 */ 127 class DefaultState extends State { 128 @Override processMessage(Message msg)129 public boolean processMessage(Message msg) { 130 switch (msg.what) { 131 default: { 132 String errorText = "processMessage: unhandled message type " + msg.what; 133 if (DBG) { 134 throw new RuntimeException(errorText); 135 } else { 136 loge(errorText); 137 } 138 break; 139 } 140 } 141 return HANDLED; 142 } 143 } 144 145 /** 146 * Idle state delivers Cell Broadcasts to receivers. It acquires the wakelock, which is 147 * released when the broadcast completes. 148 */ 149 class IdleState extends State { 150 @Override enter()151 public void enter() { 152 sendMessageDelayed(EVENT_RELEASE_WAKE_LOCK, WAKE_LOCK_TIMEOUT); 153 } 154 155 @Override exit()156 public void exit() { 157 mWakeLock.acquire(); 158 if (DBG) log("Idle: acquired wakelock, leaving Idle state"); 159 } 160 161 @Override processMessage(Message msg)162 public boolean processMessage(Message msg) { 163 switch (msg.what) { 164 case EVENT_NEW_SMS_MESSAGE: 165 log("Idle: new cell broadcast message"); 166 // transition to waiting state if we sent a broadcast 167 if (handleSmsMessage(msg)) { 168 transitionTo(mWaitingState); 169 } 170 return HANDLED; 171 172 case EVENT_RELEASE_WAKE_LOCK: 173 log("Idle: release wakelock"); 174 releaseWakeLock(); 175 return HANDLED; 176 case EVENT_BROADCAST_NOT_REQUIRED: 177 log("Idle: broadcast not required"); 178 return HANDLED; 179 default: 180 return NOT_HANDLED; 181 } 182 } 183 } 184 185 /** 186 * Waiting state waits for the result receiver to be called for the current cell broadcast. 187 * In this state, any new cell broadcasts are deferred until we return to Idle state. 188 */ 189 class WaitingState extends State { 190 @Override processMessage(Message msg)191 public boolean processMessage(Message msg) { 192 switch (msg.what) { 193 case EVENT_NEW_SMS_MESSAGE: 194 log("Waiting: deferring message until return to idle"); 195 deferMessage(msg); 196 return HANDLED; 197 198 case EVENT_BROADCAST_COMPLETE: 199 log("Waiting: broadcast complete, returning to idle"); 200 transitionTo(mIdleState); 201 return HANDLED; 202 203 case EVENT_RELEASE_WAKE_LOCK: 204 log("Waiting: release wakelock"); 205 releaseWakeLock(); 206 return HANDLED; 207 case EVENT_BROADCAST_NOT_REQUIRED: 208 log("Waiting: broadcast location timed out"); 209 if (mReceiverCount.get() == 0) { 210 log("Waiting: broadcast location timed out"); 211 transitionTo(mIdleState); 212 } else { 213 log("Waiting: broadcast location timed out, receiverCount = " 214 + mReceiverCount.get()); 215 } 216 return HANDLED; 217 default: 218 return NOT_HANDLED; 219 } 220 } 221 } 222 223 /** 224 * Implemented by subclass to handle messages in {@link IdleState}. 225 * @param message the message to process 226 * @return true to transition to {@link WaitingState}; false to stay in {@link IdleState} 227 */ handleSmsMessage(Message message)228 protected abstract boolean handleSmsMessage(Message message); 229 230 /** 231 * BroadcastReceiver to send message to return to idle state. 232 */ 233 protected final BroadcastReceiver mOrderedBroadcastReceiver = new BroadcastReceiver() { 234 @Override 235 public void onReceive(Context context, Intent intent) { 236 if (mReceiverCount.decrementAndGet() == 0) { 237 sendMessage(EVENT_BROADCAST_COMPLETE); 238 } 239 } 240 }; 241 242 /** 243 * Log with debug level. 244 * @param s the string to log 245 */ 246 @Override log(String s)247 protected void log(String s) { 248 Log.d(getName(), s); 249 } 250 251 /** 252 * Log with error level. 253 * @param s the string to log 254 */ 255 @Override loge(String s)256 protected void loge(String s) { 257 Log.e(getName(), s); 258 } 259 260 /** 261 * Log with error level. 262 * @param s the string to log 263 * @param e is a Throwable which logs additional information. 264 */ 265 @Override loge(String s, Throwable e)266 protected void loge(String s, Throwable e) { 267 Log.e(getName(), s, e); 268 } 269 } 270