blob: 789534df452e74b6fd76d0ab926f0294934d4a9d [file] [log] [blame]
Jordan Liuc872fad2019-10-11 11:42:03 -07001/*
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
17package com.android.cellbroadcastservice;
18
19import android.annotation.UnsupportedAppUsage;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
Jordan Liuc872fad2019-10-11 11:42:03 -070023import android.os.Message;
24import android.os.PowerManager;
Jordan Liu4a2ff262019-10-21 12:30:56 -070025import android.os.SystemProperties;
Jordan Liuc872fad2019-10-11 11:42:03 -070026import android.util.Log;
27
28import com.android.internal.util.State;
29import com.android.internal.util.StateMachine;
30
31import 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 */
40public abstract class WakeLockStateMachine extends StateMachine {
Jordan Liu4a2ff262019-10-21 12:30:56 -070041 protected static final boolean DBG = SystemProperties.getInt("ro.debuggable", 0) == 1;
Jordan Liuc872fad2019-10-11 11:42:03 -070042
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
Jack Yu2f37c432019-11-04 21:28:24 -080054 /** Broadcast not required due to geo-fencing check */
55 static final int EVENT_BROADCAST_NOT_REQUIRED = 4;
56
Jordan Liuc872fad2019-10-11 11:42:03 -070057 @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
70 protected WakeLockStateMachine(String debugTag, Context context) {
71 super(debugTag);
72
73 mContext = context;
74
75 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
76 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, debugTag);
77 // wake lock released after we enter idle state
78 mWakeLock.acquire();
79
80 addState(mDefaultState);
81 addState(mIdleState, mDefaultState);
82 addState(mWaitingState, mDefaultState);
83 setInitialState(mIdleState);
84 }
85
86 private void releaseWakeLock() {
87 if (mWakeLock.isHeld()) {
88 mWakeLock.release();
89 }
90
91 if (mWakeLock.isHeld()) {
92 loge("Wait lock is held after release.");
93 }
94 }
95
96 /**
97 * Tell the state machine to quit after processing all messages.
98 */
99 public final void dispose() {
100 quit();
101 }
102
103 @Override
104 protected void onQuitting() {
105 // fully release the wakelock
106 while (mWakeLock.isHeld()) {
107 mWakeLock.release();
108 }
109 }
110
111 /**
112 * Send a message with the specified object for {@link #handleSmsMessage}.
113 * @param obj the object to pass in the msg.obj field
114 */
115 public final void onCdmaCellBroadcastSms(Object obj) {
116 sendMessage(EVENT_NEW_SMS_MESSAGE, obj);
117 }
118
119 /**
120 * This parent state throws an exception (for debug builds) or prints an error for unhandled
121 * message types.
122 */
123 class DefaultState extends State {
124 @Override
125 public boolean processMessage(Message msg) {
126 switch (msg.what) {
127 default: {
128 String errorText = "processMessage: unhandled message type " + msg.what;
Jordan Liu4a2ff262019-10-21 12:30:56 -0700129 if (DBG) {
Jordan Liuc872fad2019-10-11 11:42:03 -0700130 throw new RuntimeException(errorText);
131 } else {
132 loge(errorText);
133 }
134 break;
135 }
136 }
137 return HANDLED;
138 }
139 }
140
141 /**
142 * Idle state delivers Cell Broadcasts to receivers. It acquires the wakelock, which is
143 * released when the broadcast completes.
144 */
145 class IdleState extends State {
146 @Override
147 public void enter() {
148 sendMessageDelayed(EVENT_RELEASE_WAKE_LOCK, WAKE_LOCK_TIMEOUT);
149 }
150
151 @Override
152 public void exit() {
153 mWakeLock.acquire();
Jack Yu2f37c432019-11-04 21:28:24 -0800154 if (DBG) log("Idle: acquired wakelock, leaving Idle state");
Jordan Liuc872fad2019-10-11 11:42:03 -0700155 }
156
157 @Override
158 public boolean processMessage(Message msg) {
159 switch (msg.what) {
160 case EVENT_NEW_SMS_MESSAGE:
Jack Yu2f37c432019-11-04 21:28:24 -0800161 log("Idle: new cell broadcast message");
Jordan Liuc872fad2019-10-11 11:42:03 -0700162 // transition to waiting state if we sent a broadcast
163 if (handleSmsMessage(msg)) {
164 transitionTo(mWaitingState);
165 }
166 return HANDLED;
167
168 case EVENT_RELEASE_WAKE_LOCK:
Jack Yu2f37c432019-11-04 21:28:24 -0800169 log("Idle: release wakelock");
Jordan Liuc872fad2019-10-11 11:42:03 -0700170 releaseWakeLock();
171 return HANDLED;
Jack Yu2f37c432019-11-04 21:28:24 -0800172 case EVENT_BROADCAST_NOT_REQUIRED:
173 log("Idle: broadcast not required");
174 return HANDLED;
Jordan Liuc872fad2019-10-11 11:42:03 -0700175 default:
176 return NOT_HANDLED;
177 }
178 }
179 }
180
181 /**
182 * Waiting state waits for the result receiver to be called for the current cell broadcast.
183 * In this state, any new cell broadcasts are deferred until we return to Idle state.
184 */
185 class WaitingState extends State {
186 @Override
187 public boolean processMessage(Message msg) {
188 switch (msg.what) {
189 case EVENT_NEW_SMS_MESSAGE:
Jack Yu2f37c432019-11-04 21:28:24 -0800190 log("Waiting: deferring message until return to idle");
Jordan Liuc872fad2019-10-11 11:42:03 -0700191 deferMessage(msg);
192 return HANDLED;
193
194 case EVENT_BROADCAST_COMPLETE:
Jack Yu2f37c432019-11-04 21:28:24 -0800195 log("Waiting: broadcast complete, returning to idle");
Jordan Liuc872fad2019-10-11 11:42:03 -0700196 transitionTo(mIdleState);
197 return HANDLED;
198
199 case EVENT_RELEASE_WAKE_LOCK:
Jack Yu2f37c432019-11-04 21:28:24 -0800200 log("Waiting: release wakelock");
Jordan Liuc872fad2019-10-11 11:42:03 -0700201 releaseWakeLock();
202 return HANDLED;
Jack Yu2f37c432019-11-04 21:28:24 -0800203 case EVENT_BROADCAST_NOT_REQUIRED:
204 log("Waiting: broadcast not required");
205 if (mReceiverCount.get() == 0) {
206 transitionTo(mIdleState);
207 }
208 return HANDLED;
Jordan Liuc872fad2019-10-11 11:42:03 -0700209 default:
210 return NOT_HANDLED;
211 }
212 }
213 }
214
215 /**
216 * Implemented by subclass to handle messages in {@link IdleState}.
217 * @param message the message to process
218 * @return true to transition to {@link WaitingState}; false to stay in {@link IdleState}
219 */
220 protected abstract boolean handleSmsMessage(Message message);
221
222 /**
223 * BroadcastReceiver to send message to return to idle state.
224 */
225 protected final BroadcastReceiver mReceiver = new BroadcastReceiver() {
226 @Override
227 public void onReceive(Context context, Intent intent) {
228 if (mReceiverCount.decrementAndGet() == 0) {
229 sendMessage(EVENT_BROADCAST_COMPLETE);
230 }
231 }
232 };
233
234 /**
235 * Log with debug level.
236 * @param s the string to log
237 */
238 @UnsupportedAppUsage
239 @Override
240 protected void log(String s) {
241 Log.d(getName(), s);
242 }
243
244 /**
245 * Log with error level.
246 * @param s the string to log
247 */
248 @Override
249 protected void loge(String s) {
250 Log.e(getName(), s);
251 }
252
253 /**
254 * Log with error level.
255 * @param s the string to log
256 * @param e is a Throwable which logs additional information.
257 */
258 @Override
259 protected void loge(String s, Throwable e) {
260 Log.e(getName(), s, e);
261 }
262}