150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu/* 250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Copyright (C) 2017 The Android Open Source Project 350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * 450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Licensed under the Apache License, Version 2.0 (the "License"); 550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * you may not use this file except in compliance with the License. 650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * You may obtain a copy of the License at 750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * 850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * http://www.apache.org/licenses/LICENSE-2.0 950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * 1050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Unless required by applicable law or agreed to in writing, software 1150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * distributed under the License is distributed on an "AS IS" BASIS, 1250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * See the License for the specific language governing permissions and 1450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * limitations under the License. 1550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 1650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 1750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yupackage com.android.internal.telephony; 1850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 1950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport static android.hardware.radio.V1_0.DeviceStateType.CHARGING_STATE; 2050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport static android.hardware.radio.V1_0.DeviceStateType.LOW_DATA_EXPECTED; 2150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE; 2250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 2350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.content.BroadcastReceiver; 2450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.content.Context; 2550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.content.Intent; 2650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.content.IntentFilter; 2750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.hardware.display.DisplayManager; 2850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.hardware.radio.V1_0.IndicationFilter; 2950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.net.ConnectivityManager; 3050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.os.BatteryManager; 3150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.os.Handler; 3250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.os.Message; 3350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.os.PowerManager; 3450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.telephony.Rlog; 3550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.util.LocalLog; 3650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport android.view.Display; 3750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 3850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport com.android.internal.util.IndentingPrintWriter; 3950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 4050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport java.io.FileDescriptor; 4150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport java.io.PrintWriter; 4250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yuimport java.util.ArrayList; 4350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 4450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu/** 4550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * The device state monitor monitors the device state such as charging state, power saving sate, 4650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * and then passes down the information to the radio modem for the modem to perform its own 4750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * proprietary power saving strategy. Device state monitor also turns off the unsolicited 4850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * response from the modem when the device does not need to receive it, for example, device's 4950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * screen is off and does not have activities like tethering, remote display, etc...This effectively 5050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * prevents the CPU from waking up by those unnecessary unsolicited responses such as signal 5150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * strength update. 5250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 5350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yupublic class DeviceStateMonitor extends Handler { 5450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu protected static final boolean DBG = false; /* STOPSHIP if true */ 5550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu protected static final String TAG = DeviceStateMonitor.class.getSimpleName(); 5650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 5750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private static final int EVENT_RIL_CONNECTED = 0; 5850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private static final int EVENT_SCREEN_STATE_CHANGED = 1; 5950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private static final int EVENT_POWER_SAVE_MODE_CHANGED = 2; 6050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private static final int EVENT_CHARGING_STATE_CHANGED = 3; 6150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private static final int EVENT_TETHERING_STATE_CHANGED = 4; 6250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 6350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private final Phone mPhone; 6450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 6550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private final LocalLog mLocalLog = new LocalLog(100); 6650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 6750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 6850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Flag for wifi/usb/bluetooth tethering turned on or not 6950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 7050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private boolean mIsTetheringOn; 7150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 7250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 73f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu * Screen state provided by Display Manager. True indicates one of the screen is on, otherwise 74f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu * all off. 7550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 76f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu private boolean mIsScreenOn; 7750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 7850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 7950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Indicating the device is plugged in and is supplying sufficient power that the battery level 8050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * is going up (or the battery is fully charged). See BatteryManager.isCharging() for the 8150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * details 8250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 8350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private boolean mIsCharging; 8450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 8550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 8650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Flag for device power save mode. See PowerManager.isPowerSaveMode() for the details. 8750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Note that it is not possible both mIsCharging and mIsPowerSaveOn are true at the same time. 8850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * The system will automatically end power save mode when the device starts charging. 8950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 9050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private boolean mIsPowerSaveOn; 9150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 9250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 9350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Low data expected mode. True indicates low data traffic is expected, for example, when the 9450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * device is idle (e.g. screen is off and not doing tethering in the background). Note this 9550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * doesn't mean no data is expected. 9650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 9750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private boolean mIsLowDataExpected; 9850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 9950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 10050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * The unsolicited response filter. See IndicationFilter defined in types.hal for the definition 10150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * of each bit. 10250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 10350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private int mUnsolicitedResponseFilter = IndicationFilter.ALL; 10450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 10550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private final DisplayManager.DisplayListener mDisplayListener = 10650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu new DisplayManager.DisplayListener() { 10750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu @Override 10850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu public void onDisplayAdded(int displayId) { } 10950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 11050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu @Override 11150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu public void onDisplayRemoved(int displayId) { } 11250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 11350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu @Override 11450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu public void onDisplayChanged(int displayId) { 115f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu boolean screenOn = isScreenOn(); 116f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu Message msg = obtainMessage(EVENT_SCREEN_STATE_CHANGED); 117f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu msg.arg1 = screenOn ? 1 : 0; 118f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu sendMessage(msg); 11950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 12050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu }; 12150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 12250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 12350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Device state broadcast receiver 12450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 12550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 12650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu @Override 12750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu public void onReceive(Context context, Intent intent) { 12850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu log("received: " + intent, true); 12950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 13050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu Message msg; 13150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu switch (intent.getAction()) { 13250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED: 13350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu msg = obtainMessage(EVENT_POWER_SAVE_MODE_CHANGED); 13450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu msg.arg1 = isPowerSaveModeOn() ? 1 : 0; 13550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu log("Power Save mode " + ((msg.arg1 == 1) ? "on" : "off"), true); 13650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu break; 13750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu case BatteryManager.ACTION_CHARGING: 13850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu msg = obtainMessage(EVENT_CHARGING_STATE_CHANGED); 13950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu msg.arg1 = 1; // charging 14050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu break; 14150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu case BatteryManager.ACTION_DISCHARGING: 14250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu msg = obtainMessage(EVENT_CHARGING_STATE_CHANGED); 14350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu msg.arg1 = 0; // not charging 14450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu break; 14550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu case ConnectivityManager.ACTION_TETHER_STATE_CHANGED: 14650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu ArrayList<String> activeTetherIfaces = intent.getStringArrayListExtra( 14750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu ConnectivityManager.EXTRA_ACTIVE_TETHER); 14850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 14950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu boolean isTetheringOn = activeTetherIfaces != null 15050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu && activeTetherIfaces.size() > 0; 15150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu log("Tethering " + (isTetheringOn ? "on" : "off"), true); 15250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu msg = obtainMessage(EVENT_TETHERING_STATE_CHANGED); 15350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu msg.arg1 = isTetheringOn ? 1 : 0; 15450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu break; 15550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu default: 15650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu log("Unexpected broadcast intent: " + intent, false); 15750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu return; 15850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 15950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu sendMessage(msg); 16050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 16150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu }; 16250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 16350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 16450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Device state monitor constructor. Note that each phone object should have its own device 16550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * state monitor, meaning there will be two device monitors on the multi-sim device. 16650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * 16750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param phone Phone object 16850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 16950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu public DeviceStateMonitor(Phone phone) { 17050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mPhone = phone; 17150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu DisplayManager dm = (DisplayManager) phone.getContext().getSystemService( 17250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu Context.DISPLAY_SERVICE); 17350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu dm.registerDisplayListener(mDisplayListener, null); 17450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 17550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mIsPowerSaveOn = isPowerSaveModeOn(); 17650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mIsCharging = isDeviceCharging(); 177f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu mIsScreenOn = isScreenOn(); 17850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu // Assuming tethering is always off after boot up. 17950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mIsTetheringOn = false; 18050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mIsLowDataExpected = false; 18150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 182f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu log("DeviceStateMonitor mIsPowerSaveOn=" + mIsPowerSaveOn + ",mIsScreenOn=" 183f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu + mIsScreenOn + ",mIsCharging=" + mIsCharging, false); 18450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 18550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu final IntentFilter filter = new IntentFilter(); 18650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); 18750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu filter.addAction(BatteryManager.ACTION_CHARGING); 18850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu filter.addAction(BatteryManager.ACTION_DISCHARGING); 18950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu filter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); 19050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mPhone.getContext().registerReceiver(mBroadcastReceiver, filter, null, mPhone); 19150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 19250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mPhone.mCi.registerForRilConnected(this, EVENT_RIL_CONNECTED, null); 19350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 19450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 19550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 19650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @return True if low data is expected 19750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 19850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private boolean isLowDataExpected() { 199f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu return mIsPowerSaveOn || (!mIsCharging && !mIsTetheringOn && !mIsScreenOn); 20050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 20150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 20250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 20350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @return True if signal strength update should be turned off. 20450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 20550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private boolean shouldTurnOffSignalStrength() { 206f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu return mIsPowerSaveOn || (!mIsCharging && !mIsScreenOn); 20750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 20850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 20950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 21050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @return True if full network update should be turned off. Only significant changes will 21150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * trigger the network update unsolicited response. 21250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 21350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private boolean shouldTurnOffFullNetworkUpdate() { 214f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu return mIsPowerSaveOn || (!mIsCharging && !mIsScreenOn && !mIsTetheringOn); 21550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 21650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 21750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 21850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @return True if data dormancy status update should be turned off. 21950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 22050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private boolean shouldTurnOffDormancyUpdate() { 221f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu return mIsPowerSaveOn || (!mIsCharging && !mIsTetheringOn && !mIsScreenOn); 22250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 22350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 22450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 22550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Message handler 22650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * 22750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param msg The message 22850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 22950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu @Override 23050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu public void handleMessage(Message msg) { 23150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu log("handleMessage msg=" + msg, false); 23250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu switch (msg.what) { 23350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu case EVENT_RIL_CONNECTED: 23450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu onRilConnected(); 23550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu break; 23650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu default: 23750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu updateDeviceState(msg.what, msg.arg1 != 0); 23850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 23950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 24050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 24150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 24250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Update the device and send the information to the modem. 24350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * 24450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param eventType Device state event type 24550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param state True if enabled/on, otherwise disabled/off. 24650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 24750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private void updateDeviceState(int eventType, boolean state) { 24850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu switch (eventType) { 24950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu case EVENT_SCREEN_STATE_CHANGED: 250f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu if (mIsScreenOn == state) return; 251f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu mIsScreenOn = state; 25250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu break; 25350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu case EVENT_CHARGING_STATE_CHANGED: 25450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu if (mIsCharging == state) return; 25550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mIsCharging = state; 25650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu sendDeviceState(CHARGING_STATE, mIsCharging); 25750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu break; 25850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu case EVENT_TETHERING_STATE_CHANGED: 25950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu if (mIsTetheringOn == state) return; 26050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mIsTetheringOn = state; 26150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu break; 26250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu case EVENT_POWER_SAVE_MODE_CHANGED: 26350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu if (mIsPowerSaveOn == state) return; 26450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mIsPowerSaveOn = state; 26550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu sendDeviceState(POWER_SAVE_MODE, mIsPowerSaveOn); 26650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu break; 26750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu default: 26850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu return; 26950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 27050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 27150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu if (mIsLowDataExpected != isLowDataExpected()) { 27250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mIsLowDataExpected = !mIsLowDataExpected; 27350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu sendDeviceState(LOW_DATA_EXPECTED, mIsLowDataExpected); 27450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 27550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 27650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu int newFilter = 0; 27750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu if (!shouldTurnOffSignalStrength()) { 27850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu newFilter |= IndicationFilter.SIGNAL_STRENGTH; 27950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 28050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 28150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu if (!shouldTurnOffFullNetworkUpdate()) { 28250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu newFilter |= IndicationFilter.FULL_NETWORK_STATE; 28350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 28450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 28550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu if (!shouldTurnOffDormancyUpdate()) { 28650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu newFilter |= IndicationFilter.DATA_CALL_DORMANCY_CHANGED; 28750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 28850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 28950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu setUnsolResponseFilter(newFilter, false); 29050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 29150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 29250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 29350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Called when RIL is connected during boot up or reconnected after modem restart. 29450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * 29550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * When modem crashes, if the user turns the screen off before RIL reconnects, device 29650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * state and filter cannot be sent to modem. Resend the state here so that modem 29750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * has the correct state (to stop signal strength reporting, etc). 29850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 29950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private void onRilConnected() { 30050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu log("RIL connected.", true); 30150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu sendDeviceState(CHARGING_STATE, mIsCharging); 30250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu sendDeviceState(LOW_DATA_EXPECTED, mIsLowDataExpected); 30350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu sendDeviceState(POWER_SAVE_MODE, mIsPowerSaveOn); 30450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu setUnsolResponseFilter(mUnsolicitedResponseFilter, true); 30550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 30650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 30750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 30850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Convert the device state type into string 30950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * 31050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param type Device state type 31150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @return The converted string 31250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 31350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private String deviceTypeToString(int type) { 31450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu switch (type) { 31550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu case CHARGING_STATE: return "CHARGING_STATE"; 31650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu case LOW_DATA_EXPECTED: return "LOW_DATA_EXPECTED"; 31750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu case POWER_SAVE_MODE: return "POWER_SAVE_MODE"; 31850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu default: return "UNKNOWN"; 31950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 32050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 32150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 32250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 32350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Send the device state to the modem. 32450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * 32550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param type Device state type. See DeviceStateType defined in types.hal. 32650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param state True if enabled/on, otherwise disabled/off 32750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 32850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private void sendDeviceState(int type, boolean state) { 32950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu log("send type: " + deviceTypeToString(type) + ", state=" + state, true); 33050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mPhone.mCi.sendDeviceState(type, state, null); 33150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 33250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 33350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 33450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Turn on/off the unsolicited response from the modem. 33550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * 33650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param newFilter See UnsolicitedResponseFilter in types.hal for the definition of each bit. 33750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param force Always set the filter when true. 33850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 33950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private void setUnsolResponseFilter(int newFilter, boolean force) { 34050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu if (force || newFilter != mUnsolicitedResponseFilter) { 34150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu log("old filter: " + mUnsolicitedResponseFilter + ", new filter: " + newFilter, true); 34250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mPhone.mCi.setUnsolResponseFilter(newFilter, null); 34350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mUnsolicitedResponseFilter = newFilter; 34450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 34550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 34650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 34750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 34850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @return True if the device is currently in power save mode. 34950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * See {@link android.os.BatteryManager#isPowerSaveMode BatteryManager.isPowerSaveMode()}. 35050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 35150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private boolean isPowerSaveModeOn() { 35250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu final PowerManager pm = (PowerManager) mPhone.getContext().getSystemService( 35350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu Context.POWER_SERVICE); 35450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu return pm.isPowerSaveMode(); 35550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 35650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 35750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 35850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @return Return true if the battery is currently considered to be charging. This means that 35950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * the device is plugged in and is supplying sufficient power that the battery level is 36050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * going up (or the battery is fully charged). 36150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * See {@link android.os.BatteryManager#isCharging BatteryManager.isCharging()}. 36250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 36350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private boolean isDeviceCharging() { 36450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu final BatteryManager bm = (BatteryManager) mPhone.getContext().getSystemService( 36550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu Context.BATTERY_SERVICE); 36650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu return bm.isCharging(); 36750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 36850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 36950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 370f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu * @return True if one the device's screen (e.g. main screen, wifi display, HDMI display, or 371f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu * Android auto, etc...) is on. 37250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 37350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private boolean isScreenOn() { 37450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu // Note that we don't listen to Intent.SCREEN_ON and Intent.SCREEN_OFF because they are no 37550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu // longer adequate for monitoring the screen state since they are not sent in cases where 37650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu // the screen is turned off transiently such as due to the proximity sensor. 377f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu final DisplayManager dm = (DisplayManager) mPhone.getContext().getSystemService( 378f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu Context.DISPLAY_SERVICE); 379f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu Display[] displays = dm.getDisplays(); 380f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu 381f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu if (displays != null) { 382f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu for (Display display : displays) { 383f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu // Anything other than STATE_ON is treated as screen off, such as STATE_DOZE, 384f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu // STATE_DOZE_SUSPEND, etc... 385f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu if (display.getState() == Display.STATE_ON) { 386f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu log("Screen " + Display.typeToString(display.getType()) + " on", true); 387f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu return true; 388f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu } 389f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu } 390f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu log("Screens all off", true); 391f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu return false; 392f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu } 39350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 394f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu log("No displays found", true); 395f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu return false; 39650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 39750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 39850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 39950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param msg Debug message 40050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param logIntoLocalLog True if log into the local log 40150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 40250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu private void log(String msg, boolean logIntoLocalLog) { 40350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu if (DBG) Rlog.d(TAG, msg); 40450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu if (logIntoLocalLog) { 40550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mLocalLog.log(msg); 40650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 40750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 40850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu 40950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu /** 41050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * Print the DeviceStateMonitor into the given stream. 41150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * 41250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param fd The raw file descriptor that the dump is being sent to. 41350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param pw A PrintWriter to which the dump is to be set. 41450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu * @param args Additional arguments to the dump request. 41550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu */ 41650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 41750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 41850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu ipw.increaseIndent(); 41950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu ipw.println("mIsTetheringOn=" + mIsTetheringOn); 420f15cc290d7fb870ed5a1c7a0824bf6177f234f62Jack Yu ipw.println("mIsScreenOn=" + mIsScreenOn); 42150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu ipw.println("mIsCharging=" + mIsCharging); 42250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu ipw.println("mIsPowerSaveOn=" + mIsPowerSaveOn); 42350b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu ipw.println("mIsLowDataExpected=" + mIsLowDataExpected); 42450b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu ipw.println("mUnsolicitedResponseFilter=" + mUnsolicitedResponseFilter); 42550b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu ipw.println("Local logs:"); 42650b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu ipw.increaseIndent(); 42750b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu mLocalLog.dump(fd, ipw, args); 42850b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu ipw.decreaseIndent(); 42950b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu ipw.decreaseIndent(); 43050b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu ipw.flush(); 43150b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu } 43250b941e59787a21dd2d4f3d25d700538efb9abc5Jack Yu} 433