Source Code Analysis of Android Doze Mode

Keywords: Android Java Mobile Google

Bionics of science and technology is ubiquitous and gives us inspiration. In order to prolong the battery life, google learned from the snake hibernation that in some cases mobile phones can also enter the hibernation-like situation, thus introducing today's theme, Doze mode, Doze Chinese is napping, napping certainly saves energy than activity.

What happens when a cell phone takes a nap?

According to google's official statement, Walklocks, network access, jobshedule, alarm clocks, GPS/WiFi scans will all stop. After these stops, 30% of the electricity will be saved.

When will mobile phones start to take a nap?

The figure above is Google's Doze chronological sketch, which shows that there are three conditions for a cell phone to take a nap.

1. Screen extinction
2. no plug in
3. Stillness

Is this very bionic? Screen goes out - > Close your eyes, don't plug in - > Don't eat, stay still - > Be a sleeping beauty quietly. Aren't creatures also required to satisfy these conditions in order to take a nap? Wonderful, is wonderful!

Do you have to breathe to take a nap? The maintenance window in the picture above is for you to breathe!! Walklocks, web access, jobshedule, alarm clocks, GPS/WiFi scans will all be restored when you breathe. Come on and take a big breath of fresh air. As time goes on, the interval between breaths gets bigger and bigger, and the time of each breath gets longer. Of course, buddy, it won't be infinitely long! ________ Ultimately it all comes down to a fixed value. The following analysis of the source code to know, biu!

No source code, say a ball.

Next, a mobile phone is placed quietly on the desktop. As time goes on, the process of entering the doze mode is used to analyze the source code.
Source path:/ frameworks/base/services/core/java/com/android/server/DeviceIdleController.java
The current state of doze is represented by a global integer in the system

1 private int mState;

The possible values of the state values are as follows, starting with STATE_ACTIVE. It passes through 1,2,3,4 in turn and then enters 5 states, namely STATE_IDLE.

1     private static final int STATE_ACTIVE = 0;
2     private static final int STATE_INACTIVE = 1;
3     private static final int STATE_IDLE_PENDING = 2;
4     private static final int STATE_SENSING = 3;
5     private static final int STATE_LOCATING = 4;
6     private static final int STATE_IDLE = 5;
7     private static final int STATE_IDLE_MAINTENANCE = 6;

Firstly, the screen is turned off, and the screen processing function is called back.

 1     private final DisplayManager.DisplayListener mDisplayListener
 2             = new DisplayManager.DisplayListener() {
 3         @Override public void onDisplayAdded(int displayId) {
 4         }
 5 
 6         @Override public void onDisplayRemoved(int displayId) {
 7         }
 8 
 9         @Override public void onDisplayChanged(int displayId) {
10             if (displayId == Display.DEFAULT_DISPLAY) {
11                 synchronized (DeviceIdleController.this) {
12                     updateDisplayLocked();  //Screen status change
13                 }
14             }
15         }
16     };

Enter updateDisplayLocked

1     void updateDisplayLocked() {
2                 ...
3                 becomeInactiveIfAppropriateLocked(); //See if you can enter Inactive state
4                 ....
5         }
6     }

Then we pull out the usb and call back the charging function without charging.

 1     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
 2         @Override public void onReceive(Context context, Intent intent) {
 3             if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
 4                 int plugged = intent.getIntExtra("plugged", 0);
 5                 updateChargingLocked(plugged != 0); //Charging status change
 6             } else if (ACTION_STEP_IDLE_STATE.equals(intent.getAction())) {
 7                 synchronized (DeviceIdleController.this) {
 8                     stepIdleStateLocked();
 9                 }
10             }
11         }
12     };

Enter updateChargingLocked

1     void updateChargingLocked(boolean charging) {
2          ....
3          becomeInactiveIfAppropriateLocked();//See if you can enter Inactive state
4          .....
5     }

Eventually, without plugging in or turning off the screen, it goes to becomeInactiveIfAppropriateLocked, the state mState becomes STATE_INACTIVE, and a timer is turned on.

 1 void becomeInactiveIfAppropriateLocked() {
 2         if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
 3         //The condition of no plug-in and screen extinguishing is satisfied.
 4         if (((!mScreenOn && !mCharging) || mForceIdle) && mEnabled && mState == STATE_ACTIVE) {
 5             .....
 6             mState = STATE_INACTIVE;
 7             scheduleAlarmLocked(mInactiveTimeout, false);   
 8             ......
 9         }
10     }
11 
12     The timing is constant for 30 minutes.
13     INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT,
14                         !COMPRESS_TIME ? 30 * 60 * 1000L : 3 * 60 * 1000L);

When the timer arrives, pending intent will be sent out and the broadcasting receiver will process it.

 1  Intent intent = new Intent(ACTION_STEP_IDLE_STATE)
 2                      .setPackage("android")
 3                      .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
 4  mAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
 5 
 6     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
 7         @Override public void onReceive(Context context, Intent intent) {
 8             if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
 9                 int plugged = intent.getIntExtra("plugged", 0);
10                 updateChargingLocked(plugged != 0);
11             } else if (ACTION_STEP_IDLE_STATE.equals(intent.getAction())) {
12                 synchronized (DeviceIdleController.this) {
13                     stepIdleStateLocked();   //Receive broadcasting
14                 }
15             }
16         }
17     };

Enter step IdleStateLocked, which is the main function for state transition processing

 1     void stepIdleStateLocked() {
 2         if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
 3         EventLogTags.writeDeviceIdleStep();
 4 
 5         final long now = SystemClock.elapsedRealtime();
 6         if ((now+mConstants.MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {
 7             // Whoops, there is an upcoming alarm.  We don't actually want to go idle.
 8             if (mState != STATE_ACTIVE) {
 9                 becomeActiveLocked("alarm", Process.myUid());
10             }
11             return;
12         }
13 
14         switch (mState) {
15             case STATE_INACTIVE:
16                 // We have now been inactive long enough, it is time to start looking
17                 // for significant motion and sleep some more while doing so.
18                 startMonitoringSignificantMotion(); //Observe if there are any small movements.
19                 scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false); //How long does it take to set up a small observation action?
20                 mState = STATE_IDLE_PENDING; //The status is updated to STATE_IDLE_PENDING
21                 break;
22             case STATE_IDLE_PENDING: //Small action observation is over. It's terrific. There's no small action all the time. It's going to come in here.
23                 mState = STATE_SENSING;//The status is updated to STATE_SENSING
24                 scheduleSensingAlarmLocked(mConstants.SENSING_TIMEOUT);//Setting Sensor Induction Time
25                 mAnyMotionDetector.checkForAnyMotion(); //Sensor Sensor Sensor Whether Mobile Phone Moves
26                 break;
27             case STATE_SENSING: //The sensor didn't find the mobile phone moving, so it's the last one. Look at it. GPS Did you move?
28                 mState = STATE_LOCATING;//The status is updated to STATE_LOCATING
29                 scheduleSensingAlarmLocked(mConstants.LOCATING_TIMEOUT);//Set up GPS Observation duration
30                 mLocationManager.requestLocationUpdates(mLocationRequest, mGenericLocationListener,
31                         mHandler.getLooper());//GPS Start induction
32                 break;
33             case STATE_LOCATING:  //GPS It also found no movement.
34                 cancelSensingAlarmLocked();
35                 cancelLocatingLocked();
36                 mAnyMotionDetector.stop();  //Nothing here break,Go straight to the next case
37             case STATE_IDLE_MAINTENANCE:
38                 scheduleAlarmLocked(mNextIdleDelay, true);//Set how long to take a nap and breathe
39                 mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);//Update how long to breathe after the next nap
40                 if (DEBUG) Slog.d(TAG, "Setting mNextIdleDelay = " + mNextIdleDelay);
41                 mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT);
42                 mState = STATE_IDLE; //Oh yeah finally got in. STATE_IDLE
43                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
44                 break;
45             case STATE_IDLE: //After a nap, take a breath. That's it.
46                 scheduleAlarmLocked(mNextIdlePendingDelay, false);
47                 mState = STATE_IDLE_MAINTENANCE; //The status is updated to STATE_IDLE_MAINTENANCE
48                 mNextIdlePendingDelay = Math.min(mConstants.MAX_IDLE_PENDING_TIMEOUT,
49                         (long)(mNextIdlePendingDelay * mConstants.IDLE_PENDING_FACTOR));
50                //Update the next breathing time
51                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
52                 break;
53         }
54     }

Math.min(mConstants.MAX_IDLE_PENDING_TIMEOUT,
(long)(mNextIdlePendingDelay * mConstants.IDLE_PENDING_FACTOR));
Do you see this sentence? Take the minimum value, which is to ensure that idle and window time will not become infinite.
In order to give you a sensory experience, let me just list some of the time above.

Turn off the screen and enter INACTIVE time without plugging in for 30 minutes.

Observation of small movements for 30 minutes
IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
                        !COMPRESS_TIME ? 30 * 60 * 1000L : 3 * 60 * 1000L);

Observing the sensor for 4 minutes
SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT,
                        !DEBUG ? 4 * 60 * 1000L : 60 * 1000L);

Observation of GPS takes 30 seconds
LOCATING_TIMEOUT = mParser.getLong(KEY_LOCATING_TIMEOUT,
                        !DEBUG ? 30 * 1000L : 15 * 1000L);

So the total time to enter idle is 30 minutes + 30 minutes + 4 minutes + 30 s = 1 hour, 4 minutes and 30 seconds, haha ha ha!!

Let's look at a state transition diagram below. Before idle state is reached, basically anything will change back to ACTIVE state. When it becomes IDLE state, it can only plug in or light the screen before leaving IDLE state. Just like before you fall asleep, it's easy to wake up, but after deep sleep, it's estimated that only the alarm clock can wake you up!!

 

 

So much said above, what does it have to do with my application development?

Actually, it doesn't matter much. Look at the source code.
But as a new mechanism, it's best to test whether your application can work properly in these states, at least not hang up.
google provides adb instructions to force states to change, so you don't have to wait for them to change.

1 adb shell dumpsys battery unplug       //Equivalent to no plug-in
2 adb shell dumpsys device idle step     //Let the state transition

 

From: http://www.jianshu.com/p/8fb25f53bed4?Utm_campaign=haruki&utm_content=note&utm_medium=reader_share&utm_source=qqqq#

Posted by nigma on Sat, 23 Mar 2019 07:42:54 -0700