1/*
2 * Copyright (C) 2017 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 static android.hardware.radio.V1_0.DeviceStateType.CHARGING_STATE;
20import static android.hardware.radio.V1_0.DeviceStateType.LOW_DATA_EXPECTED;
21import static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE;
22
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.hardware.display.DisplayManager;
28import android.hardware.radio.V1_0.IndicationFilter;
29import android.net.ConnectivityManager;
30import android.os.BatteryManager;
31import android.os.Handler;
32import android.os.Message;
33import android.os.PowerManager;
34import android.telephony.Rlog;
35import android.util.LocalLog;
36import android.view.Display;
37
38import com.android.internal.util.IndentingPrintWriter;
39
40import java.io.FileDescriptor;
41import java.io.PrintWriter;
42import java.util.ArrayList;
43
44/**
45 * The device state monitor monitors the device state such as charging state, power saving sate,
46 * and then passes down the information to the radio modem for the modem to perform its own
47 * proprietary power saving strategy. Device state monitor also turns off the unsolicited
48 * response from the modem when the device does not need to receive it, for example, device's
49 * screen is off and does not have activities like tethering, remote display, etc...This effectively
50 * prevents the CPU from waking up by those unnecessary unsolicited responses such as signal
51 * strength update.
52 */
53public class DeviceStateMonitor extends Handler {
54    protected static final boolean DBG = false;      /* STOPSHIP if true */
55    protected static final String TAG = DeviceStateMonitor.class.getSimpleName();
56
57    private static final int EVENT_RIL_CONNECTED                = 0;
58    private static final int EVENT_SCREEN_STATE_CHANGED         = 1;
59    private static final int EVENT_POWER_SAVE_MODE_CHANGED      = 2;
60    private static final int EVENT_CHARGING_STATE_CHANGED       = 3;
61    private static final int EVENT_TETHERING_STATE_CHANGED      = 4;
62
63    private final Phone mPhone;
64
65    private final LocalLog mLocalLog = new LocalLog(100);
66
67    /**
68     * Flag for wifi/usb/bluetooth tethering turned on or not
69     */
70    private boolean mIsTetheringOn;
71
72    /**
73     * Screen state provided by Display Manager. True indicates one of the screen is on, otherwise
74     * all off.
75     */
76    private boolean mIsScreenOn;
77
78    /**
79     * Indicating the device is plugged in and is supplying sufficient power that the battery level
80     * is going up (or the battery is fully charged). See BatteryManager.isCharging() for the
81     * details
82     */
83    private boolean mIsCharging;
84
85    /**
86     * Flag for device power save mode. See PowerManager.isPowerSaveMode() for the details.
87     * Note that it is not possible both mIsCharging and mIsPowerSaveOn are true at the same time.
88     * The system will automatically end power save mode when the device starts charging.
89     */
90    private boolean mIsPowerSaveOn;
91
92    /**
93     * Low data expected mode. True indicates low data traffic is expected, for example, when the
94     * device is idle (e.g. screen is off and not doing tethering in the background). Note this
95     * doesn't mean no data is expected.
96     */
97    private boolean mIsLowDataExpected;
98
99    /**
100     * The unsolicited response filter. See IndicationFilter defined in types.hal for the definition
101     * of each bit.
102     */
103    private int mUnsolicitedResponseFilter = IndicationFilter.ALL;
104
105    private final DisplayManager.DisplayListener mDisplayListener =
106            new DisplayManager.DisplayListener() {
107                @Override
108                public void onDisplayAdded(int displayId) { }
109
110                @Override
111                public void onDisplayRemoved(int displayId) { }
112
113                @Override
114                public void onDisplayChanged(int displayId) {
115                    boolean screenOn = isScreenOn();
116                    Message msg = obtainMessage(EVENT_SCREEN_STATE_CHANGED);
117                    msg.arg1 = screenOn ? 1 : 0;
118                    sendMessage(msg);
119                }
120            };
121
122    /**
123     * Device state broadcast receiver
124     */
125    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
126        @Override
127        public void onReceive(Context context, Intent intent) {
128            log("received: " + intent, true);
129
130            Message msg;
131            switch (intent.getAction()) {
132                case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
133                    msg = obtainMessage(EVENT_POWER_SAVE_MODE_CHANGED);
134                    msg.arg1 = isPowerSaveModeOn() ? 1 : 0;
135                    log("Power Save mode " + ((msg.arg1 == 1) ? "on" : "off"), true);
136                    break;
137                case BatteryManager.ACTION_CHARGING:
138                    msg = obtainMessage(EVENT_CHARGING_STATE_CHANGED);
139                    msg.arg1 = 1;   // charging
140                    break;
141                case BatteryManager.ACTION_DISCHARGING:
142                    msg = obtainMessage(EVENT_CHARGING_STATE_CHANGED);
143                    msg.arg1 = 0;   // not charging
144                    break;
145                case ConnectivityManager.ACTION_TETHER_STATE_CHANGED:
146                    ArrayList<String> activeTetherIfaces = intent.getStringArrayListExtra(
147                            ConnectivityManager.EXTRA_ACTIVE_TETHER);
148
149                    boolean isTetheringOn = activeTetherIfaces != null
150                            && activeTetherIfaces.size() > 0;
151                    log("Tethering " + (isTetheringOn ? "on" : "off"), true);
152                    msg = obtainMessage(EVENT_TETHERING_STATE_CHANGED);
153                    msg.arg1 = isTetheringOn ? 1 : 0;
154                    break;
155                default:
156                    log("Unexpected broadcast intent: " + intent, false);
157                    return;
158            }
159            sendMessage(msg);
160        }
161    };
162
163    /**
164     * Device state monitor constructor. Note that each phone object should have its own device
165     * state monitor, meaning there will be two device monitors on the multi-sim device.
166     *
167     * @param phone Phone object
168     */
169    public DeviceStateMonitor(Phone phone) {
170        mPhone = phone;
171        DisplayManager dm = (DisplayManager) phone.getContext().getSystemService(
172                Context.DISPLAY_SERVICE);
173        dm.registerDisplayListener(mDisplayListener, null);
174
175        mIsPowerSaveOn = isPowerSaveModeOn();
176        mIsCharging = isDeviceCharging();
177        mIsScreenOn = isScreenOn();
178        // Assuming tethering is always off after boot up.
179        mIsTetheringOn = false;
180        mIsLowDataExpected = false;
181
182        log("DeviceStateMonitor mIsPowerSaveOn=" + mIsPowerSaveOn + ",mIsScreenOn="
183                + mIsScreenOn + ",mIsCharging=" + mIsCharging, false);
184
185        final IntentFilter filter = new IntentFilter();
186        filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
187        filter.addAction(BatteryManager.ACTION_CHARGING);
188        filter.addAction(BatteryManager.ACTION_DISCHARGING);
189        filter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
190        mPhone.getContext().registerReceiver(mBroadcastReceiver, filter, null, mPhone);
191
192        mPhone.mCi.registerForRilConnected(this, EVENT_RIL_CONNECTED, null);
193    }
194
195    /**
196     * @return True if low data is expected
197     */
198    private boolean isLowDataExpected() {
199        return mIsPowerSaveOn || (!mIsCharging && !mIsTetheringOn && !mIsScreenOn);
200    }
201
202    /**
203     * @return True if signal strength update should be turned off.
204     */
205    private boolean shouldTurnOffSignalStrength() {
206        return mIsPowerSaveOn || (!mIsCharging && !mIsScreenOn);
207    }
208
209    /**
210     * @return True if full network update should be turned off. Only significant changes will
211     * trigger the network update unsolicited response.
212     */
213    private boolean shouldTurnOffFullNetworkUpdate() {
214        return mIsPowerSaveOn || (!mIsCharging && !mIsScreenOn && !mIsTetheringOn);
215    }
216
217    /**
218     * @return True if data dormancy status update should be turned off.
219     */
220    private boolean shouldTurnOffDormancyUpdate() {
221        return mIsPowerSaveOn || (!mIsCharging && !mIsTetheringOn && !mIsScreenOn);
222    }
223
224    /**
225     * Message handler
226     *
227     * @param msg The message
228     */
229    @Override
230    public void handleMessage(Message msg) {
231        log("handleMessage msg=" + msg, false);
232        switch (msg.what) {
233            case EVENT_RIL_CONNECTED:
234                onRilConnected();
235                break;
236            default:
237                updateDeviceState(msg.what, msg.arg1 != 0);
238        }
239    }
240
241    /**
242     * Update the device and send the information to the modem.
243     *
244     * @param eventType Device state event type
245     * @param state True if enabled/on, otherwise disabled/off.
246     */
247    private void updateDeviceState(int eventType, boolean state) {
248        switch (eventType) {
249            case EVENT_SCREEN_STATE_CHANGED:
250                if (mIsScreenOn == state) return;
251                mIsScreenOn = state;
252                break;
253            case EVENT_CHARGING_STATE_CHANGED:
254                if (mIsCharging == state) return;
255                mIsCharging = state;
256                sendDeviceState(CHARGING_STATE, mIsCharging);
257                break;
258            case EVENT_TETHERING_STATE_CHANGED:
259                if (mIsTetheringOn == state) return;
260                mIsTetheringOn = state;
261                break;
262            case EVENT_POWER_SAVE_MODE_CHANGED:
263                if (mIsPowerSaveOn == state) return;
264                mIsPowerSaveOn = state;
265                sendDeviceState(POWER_SAVE_MODE, mIsPowerSaveOn);
266                break;
267            default:
268                return;
269        }
270
271        if (mIsLowDataExpected != isLowDataExpected()) {
272            mIsLowDataExpected = !mIsLowDataExpected;
273            sendDeviceState(LOW_DATA_EXPECTED, mIsLowDataExpected);
274        }
275
276        int newFilter = 0;
277        if (!shouldTurnOffSignalStrength()) {
278            newFilter |= IndicationFilter.SIGNAL_STRENGTH;
279        }
280
281        if (!shouldTurnOffFullNetworkUpdate()) {
282            newFilter |= IndicationFilter.FULL_NETWORK_STATE;
283        }
284
285        if (!shouldTurnOffDormancyUpdate()) {
286            newFilter |= IndicationFilter.DATA_CALL_DORMANCY_CHANGED;
287        }
288
289        setUnsolResponseFilter(newFilter, false);
290    }
291
292    /**
293     * Called when RIL is connected during boot up or reconnected after modem restart.
294     *
295     * When modem crashes, if the user turns the screen off before RIL reconnects, device
296     * state and filter cannot be sent to modem. Resend the state here so that modem
297     * has the correct state (to stop signal strength reporting, etc).
298     */
299    private void onRilConnected() {
300        log("RIL connected.", true);
301        sendDeviceState(CHARGING_STATE, mIsCharging);
302        sendDeviceState(LOW_DATA_EXPECTED, mIsLowDataExpected);
303        sendDeviceState(POWER_SAVE_MODE, mIsPowerSaveOn);
304        setUnsolResponseFilter(mUnsolicitedResponseFilter, true);
305    }
306
307    /**
308     * Convert the device state type into string
309     *
310     * @param type Device state type
311     * @return The converted string
312     */
313    private String deviceTypeToString(int type) {
314        switch (type) {
315            case CHARGING_STATE: return "CHARGING_STATE";
316            case LOW_DATA_EXPECTED: return "LOW_DATA_EXPECTED";
317            case POWER_SAVE_MODE: return "POWER_SAVE_MODE";
318            default: return "UNKNOWN";
319        }
320    }
321
322    /**
323     * Send the device state to the modem.
324     *
325     * @param type Device state type. See DeviceStateType defined in types.hal.
326     * @param state True if enabled/on, otherwise disabled/off
327     */
328    private void sendDeviceState(int type, boolean state) {
329        log("send type: " + deviceTypeToString(type) + ", state=" + state, true);
330        mPhone.mCi.sendDeviceState(type, state, null);
331    }
332
333    /**
334     * Turn on/off the unsolicited response from the modem.
335     *
336     * @param newFilter See UnsolicitedResponseFilter in types.hal for the definition of each bit.
337     * @param force Always set the filter when true.
338     */
339    private void setUnsolResponseFilter(int newFilter, boolean force) {
340        if (force || newFilter != mUnsolicitedResponseFilter) {
341            log("old filter: " + mUnsolicitedResponseFilter + ", new filter: " + newFilter, true);
342            mPhone.mCi.setUnsolResponseFilter(newFilter, null);
343            mUnsolicitedResponseFilter = newFilter;
344        }
345    }
346
347    /**
348     * @return True if the device is currently in power save mode.
349     * See {@link android.os.BatteryManager#isPowerSaveMode BatteryManager.isPowerSaveMode()}.
350     */
351    private boolean isPowerSaveModeOn() {
352        final PowerManager pm = (PowerManager) mPhone.getContext().getSystemService(
353                Context.POWER_SERVICE);
354        return pm.isPowerSaveMode();
355    }
356
357    /**
358     * @return Return true if the battery is currently considered to be charging. This means that
359     * the device is plugged in and is supplying sufficient power that the battery level is
360     * going up (or the battery is fully charged).
361     * See {@link android.os.BatteryManager#isCharging BatteryManager.isCharging()}.
362     */
363    private boolean isDeviceCharging() {
364        final BatteryManager bm = (BatteryManager) mPhone.getContext().getSystemService(
365                Context.BATTERY_SERVICE);
366        return bm.isCharging();
367    }
368
369    /**
370     * @return True if one the device's screen (e.g. main screen, wifi display, HDMI display, or
371     *         Android auto, etc...) is on.
372     */
373    private boolean isScreenOn() {
374        // Note that we don't listen to Intent.SCREEN_ON and Intent.SCREEN_OFF because they are no
375        // longer adequate for monitoring the screen state since they are not sent in cases where
376        // the screen is turned off transiently such as due to the proximity sensor.
377        final DisplayManager dm = (DisplayManager) mPhone.getContext().getSystemService(
378                Context.DISPLAY_SERVICE);
379        Display[] displays = dm.getDisplays();
380
381        if (displays != null) {
382            for (Display display : displays) {
383                // Anything other than STATE_ON is treated as screen off, such as STATE_DOZE,
384                // STATE_DOZE_SUSPEND, etc...
385                if (display.getState() == Display.STATE_ON) {
386                    log("Screen " + Display.typeToString(display.getType()) + " on", true);
387                    return true;
388                }
389            }
390            log("Screens all off", true);
391            return false;
392        }
393
394        log("No displays found", true);
395        return false;
396    }
397
398    /**
399     * @param msg Debug message
400     * @param logIntoLocalLog True if log into the local log
401     */
402    private void log(String msg, boolean logIntoLocalLog) {
403        if (DBG) Rlog.d(TAG, msg);
404        if (logIntoLocalLog) {
405            mLocalLog.log(msg);
406        }
407    }
408
409    /**
410     * Print the DeviceStateMonitor into the given stream.
411     *
412     * @param fd The raw file descriptor that the dump is being sent to.
413     * @param pw A PrintWriter to which the dump is to be set.
414     * @param args Additional arguments to the dump request.
415     */
416    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
417        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
418        ipw.increaseIndent();
419        ipw.println("mIsTetheringOn=" + mIsTetheringOn);
420        ipw.println("mIsScreenOn=" + mIsScreenOn);
421        ipw.println("mIsCharging=" + mIsCharging);
422        ipw.println("mIsPowerSaveOn=" + mIsPowerSaveOn);
423        ipw.println("mIsLowDataExpected=" + mIsLowDataExpected);
424        ipw.println("mUnsolicitedResponseFilter=" + mUnsolicitedResponseFilter);
425        ipw.println("Local logs:");
426        ipw.increaseIndent();
427        mLocalLog.dump(fd, ipw, args);
428        ipw.decreaseIndent();
429        ipw.decreaseIndent();
430        ipw.flush();
431    }
432}
433