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