blob: 0cb5352ef6f852bed694164bae94db4a1e50c0d8 [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
Jordan Liuc872fad2019-10-11 11:42:03 -070019import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
Jack Yue7591d32019-11-07 22:59:46 -080022import android.os.Looper;
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 Liu75ebf632019-12-16 15:35:27 -080026import android.util.Log;
Jordan Liuc872fad2019-10-11 11:42:03 -070027
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 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();
Jordan Liuc872fad2019-10-11 11:42:03 -070065 private final IdleState mIdleState = new IdleState();
66 private final WaitingState mWaitingState = new WaitingState();
67
Jack Yue7591d32019-11-07 22:59:46 -080068 protected WakeLockStateMachine(String debugTag, Context context, Looper looper) {
69 super(debugTag, looper);
Jordan Liuc872fad2019-10-11 11:42:03 -070070
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
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 */
97 public final void dispose() {
98 quit();
99 }
100
101 @Override
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 */
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
123 public boolean processMessage(Message msg) {
124 switch (msg.what) {
125 default: {
126 String errorText = "processMessage: unhandled message type " + msg.what;
Jordan Liu4a2ff262019-10-21 12:30:56 -0700127 if (DBG) {
Jordan Liuc872fad2019-10-11 11:42:03 -0700128 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
145 public void enter() {
146 sendMessageDelayed(EVENT_RELEASE_WAKE_LOCK, WAKE_LOCK_TIMEOUT);
147 }
148
149 @Override
150 public void exit() {
151 mWakeLock.acquire();
Jack Yu2f37c432019-11-04 21:28:24 -0800152 if (DBG) log("Idle: acquired wakelock, leaving Idle state");
Jordan Liuc872fad2019-10-11 11:42:03 -0700153 }
154
155 @Override
156 public boolean processMessage(Message msg) {
157 switch (msg.what) {
158 case EVENT_NEW_SMS_MESSAGE:
Jack Yu2f37c432019-11-04 21:28:24 -0800159 log("Idle: new cell broadcast message");
Jordan Liuc872fad2019-10-11 11:42:03 -0700160 // 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:
Jack Yu2f37c432019-11-04 21:28:24 -0800167 log("Idle: release wakelock");
Jordan Liuc872fad2019-10-11 11:42:03 -0700168 releaseWakeLock();
169 return HANDLED;
Jack Yu2f37c432019-11-04 21:28:24 -0800170 case EVENT_BROADCAST_NOT_REQUIRED:
171 log("Idle: broadcast not required");
172 return HANDLED;
Jordan Liuc872fad2019-10-11 11:42:03 -0700173 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
185 public boolean processMessage(Message msg) {
186 switch (msg.what) {
187 case EVENT_NEW_SMS_MESSAGE:
Jack Yu2f37c432019-11-04 21:28:24 -0800188 log("Waiting: deferring message until return to idle");
Jordan Liuc872fad2019-10-11 11:42:03 -0700189 deferMessage(msg);
190 return HANDLED;
191
192 case EVENT_BROADCAST_COMPLETE:
Jack Yu2f37c432019-11-04 21:28:24 -0800193 log("Waiting: broadcast complete, returning to idle");
Jordan Liuc872fad2019-10-11 11:42:03 -0700194 transitionTo(mIdleState);
195 return HANDLED;
196
197 case EVENT_RELEASE_WAKE_LOCK:
Jack Yu2f37c432019-11-04 21:28:24 -0800198 log("Waiting: release wakelock");
Jordan Liuc872fad2019-10-11 11:42:03 -0700199 releaseWakeLock();
200 return HANDLED;
Jack Yu2f37c432019-11-04 21:28:24 -0800201 case EVENT_BROADCAST_NOT_REQUIRED:
202 log("Waiting: broadcast not required");
203 if (mReceiverCount.get() == 0) {
204 transitionTo(mIdleState);
205 }
206 return HANDLED;
Jordan Liuc872fad2019-10-11 11:42:03 -0700207 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 */
218 protected abstract boolean handleSmsMessage(Message message);
219
220 /**
221 * BroadcastReceiver to send message to return to idle state.
222 */
223 protected final BroadcastReceiver mReceiver = 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 */
Jordan Liuc872fad2019-10-11 11:42:03 -0700236 @Override
237 protected void log(String s) {
Jordan Liu75ebf632019-12-16 15:35:27 -0800238 Log.d(getName(), s);
Jordan Liuc872fad2019-10-11 11:42:03 -0700239 }
240
241 /**
242 * Log with error level.
243 * @param s the string to log
244 */
245 @Override
246 protected void loge(String s) {
Jordan Liu75ebf632019-12-16 15:35:27 -0800247 Log.e(getName(), s);
Jordan Liuc872fad2019-10-11 11:42:03 -0700248 }
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
256 protected void loge(String s, Throwable e) {
Jordan Liu75ebf632019-12-16 15:35:27 -0800257 Log.e(getName(), s, e);
Jordan Liuc872fad2019-10-11 11:42:03 -0700258 }
259}