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