• 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.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.os.Build;
23 import android.os.Message;
24 import android.os.PowerManager;
25 import android.telephony.Rlog;
26 
27 import com.android.internal.util.State;
28 import com.android.internal.util.StateMachine;
29 
30 /**
31  * Generic state machine for handling messages and waiting for ordered broadcasts to complete.
32  * Subclasses implement {@link #handleSmsMessage}, which returns true to transition into waiting
33  * state, or false to remain in idle state. The wakelock is acquired on exit from idle state,
34  * and is released a few seconds after returning to idle state, or immediately upon calling
35  * {@link #quit}.
36  */
37 public abstract class WakeLockStateMachine extends StateMachine {
38     protected static final boolean DBG = true;    // TODO: change to false
39 
40     private final PowerManager.WakeLock mWakeLock;
41 
42     /** New message to process. */
43     public static final int EVENT_NEW_SMS_MESSAGE = 1;
44 
45     /** Result receiver called for current cell broadcast. */
46     protected static final int EVENT_BROADCAST_COMPLETE = 2;
47 
48     /** Release wakelock after a short timeout when returning to idle state. */
49     static final int EVENT_RELEASE_WAKE_LOCK = 3;
50 
51     static final int EVENT_UPDATE_PHONE_OBJECT = 4;
52 
53     protected Phone mPhone;
54 
55     protected Context mContext;
56 
57     /** Wakelock release delay when returning to idle state. */
58     private static final int WAKE_LOCK_TIMEOUT = 3000;
59 
60     private final DefaultState mDefaultState = new DefaultState();
61     private final IdleState mIdleState = new IdleState();
62     private final WaitingState mWaitingState = new WaitingState();
63 
WakeLockStateMachine(String debugTag, Context context, Phone phone)64     protected WakeLockStateMachine(String debugTag, Context context, Phone phone) {
65         super(debugTag);
66 
67         mContext = context;
68         mPhone = phone;
69 
70         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
71         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, debugTag);
72         mWakeLock.acquire();    // wake lock released after we enter idle state
73 
74         addState(mDefaultState);
75         addState(mIdleState, mDefaultState);
76         addState(mWaitingState, mDefaultState);
77         setInitialState(mIdleState);
78     }
79 
updatePhoneObject(Phone phone)80     public void updatePhoneObject(Phone phone) {
81         sendMessage(EVENT_UPDATE_PHONE_OBJECT, phone);
82     }
83 
84     /**
85      * Tell the state machine to quit after processing all messages.
86      */
dispose()87     public final void dispose() {
88         quit();
89     }
90 
91     @Override
onQuitting()92     protected void onQuitting() {
93         // fully release the wakelock
94         while (mWakeLock.isHeld()) {
95             mWakeLock.release();
96         }
97     }
98 
99     /**
100      * Send a message with the specified object for {@link #handleSmsMessage}.
101      * @param obj the object to pass in the msg.obj field
102      */
dispatchSmsMessage(Object obj)103     public final void dispatchSmsMessage(Object obj) {
104         sendMessage(EVENT_NEW_SMS_MESSAGE, obj);
105     }
106 
107     /**
108      * This parent state throws an exception (for debug builds) or prints an error for unhandled
109      * message types.
110      */
111     class DefaultState extends State {
112         @Override
processMessage(Message msg)113         public boolean processMessage(Message msg) {
114             switch (msg.what) {
115                 case EVENT_UPDATE_PHONE_OBJECT: {
116                     mPhone = (Phone) msg.obj;
117                     log("updatePhoneObject: phone=" + mPhone.getClass().getSimpleName());
118                     break;
119                 }
120                 default: {
121                     String errorText = "processMessage: unhandled message type " + msg.what;
122                     if (Build.IS_DEBUGGABLE) {
123                         throw new RuntimeException(errorText);
124                     } else {
125                         loge(errorText);
126                     }
127                     break;
128                 }
129             }
130             return HANDLED;
131         }
132     }
133 
134     /**
135      * Idle state delivers Cell Broadcasts to receivers. It acquires the wakelock, which is
136      * released when the broadcast completes.
137      */
138     class IdleState extends State {
139         @Override
enter()140         public void enter() {
141             sendMessageDelayed(EVENT_RELEASE_WAKE_LOCK, WAKE_LOCK_TIMEOUT);
142         }
143 
144         @Override
exit()145         public void exit() {
146             mWakeLock.acquire();
147             if (DBG) log("acquired wakelock, leaving Idle state");
148         }
149 
150         @Override
processMessage(Message msg)151         public boolean processMessage(Message msg) {
152             switch (msg.what) {
153                 case EVENT_NEW_SMS_MESSAGE:
154                     // transition to waiting state if we sent a broadcast
155                     if (handleSmsMessage(msg)) {
156                         transitionTo(mWaitingState);
157                     }
158                     return HANDLED;
159 
160                 case EVENT_RELEASE_WAKE_LOCK:
161                     mWakeLock.release();
162                     if (DBG) {
163                         if (mWakeLock.isHeld()) {
164                             // this is okay as long as we call release() for every acquire()
165                             log("mWakeLock is still held after release");
166                         } else {
167                             log("mWakeLock released");
168                         }
169                     }
170                     return HANDLED;
171 
172                 default:
173                     return NOT_HANDLED;
174             }
175         }
176     }
177 
178     /**
179      * Waiting state waits for the result receiver to be called for the current cell broadcast.
180      * In this state, any new cell broadcasts are deferred until we return to Idle state.
181      */
182     class WaitingState extends State {
183         @Override
processMessage(Message msg)184         public boolean processMessage(Message msg) {
185             switch (msg.what) {
186                 case EVENT_NEW_SMS_MESSAGE:
187                     log("deferring message until return to idle");
188                     deferMessage(msg);
189                     return HANDLED;
190 
191                 case EVENT_BROADCAST_COMPLETE:
192                     log("broadcast complete, returning to idle");
193                     transitionTo(mIdleState);
194                     return HANDLED;
195 
196                 case EVENT_RELEASE_WAKE_LOCK:
197                     mWakeLock.release();    // decrement wakelock from previous entry to Idle
198                     if (!mWakeLock.isHeld()) {
199                         // wakelock should still be held until 3 seconds after we enter Idle
200                         loge("mWakeLock released while still in WaitingState!");
201                     }
202                     return HANDLED;
203 
204                 default:
205                     return NOT_HANDLED;
206             }
207         }
208     }
209 
210     /**
211      * Implemented by subclass to handle messages in {@link IdleState}.
212      * @param message the message to process
213      * @return true to transition to {@link WaitingState}; false to stay in {@link IdleState}
214      */
handleSmsMessage(Message message)215     protected abstract boolean handleSmsMessage(Message message);
216 
217     /**
218      * BroadcastReceiver to send message to return to idle state.
219      */
220     protected final BroadcastReceiver mReceiver = new BroadcastReceiver() {
221         @Override
222         public void onReceive(Context context, Intent intent) {
223             sendMessage(EVENT_BROADCAST_COMPLETE);
224         }
225     };
226 
227     /**
228      * Log with debug level.
229      * @param s the string to log
230      */
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