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