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
17package com.android.internal.telephony;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.os.Build;
23import android.os.Message;
24import android.os.PowerManager;
25import android.telephony.Rlog;
26
27import com.android.internal.util.State;
28import 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 */
37public 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 PhoneBase 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
64    protected WakeLockStateMachine(String debugTag, Context context, PhoneBase 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
80    public void updatePhoneObject(PhoneBase phone) {
81        sendMessage(EVENT_UPDATE_PHONE_OBJECT, phone);
82    }
83
84    /**
85     * Tell the state machine to quit after processing all messages.
86     */
87    public final void dispose() {
88        quit();
89    }
90
91    @Override
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     */
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
113        public boolean processMessage(Message msg) {
114            switch (msg.what) {
115                case EVENT_UPDATE_PHONE_OBJECT: {
116                    mPhone = (PhoneBase) 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
140        public void enter() {
141            sendMessageDelayed(EVENT_RELEASE_WAKE_LOCK, WAKE_LOCK_TIMEOUT);
142        }
143
144        @Override
145        public void exit() {
146            mWakeLock.acquire();
147            if (DBG) log("acquired wakelock, leaving Idle state");
148        }
149
150        @Override
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
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     */
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
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
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
251    protected void loge(String s, Throwable e) {
252        Rlog.e(getName(), s, e);
253    }
254}
255