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