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