android6.0系统 Doze模式(DeviceIdle)实现与控制逻辑

DeviceIdleController的实现逻辑


1.服务启动 2

1.1SystemServer.java启动阶段 2

1.2准备阶段 5

2. 进入Idle模式 6

2.1进入idle模式的流程 6

3. 状态变化的驱动因素 10

4.进入Idle模式后的处理 10

4.1对于PowerManager的限制 11

4.2对网络的限制 13

4.3 Alarm限制 13


1.服务启动

 Doze功能概述

当系统灭屏并长时间处于静止状态时,系统会进入Doze状态,此时

1)不在White list里的APP会被限制网络访问

2wake lock会被忽略

3)通过alarm manger设置的alarm 被推迟,除非设置了允许在idle状态也能工作的flag

5wifi scan被取消

6jobschedule sync被推迟


1.1SystemServer.java启动阶段

 

startOtherServices() 中有

mSystemServiceManager.startService(DeviceIdleController.class);这里启动DeviceIdleController的服务

 

首先调用到DeviceIdleController的构造函数:

    public DeviceIdleController(Context context) {

        super(context);

        mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));

        mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());

}

构造函数仅仅做了两件事:1.创建一个保证原子操作的的mConfigFile的文件(系统/data/system/deviceidle.xml文件);2.创建一个handler线程执行idle的状态变化消息处理。

 

SystemServer的start的函数在执行了service的构造函数之后,会将该函数add到SystemService的list中,然后执行到Service的onStart函数中。

DeviceIdleController的onStart函数总共做了两件事:

 

   mEnabled = getContext().getResources().getBoolean(

                    com.android.internal.R.bool.config_enableAutoPowerModes);

Idle模式中开关在frameworks/base/core/res/res/values/config.xml中配置。原生逻辑默认是关闭的,可以修改配置打开

 

 

  SystemConfig sysConfig = SystemConfig.getInstance();

  ArraySet<String> allowPowerExceptIdle =   sysConfig.getAllowInPowerSaveExceptIdle();

            for (int i=0; i<allowPowerExceptIdle.size(); i++) {

                String pkg = allowPowerExceptIdle.valueAt(i);

                try {

                    ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);

                    if ((ai.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {

                        int appid = UserHandle.getAppId(ai.uid);

                        mPowerSaveWhitelistAppsExceptIdle.put(ai.packageNameappid);

                        mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appidtrue);

                    }

将配置文件中配置系统应用白名单应用添加到mPowerSaveWhitelistAppsExceptIdle列表中,省电模式下这些应用即使不在前台也能有联网权限(除了在Idle状态下)

 

  ArraySet<String> allowPower = sysConfig.getAllowInPowerSave();

            for (int i=0; i<allowPower.size(); i++) {

                String pkg = allowPower.valueAt(i);

                try {

                    ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);

                    if ((ai.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {

                        int appid = UserHandle.getAppId(ai.uid);

                        // These apps are on both the whitelist-except-idle as well

                        // as the full whitelist, so they apply in all cases.

                        mPowerSaveWhitelistAppsExceptIdle.put(ai.packageNameappid);

                        mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appidtrue);

                        mPowerSaveWhitelistApps.put(ai.packageNameappid);

                        mPowerSaveWhitelistSystemAppIds.put(appidtrue);

                    }

将系统完整省电模式的白名单应用添加到mPowerSaveWhitelistApps,该应用列表中的应用在省电模式下的所有状态都有联网权限。

 

 

            mConstants = new Constants(mHandler, getContext().getContentResolver());

 

            readConfigFileLocked();

            updateWhitelistAppIdsLocked();

将系统中/data/system/deviceidle.xml配置的用户App读取到mPowerSaveWhitelistUserApps白名单列表中去,并更新所有的白名单应用列表。省电模式下出Idle模式不能工作的白名单应用列表:mPowerSaveWhitelistExceptIdleAppIdArray省电模式下在Idle状态下也能工作的完全白名单列表:mPowerSaveWhitelistAllAppIdArraymPowerSaveWhitelistAllAppIdArray设置到PowerManagerService中的白名单中

 

            mScreenOn = true;

            mCharging = true;

            mState = STATE_ACTIVE;

            mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;

        }

 

        publishBinderService(Context.DEVICE_IDLE_CONTROLLERnew BinderService());

        publishLocalService(LocalService.classnew LocalService());

    }

 

2.将deviceidle注册到BinderService()中。将DeviceIdleController的内部类LocalService注册到本地服务中。

 

onStart的详细

mPowerSaveWhitelistAppsExceptIdle里读取的类似allow-in-power-save-except-idle,和allow-in-power-save的应用包名例如:

<allow-in-power-save-except-idle package="com.android.providers.downloads" />

 

<allow-in-power-save package="com.google.android.gms" />

 

 

读取allow-in-power-save的应用,并将配置的应用添加到mPowerSaveWhitelistApps的List中。并且通过PackageManager通过包名获取到应用的appid。

并且将mPowerSaveWhitelistSystemAppIds.put(appidtrue);

 

以上两种通过配置文件里面读取的系统应用,将其将入到mPowerSaveWhitelistSystemAppIdsExceptIdle, mPowerSaveWhitelistSystemAppIds两个列表中。在加入到列表时会判断该应用是否为系统应用,所以这两个列表里装的都是系统应用。

 

readConfigFileLocked()函数将读取系统中/data/system/deviceidle.xml文件,并将其解析出来应用的包名,将其解析出来的包名,通过pm获取应用信息,将其加入到mPowerSaveWhitelistUserApps列表中。

 

updateWhitelistAppIdsLocked()函数中建立两个列表:

mPowerSaveWhitelistExceptIdleAppIdArray:除了Idle模式下,省电模式的完整白名单

mPowerSaveWhitelistAllAppIdArray:列表里保存的完整的省电模式(包括Idle)白名单的应用ID。

 

调用PowerManagerService的setDeviceIdleWhitelist函数:

 mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);

将完整的省电模式下应用白名单,设置到PowerManagerService的wakelock列表中。

 

在onStart中创建:

mConstants = new Constants(mHandler, getContext().getContentResolver());

注册ContentObserver的,监听Settings.Global.DEVICE_IDLE_CONSTANTS,在updateConstants中初始化各个阶段的时间

INACTIVE_TIMEOUT = 30min

SENSING_TIMEOUT = 4min

LOCATING_TIMEOUT = 30s

MOTION_INACTIVE_TIMEOUT = 10min

IDLE_AFTER_INACTIVE_TIMEOUT = 30min

IDLE_PENDING_TIMEOUT = 5min

MAX_IDLE_PENDING_TIMEOUT = 10min

IDLE_PENDING_FACTOR =2f

IDLE_TIMEOUT = 1h

MAX_IDLE_TIMEOUT = 6h

IDLE_FACTOR = 2f

MIN_TIME_TO_ALARM = 1h

这些参数在后面进入Idle模式的过程中都将用到

 

 

最后将设置设备状态: mState = STATE_ACTIVE;

 

大致流程图如下:

 

 

 

 

 

 

1.2准备阶段

作为一个系统服务,启动过程都需要走到onBootPhase(int phase),当系统服务准备好了,就可以执行该服务的准备工作。

准备工作:1.获取AlarmManager服务

2.获取BatteryService服务

3.获取网络NetworkPolicyManager服务

4.获取Display服务

5.获取SensorManager服务(mSigMotionSensor)

6.获取LocationManager服务

7.创建运动检测对象

8.创建AlarmIntent(ACTION_STEP_IDLE_STATE),广播Intent。

9.创建mSensingAlarmIntentACTION_STEP_IDLE_STATE,在Sensing和Locating的时候广播Intent。

10.并注册广播接收器mReceiver,若接收到Intent(ACTION_STEP_IDLE_STATE)执行进入idle模式流程函数stepIdleStateLocked();

11.注册DisplayManager的监听器mDisplayListener,监听屏幕显示状态变化

 

 

 

2.进入Idle模式

2.1进入idle模式的流程

1).INACTIVE

进入idle模式主要是在Step,在屏幕进入灭屏幕状态时,mDisplayListener会监听到屏幕显示状态的变化,继而会调用到updateDisplayLocked();更新屏幕状态,进入到灭屏时,会在becomeInactiveIfAppropriateLocked() 中将设备状态mState设置为 STATE_INACTIVE,然后发送延迟为INACTIVE_TIMEOUT的一个带有mAlarmIntentalarm,由于mAlarmIntent中含有ACTION_STEP_IDLE_STATE的消息广播,所以在INACTIVE_TIMEOUT(30min)后,mReceiver会收到Intent为ACTION_STEP_IDLE_STATE的广播,进入到stepIdleStateLocked()中;

函数开始首先判断,下一次能从Idle状态唤醒的Alarm距离现在的时间,如果小于MIN_TIME_TO_ALARM,则直接唤醒设备,知道下一次唤醒Alarm发送成功:

final long now = SystemClock.elapsedRealtime();

if ((now+mConstants.MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {

    if (mState != STATE_ACTIVE) {

        becomeActiveLocked("alarm", Process.myUid());

    }

    return;

}

 

 

2).STATE_IDLE_PENDING;

如果不满足则开始一步一步进入Idle模式

我们可知当屏幕灭屏时,mState的状态是INACTIVE_TIMEOUT,30min后发送广播,则执行:

case STATE_INACTIVE:

           // We have now been inactive long enough, it is time to start looking

           // for significant motion and sleep some more while doing so.

           startMonitoringSignificantMotion();

           scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUTfalse);

           // Reset the upcoming idle delays.

           mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;

           mNextIdleDelay = mConstants.IDLE_TIMEOUT;

           mState = STATE_IDLE_PENDING;

           if (DEBUG) Slog.d(TAG"Moved from STATE_INACTIVE to STATE_IDLE_PENDING.");

           EventLogTags.writeDeviceIdle(mState"step");

           break;

此时mState已经是STATE_IDLE_PENDING的状态,开始检查SignificantMotion(显著运动)了;然后重新设置下一次Alarm发送的时间为IDLE_AFTER_INACTIVE_TIMEOUT(30min)。并且在此初始化mNextIdlePendingDelay(自动退出Idle的间隙时间5min),和 mNextIdleDelay(Idle持续时间1h).

 

3).SENSING:

case STATE_IDLE_PENDING:

     mState = STATE_SENSING;

     if (DEBUG) Slog.d(TAG"Moved from STATE_IDLE_PENDING to STATE_SENSING.");

     EventLogTags.writeDeviceIdle(mState"step");

     scheduleSensingAlarmLocked(mConstants.SENSING_TIMEOUT);

     cancelLocatingLocked();

     mAnyMotionDetector.checkForAnyMotion();

     mNotMoving = false;

     mLocated = false;

     mLastGenericLocation = null;

     mLastGpsLocation = null;

当在STATE_IDLE_PENDING时,30分钟内没有检测到SignificantMotion(显著运动),就进入到下一个状态——STATE_SENSING,在这个状态下会重新设置该状态下发送alarm的时间SENSING_TIMEOUT(4min);并且不检测位置移动(因为位置移动下一个状态会去检查判断);检查设备是否处于一个静止状态;

并初始化几个参数。

 

4)LOCATIN

case STATE_SENSING:

    mState = STATE_LOCATING;

    if (DEBUG) Slog.d(TAG"Moved from STATE_SENSING to STATE_LOCATING.");

    EventLogTags.writeDeviceIdle(mState"step");

    scheduleSensingAlarmLocked(mConstants.LOCATING_TIMEOUT);

    mLocating = true;

    mLocationManager.requestLocationUpdates(mLocationRequestmGenericLocationListenermHandler.getLooper());

    if (mLocationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {

          mHaveGps = true;                mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,

                            mGpsLocationListenermHandler.getLooper());

    } else {

           mHaveGps = false;

           }

   break;

mAnyMotionDetector检测为静止时,进入到STATE_LOCATING状态,并设置发送Alarm的超时时间为LOCATING_TIMEOUT(30s),并且检测位置移动。

 

5)IDLE

case STATE_IDLE_MAINTENANCE:

       scheduleAlarmLocked(mNextIdleDelaytrue);

       if (DEBUG) Slog.d(TAG"Moved to STATE_IDLE. Next alarm in " + mNextIdleDelay + " ms.");

       mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);

       if (DEBUG) Slog.d(TAG"Setting mNextIdleDelay = " + mNextIdleDelay);

       mNextIdleDelay = Math.min(mNextIdleDelaymConstants.MAX_IDLE_TIMEOUT);

       mState = STATE_IDLE;

       EventLogTags.writeDeviceIdle(mState"step");

       mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);

       break;

当位置定位满足精确标准时,停掉Alarm检测,位置检测,设备移动检测等,直接进入Idle模式,此时重置Idle的发送超时时间为mNextIdleDelay(1h),并且通过Handler发送消息MSG_REPORT_IDLE_ON,在handleMessage中去做idle模式下的各种限制。

 

case STATE_IDLE:

       // We have been idling long enough, now it is time to do some work.

       scheduleAlarmLocked(mNextIdlePendingDelayfalse);

       if (DEBUG) Slog.d(TAG"Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE. " + "Next alarm in " + mNextIdlePendingDelay + " ms.");

       mNextIdlePendingDelay = Math.min(mConstants.MAX_IDLE_PENDING_TIMEOUT,

             (long)(mNextIdlePendingDelay * mConstants.IDLE_PENDING_FACTOR));

       mState = STATE_IDLE_MAINTENANCE;

       EventLogTags.writeDeviceIdle(mState"step");

       mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);

       break;

当进入Idle模式后,每隔一段时间都会进入到STATE_IDLE_MAINTENANCE;的间隙状态去处理相应的一些网络同步,Alarm发送的功能,具体操作在handleMessage中去做。



 

其整个流程大致如下:

系统启动时设置初始状态为STATE_ACTIVE。之后根据屏幕状态/充电状态/传感器状态来进行状态转移。

(1)当screenoff 且non-charging时,进入STATE_INACTIVE。此时会设置INACTIVE_TIMEOUT,默认为30min

(2)当INACTIVE_TIMEOUT时,进入STATE_IDLE_PENDING,此时会开启SignificantMotion,并设置IDLE_AFTER_INACTIVE_TIMEOUT,默认为30min.

(3)当IDLE_AFTER_INACTIVE_TIMEOUT时,进入STATE_SENSING,此时启动AnyMotionDetetor

(4)当AnyMoTionDetetor检测为静止时,进入STATE_LOCATING,并设置LOCATING_TIMEOUT,默认为30s,并请求当前的Location状态。

(5)当Location状态显示当前位置达到设定的精确标准时,直接进入STATE_IDLE,否则等到LOCATING_TIMEOUT,进入STATE_IDLE.此时停止AnyMotionDetetor,通过AlarmManager.setIdleUntil 通只AlarmManager进入IDLE,直到指定的alarm到来(这里设置的alarm默认为60min);通知PowerManager/NetworkPolicy/BatteryStas 进入IDLE状态。

并发送broadcast: ACTION_DEVICE_IDLE_MODE_CHANGED.

(6)当(5)中设置的alarm唤醒时,进入STATE_IDLE_MAINTAINTENCE,此时设置一个默认为5min的timeout,同时通知PowerManager/NetworkPolicy/BatteryStas退出IDLE状态

(7)当(6)设置的timeout 到时,再次进入STATE_IDLE

(8)在状态STATE_SENSING和STATE_LOCATING,可能会因为AnyMoTionDetetor监测到移动或者SignificantMotion检测到有动作而重新进入STATE_INACTIVE

(9)在状态STATE_IDLE_PENDING,可能会因为SignificantMotion检测到有动作而重新进入STATE_INACTIVE

(10)当screenon 或者 charging 都会重新进入STATE_ACTIVE

 

 

 

 

 

3. 状态变化的驱动因素

1screen 状态

2)充电状态(charging)

3AnyMonitionDetetor 监测到的状态。其作用区间为进入STATE_SENSING开始到离开STATE_LOCATING

4SignificantMotionSensor监测到的状态,其作用区间为进入STATE_IDLE_PENDING开始,到重新进入ACTIVE或者INACTIVE状态

5)外部模块直接调用exitIdle

6)使用dumpsys force-idle

7)控制IDLE功能的总开关:

config.xmlconfig_enableAutoPowerModes,默认为false

8)当检测到线控耳机按钮事件(voice-search)时,会退出idle模式。

 

 

 

4.进入Idle模式后的处理

case MSG_REPORT_IDLE_ON: {

       EventLogTags.writeDeviceIdleOnStart();

       mLocalPowerManager.setDeviceIdleMode(true);

       try {

             mNetworkPolicyManager.setDeviceIdleMode(true);

             mBatteryStats.noteDeviceIdleMode(truenull, Process.myUid());

           } catch (RemoteException e) {

     }

      getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);

      EventLogTags.writeDeviceIdleOnComplete();

  } break;

 

可以直观的看到进入Idle模式后,对PowerManager,NetworkPolicy,有直接限制,而进入到Idle模式后BatteryStats统计电池电量使用信息,将其当成与Alarm,Audio等模块一样去计算。

4.1对于PowerManager的限制

在服务启动阶段,onBootPhase()函数中,有:

mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);

将Idle模式白名单应用设置到PowerManagerService中,所以调用到PowerManagerService中时,去更新wakelock函数updateWakeLockDisabledStatesLocked():

 private void updateWakeLockDisabledStatesLocked() {

 boolean changed = false;

        final int numWakeLocks = mWakeLocks.size();

        for (int i = 0; i < numWakeLocksi++) {

            final WakeLock wakeLock = mWakeLocks.get(i);

            if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)

                    == PowerManager.PARTIAL_WAKE_LOCK) {

                if (setWakeLockDisabledStateLocked(wakeLock)) {

                    changed = true;

                    if (wakeLock.mDisabled) {

                        // This wake lock is no longer being respected.

                        notifyWakeLockReleasedLocked(wakeLock);

                    } else {

                        notifyWakeLockAcquiredLocked(wakeLock);

                    }

                }

            }

        }

        if (changed) {

            mDirty |= DIRTY_WAKE_LOCKS;

            updatePowerStateLocked();

        }

}

 

进入到Idle模式后,会遍历mWakeLocks列表,将该列表将不在白名单应用的PARTIAL_WAKE_LOCK类型的wakelock设置mDisabled参数。

函数setWakeLockDisabledStateLocked:

if (mDeviceIdleMode) {

     final int appid = UserHandle.getAppId(wakeLock.mOwnerUid);

     // If we are in idle mode, we will ignore all partial wake locks that are

     // for application uids that are not whitelisted.

         if (appid >= Process.FIRST_APPLICATION_UID &&

             Arrays.binarySearch(mDeviceIdleWhitelistappid) < 0 &&

             Arrays.binarySearch(mDeviceIdleTempWhitelistappid) < 0 &&

             mUidState.get(wakeLock.mOwnerUid,

                                ActivityManager.PROCESS_STATE_CACHED_EMPTY)

              > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {

                    disabled = true;

                }

            }

            if (wakeLock.mDisabled != disabled) {

                wakeLock.mDisabled = disabled;

                return true;

}

判断首先wakelock的appid需要大于Process.FIRST_APPLICATION_UID(10000:非系统应用Uid一般都是大于10000),appid不能在省电模式白名单以及零时白名单中,并且该uid的Service不能为一个前台服务。当满足这几个条件,该appid申请的wakelock会被disable掉,相当于release了,但是还在mWakelocks列表中,退出Idle状态时会重新生效。退出Idle时会重新走一遍setWakeLockDisabledStateLocked,将mWakeLocks列表中的所有wakelock的mDisbale参数置为false,所以退出Idle时,mWakeLocks列表中的所有wakelock会重新生效。

 

 

 

4.2对网络的限制

在NetworkPolicyManagerService中来控制idle状态下对网络访问的控制:

(1)监听ACTION_POWER_SAVE_WHITELIST_CHANGED来维护mPowerSaveWhitelistAppIds

(2)向DeviceIdleController注册mTempPowerSaveChangedCallback来维护 mPowerSaveTempWhitelistAppIds

(3)提供setDeviceIdleMode供DeviceIdleController 通知进入idle

 

当进入Idle模式后,便会通过NetworkPolicyManagerService通过updateRulesForGlobalChangeLocked全局去更新网络访问控制策略规则:

 void updateRulesForGlobalChangeLocked(boolean restrictedNetworksChanged) {

        final PackageManager pm = mContext.getPackageManager();

 

        updateRulesForDeviceIdleLocked();

        updateRulesForAppIdleLocked();

 

        // update rules for all installed applications

        final List<UserInfo> users = mUserManager.getUsers();

        final List<ApplicationInfo> apps = pm.getInstalledApplications(

                PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_DISABLED_COMPONENTS);

 

        for (UserInfo user : users) {

            for (ApplicationInfo app : apps) {

                final int uid = UserHandle.getUid(user.idapp.uid);

                updateRulesForUidLocked(uid);

            }

        }

 

        // limit data usage for some internal system services

        updateRulesForUidLocked(android.os.Process.MEDIA_UID);

        updateRulesForUidLocked(android.os.Process.DRM_UID);

 

}

(1)将mPowerSaveWhitelistAppIds,和mPowerSaveTempWhitelistAppIds中的app设置rule为FIREWALL_RULE_ALLOW(允许网络访问),加入到FIREWALL_CHAIN_DOZABLE这个chain中。

(2)向UsageStatesService注册AppIdleStateChangeListener,当UsageStatesService检测到有app standby时候,此时NetworkPolicyManagerService将会限制该app访问网络。

(6)当不在White list的APP 在进入IDLE状态时,将无法再使用网络。(通过添加使能firewall的相关chain来实现)

 

 

 

4.3 Alarm限制

AlarmManagerService增加了一套新的标志,用来区分不同alarm在idle模式下的工作状态。同时新增了3个api接口,用来设置可在idle模式下执行的alarm。具体情况如下:

public static final int FLAG_STANDALONE = 1<<0;

这种Alarm是独立的Alarm,不会和其他Alarm合并到一个batch中。

 

public static final int FLAG_WAKE_FROM_IDLE = 1<<1;

这种Alarm可以从idle模式下将设备唤醒,(用于闹钟,日程)

 

public static final int FLAG_ALLOW_WHILE_IDLE = 1<<2;

这种Alarm在Idle状态下依旧可以被发送执行,它不会使设备推出Idle,仅仅让Alarm运行,

    

public static final int FLAG_IDLE_UNTIL = 1<<4;

这种Alarm是标记一个时间点来退出idle状态。

 

AlarmManager中新增了三个方法:

setAndAllowWhileIdle:设置可在idle模式下执行的alarm(flag: FLAG_ALLOW_WHILE_IDLE)

setExactAndAllowWhileIdl:同setExact(),设置可以在idle模式下执行的精确Alarm

setIdleUntil:设置idle-until alarm,这种Alarm(flag:FLAG_IDLE_UNTIL)将会使设备退出idle,进入idle-mataintence状态,只能由系统服务调用,在6.0系统中暂时只有DeviceIdleController在Idle状态和Idle-Matainence状态切换时候调用,

 

在Alarm中alarm manager进入idle模式,会将先前设置的普通alarm(不带FLAG_ALLOW_WHILE_IDLE/ FLAG_WAKE_FROM_IDLE标志)放到PendingList,同时新来的普通alarm也会被存到PendingList。PendingList中的alarm不会被设置到底层alarm设备,因此也就不会有唤醒动作。当idle-until alarm的触发时间到来,PendingList才会重新设置。idle-until alarm的触发时间是一个可设置的参数,默认值是15分钟,也就是说如果设备在idle模式下,每15分钟唤醒一次。类似于15分钟的心跳同步。

 

 

以上是google原生代码上对于idle状态的控制逻辑,由于进入Idle状态后,会对系统的wakelock,network,Alarm作限制,以保证最大的省电效率。可以看出原生逻辑对于进入DeviceIdle状态是有着非常严格苛刻的条件的,从进入开始进入Idle到完全进入Idle状态中间需要走五个状态流程,且需要判断位置,移动以及明显动作,这些外界因素一个不满足都会使设备进入Idle状态失败。所以Idle状态基本只能在晚上睡眠时间才能完全进入。

猜你喜欢

转载自blog.csdn.net/esfly/article/details/80598574