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