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