• 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.internal.telephony;
18 
19 import android.annotation.UnsupportedAppUsage;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.os.Build;
24 import android.os.Message;
25 import android.os.PowerManager;
26 import android.telephony.Rlog;
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 = true;    // TODO: change to false
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     @UnsupportedAppUsage
55     protected Phone mPhone;
56 
57     @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 
WakeLockStateMachine(String debugTag, Context context, Phone phone)70     protected WakeLockStateMachine(String debugTag, Context context, Phone phone) {
71         super(debugTag);
72 
73         mContext = context;
74         mPhone = phone;
75 
76         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
77         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, debugTag);
78         mWakeLock.acquire();    // wake lock released after we enter idle state
79 
80         addState(mDefaultState);
81         addState(mIdleState, mDefaultState);
82         addState(mWaitingState, mDefaultState);
83         setInitialState(mIdleState);
84     }
85 
86     /**
87      * Tell the state machine to quit after processing all messages.
88      */
dispose()89     public final void dispose() {
90         quit();
91     }
92 
93     @Override
onQuitting()94     protected void onQuitting() {
95         // fully release the wakelock
96         while (mWakeLock.isHeld()) {
97             mWakeLock.release();
98         }
99     }
100 
101     /**
102      * Send a message with the specified object for {@link #handleSmsMessage}.
103      * @param obj the object to pass in the msg.obj field
104      */
dispatchSmsMessage(Object obj)105     public final void dispatchSmsMessage(Object obj) {
106         sendMessage(EVENT_NEW_SMS_MESSAGE, obj);
107     }
108 
109     /**
110      * This parent state throws an exception (for debug builds) or prints an error for unhandled
111      * message types.
112      */
113     class DefaultState extends State {
114         @Override
processMessage(Message msg)115         public boolean processMessage(Message msg) {
116             switch (msg.what) {
117                 default: {
118                     String errorText = "processMessage: unhandled message type " + msg.what;
119                     if (Build.IS_DEBUGGABLE) {
120                         throw new RuntimeException(errorText);
121                     } else {
122                         loge(errorText);
123                     }
124                     break;
125                 }
126             }
127             return HANDLED;
128         }
129     }
130 
131     /**
132      * Idle state delivers Cell Broadcasts to receivers. It acquires the wakelock, which is
133      * released when the broadcast completes.
134      */
135     class IdleState extends State {
136         @Override
enter()137         public void enter() {
138             sendMessageDelayed(EVENT_RELEASE_WAKE_LOCK, WAKE_LOCK_TIMEOUT);
139         }
140 
141         @Override
exit()142         public void exit() {
143             mWakeLock.acquire();
144             if (DBG) log("acquired wakelock, leaving Idle state");
145         }
146 
147         @Override
processMessage(Message msg)148         public boolean processMessage(Message msg) {
149             switch (msg.what) {
150                 case EVENT_NEW_SMS_MESSAGE:
151                     // transition to waiting state if we sent a broadcast
152                     if (handleSmsMessage(msg)) {
153                         transitionTo(mWaitingState);
154                     }
155                     return HANDLED;
156 
157                 case EVENT_RELEASE_WAKE_LOCK:
158                     mWakeLock.release();
159                     if (DBG) {
160                         if (mWakeLock.isHeld()) {
161                             // this is okay as long as we call release() for every acquire()
162                             log("mWakeLock is still held after release");
163                         } else {
164                             log("mWakeLock released");
165                         }
166                     }
167                     return HANDLED;
168 
169                 default:
170                     return NOT_HANDLED;
171             }
172         }
173     }
174 
175     /**
176      * Waiting state waits for the result receiver to be called for the current cell broadcast.
177      * In this state, any new cell broadcasts are deferred until we return to Idle state.
178      */
179     class WaitingState extends State {
180         @Override
processMessage(Message msg)181         public boolean processMessage(Message msg) {
182             switch (msg.what) {
183                 case EVENT_NEW_SMS_MESSAGE:
184                     log("deferring message until return to idle");
185                     deferMessage(msg);
186                     return HANDLED;
187 
188                 case EVENT_BROADCAST_COMPLETE:
189                     log("broadcast complete, returning to idle");
190                     transitionTo(mIdleState);
191                     return HANDLED;
192 
193                 case EVENT_RELEASE_WAKE_LOCK:
194                     mWakeLock.release();    // decrement wakelock from previous entry to Idle
195                     if (!mWakeLock.isHeld()) {
196                         // wakelock should still be held until 3 seconds after we enter Idle
197                         loge("mWakeLock released while still in WaitingState!");
198                     }
199                     return HANDLED;
200 
201                 default:
202                     return NOT_HANDLED;
203             }
204         }
205     }
206 
207     /**
208      * Implemented by subclass to handle messages in {@link IdleState}.
209      * @param message the message to process
210      * @return true to transition to {@link WaitingState}; false to stay in {@link IdleState}
211      */
handleSmsMessage(Message message)212     protected abstract boolean handleSmsMessage(Message message);
213 
214     /**
215      * BroadcastReceiver to send message to return to idle state.
216      */
217     protected final BroadcastReceiver mReceiver = new BroadcastReceiver() {
218         @Override
219         public void onReceive(Context context, Intent intent) {
220             if (mReceiverCount.decrementAndGet() == 0) {
221                 sendMessage(EVENT_BROADCAST_COMPLETE);
222             }
223         }
224     };
225 
226     /**
227      * Log with debug level.
228      * @param s the string to log
229      */
230     @UnsupportedAppUsage
231     @Override
log(String s)232     protected void log(String s) {
233         Rlog.d(getName(), s);
234     }
235 
236     /**
237      * Log with error level.
238      * @param s the string to log
239      */
240     @Override
loge(String s)241     protected void loge(String s) {
242         Rlog.e(getName(), s);
243     }
244 
245     /**
246      * Log with error level.
247      * @param s the string to log
248      * @param e is a Throwable which logs additional information.
249      */
250     @Override
loge(String s, Throwable e)251     protected void loge(String s, Throwable e) {
252         Rlog.e(getName(), s, e);
253     }
254 }
255