上一篇文章:JobScheduler源码分析(三) Job从创建到执行
在前两篇文章中,对JobSchedulerService的启动和Job的调度过程大致做了个梳理,通过前几篇的分析我们知道,要使得客户端Job被JSS调度执行,必须满足该Job在创建时所设置的约束,而这些约束何时满足,这将由StateController
进行控制,本篇中将对所有StateController
类的控制流程进行分析。
通过前面文章的分析得,共有八个StateController
的子类,各自单独地记录每个Job的状态,并在Job准备好运行时通知JSS进行调度执行,或者在约束条件不满足时通知JSS停止Job的执行。所有的StateController类有如下共性:
StateController.maybeStartTrackingJobLocked()
方法开始记录跟踪此Job;StateController.maybeStopTrackingJobLocked()
方法停止此Job的记录;除了以上共同特性外,各个状态控制器中对约束的设置,大部分是通过广播实现的,状态控制器内部会注册一个广播,当广播接收器收到广播后,将根据得到的状态,通过setXXXConstraintSatisfied()
方法,对satisfiedConstraints
进行按位运算,比如在DeviceIdleJobsController中会调用setDeviceNotDozingConstraintSatisfied(true/false)
来设置是否Doze的约束条件满足。第三篇文章分析时说过:JobStatus中有一个全局变量satisfiedConstraints
,这个表示该Job当前满足的约束,如果当前状态控制器所检测到的系统状态满足某个约束,则按位或,否则清零即可。最终在判断Job约束条件是否满足时,将拿satisfiedConstraints
和requiredConstraints
进行比较,requiredConstraints
变量上记录的是创建Job时在JobInfo设置的所有约束条件,也是通过按位或运算进行标记的:
public boolean isConstraintsSatisfied() {
//该Job所有的约束条件
final int req = requiredConstraints & CONSTRAINTS_OF_INTEREST;
//该Job现在所满足的约束条件
int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
//相等,说明当前所满足约束已是所需所有约束
return (sat & req) == req;
}
下面我们就具体来看看,各个状态控制器如何获取并将结果设置给satisfiedConstraints
的,这部分逻辑比较清晰易懂,就不再啰哩啰嗦了,点到为止即可。
DeviceIdleJobsController用来控制Job对Doze的依赖条件,或者也可以说Doze对Job的限制,当设备进入Doze模式的IDLE状态时,将会限制除了Doze白名单外的所有应用的Job调度,当Doze退出IDLE状态进入维护状态后,将会对所有应用的Job解除限制。而DeviceIdleJobsController中则是通过广播的形式来感知Doze模式的状态变化,在其构造方法中可以看到广播的注册和监听:
public DeviceIdleJobsController(JobSchedulerService service) {
super(service);
// ......
final IntentFilter filter = new IntentFilter();
// Deep Doze状态发生改变后发送
filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
// Light Doze状态发生改变后发送
filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
// 白名单列表发生变化时发送
filter.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
// i临时白名单列表发生变化时发送
filter.addAction(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED);
mContext.registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, filter, null, null);
}
来看看关于广播接收器中的逻辑,其中只看当Doze的状态发生变化的部分:
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
// Deep Doze和Light Doze进入/退出IDLE后发送
case PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED:
case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
//根据Doze是否进入IDLE状态更新状态
updateIdleMode(mPowerManager != null && (mPowerManager.isDeviceIdleMode()
|| mPowerManager.isLightDeviceIdleMode()));
break;
}
}
};
以上逻辑中可以看到,不管是Deep Doze还是Light Doze,当进入或者退出IDLE后,执行的是同样的流程,这一方面也说明两种Doze对Job都是有延迟的。然后通过PowerManager获取到当前Doze的状态,并调用updateIdleMode()
更新,updateIdleMode()
方法中的处理主要如下:
updateTaskStateLocked()
方法更新Job的Doze约束;onDeviceIdleStateChanged()
回调进入JSS中。来看看updateTaskStateLocked()
:
private boolean updateTaskStateLocked(JobStatus task) {
//是否豁免Doze模式的限制:设置了标记且(对应app处于前台或app处于临时白名单列表中)
final boolean allowInIdle = ((task.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0)
&& (mForegroundUids.get(task.getSourceUid()) || isTempWhitelistedLocked(task));
//Job所属应用是否处于Doze白名单中
final boolean whitelisted = isWhitelistedLocked(task);
//Job是否允许调度:Doze模式不处于IDLE状态或应用处于白名单中或应用处于前台且带有FLAG_IMPORTANT_WHILE_FOREGROUND
final boolean enableTask = !mDeviceIdleMode || whitelisted || allowInIdle;
//设置Doze模式约束条件
return task.setDeviceNotDozingConstraintSatisfied(enableTask, whitelisted);
}
在以上方法中,判断是否这个Job可以在Doze下调度呢?并将结果通过setDeviceNotDozingConstraintSatisfied()
设置给JobStatus:
boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
dozeWhitelisted = whitelisted;
//该方法用来设置或这取消约束条件
return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state);
}
最终,通过setDeviceNotDozingConstraintSatisfied()
方法将CONSTRAINT_DEVICE_NOT_DOZING
标记设置给satisfiedConstraints
。
以上流程说明,当Doze模式不处于IDLE状态,或者Job所属应用处于白名单,或者Job所属应用被豁免,那么将设置为satisfiedConstraints
设置CONSTRAINT_DEVICE_NOT_DOZING
标记,表示此时已满足Doze的约束要求了,将不会被Doze模式所限制。在Job调度过程中,会看到关于他的判断:
//JobStatus.java:
public boolean isReady() {
//Doze模式是否处于非IDLE状态:不处于IDLE状态&&没有设置FLAG_WILL_BE_FOREGROUND标记
final boolean notDozing = (satisfiedConstraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0
|| (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
return (isConstraintsSatisfied() || deadlineSatisfied) && notDozing && notRestrictedInBg;
}
在JSS的回调方法onDeviceIdleStateChanged()
中,将会根据Doze状态的变化决定重新检查Job队列还是取消当前正在运行的Job。
IdleController用来控制设备闲置状态对Job的约束,仅仅对设置过setRequiresDeviceIdle(true)
的Job有效。
注意,这个闲置状态和Doze模式中的IDLE状态不是同一个概念,在IdleController中,会注册三类广播:亮灭屏广播、开始/结束屏保广播和无线充电广播,同时在配置文件config.xml中配置了一个阀值,规定当进入收到灭屏等广播后,到达这个阀值时,认定设备处于空闲状态。下面来看具体实现逻辑。
首先来看其构造方法:
public IdleController(JobSchedulerService service) {
super(service);
//初始化
initIdleStateTracking();
}
private void initIdleStateTracking() {
//处于闲置状态的阀值
mInactivityIdleThreshold = mContext.getResources().getInteger(
com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
//设置Alarm的窗口时间
mIdleWindowSlop = mContext.getResources().getInteger(
com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
//BroadcastReceiver子类
mIdleTracker = new IdlenessTracker();
mIdleTracker.startTracking();
}
在以上构造方法中,主要做了以下工作:
<integer name="config_jobSchedulerInactivityIdleThreshold">4260000integer>
<integer name="config_jobSchedulerIdleWindowSlop">300000integer>
startTracking()
中完成的: public void startTracking() {
IntentFilter filter = new IntentFilter();
// 屏幕状态广播
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
// 屏保状态广播
filter.addAction(Intent.ACTION_DREAMING_STARTED);
filter.addAction(Intent.ACTION_DREAMING_STOPPED);
// AMS发送的广播
filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
// 无线充电状态
filter.addAction(Intent.ACTION_DOCK_IDLE);
filter.addAction(Intent.ACTION_DOCK_ACTIVE);
mContext.registerReceiver(this, filter);
}
在广播接收器中的逻辑,将根据是否是限制状态来设置相关约束,并回调onControllerStateChanged()
进入JSS中,重新check Job队列。:
void reportNewIdleState(boolean isIdle) {
synchronized (mLock) {
for (int i = mTrackedTasks.size()-1; i >= 0; i--) {
mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(isIdle);
}
}
mStateChangedListener.onControllerStateChanged();
}
在setIdleConstraintSatisfied()
方法中:
boolean setIdleConstraintSatisfied(boolean state) {
//设置CONSTRAINT_IDLE标记
return setConstraintSatisfied(CONSTRAINT_IDLE, state);
}
在以上方法中,将根据当前是否处于闲置状态,对JobStatus中的全局变量satisfiedConstraints
设置CONSTRAINT_IDLE
标记。
TimeController是用来控制截止时间和延迟时间对Job的约束,仅对设置了setOverrideDeadline()
和setMinimumLatency()
的Job有效。
在TimeController中,会根据以上两个方法设置的延迟时间和截至时间,分别设置一个Alarm,当到达警报时间后,在AlarmListener.onAlarm()
方法中去更新,并在evaluateDeadlineConstraint()
中更新相关标记:
private boolean evaluateDeadlineConstraint(JobStatus job, long nowElapsedMillis) {
final long jobDeadline = job.getLatestRunTimeElapsed();
//到达截至时间
if (jobDeadline <= nowElapsedMillis) {
//到达延迟调度时间
if (job.hasTimingDelayConstraint()) {
job.setTimingDelayConstraintSatisfied(true);
}
job.setDeadlineConstraintSatisfied(true);
return true;
}
return false;
}
最终,会在setTimingDelayConstraintSatisfied()
中设置CONSTRAINT_TIMING_DELAY
标记给satisfiedConstraints
:
boolean setTimingDelayConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state);
}
会在setDeadlineConstraintSatisfied()
中设置CONSTRAINT_DEADLINE
标记给satisfiedConstraints
:
boolean setDeadlineConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_DEADLINE, state);
}
BatteryController用来控制充电状态和低电量模式对Job的约束,仅仅对设置过setRequiresBatteryNotLow(true)
或setRequiresCharging(true)
的Job有效。
充电状态和低电量模式这两个状态也是通过广播进行检测的,在其构造方法中,调用startTracking()
方法注册广播:
public void startTracking() {
IntentFilter filter = new IntentFilter();
// 低电量时发送
filter.addAction(Intent.ACTION_BATTERY_LOW);
// 退出低电量时发送
filter.addAction(Intent.ACTION_BATTERY_OKAY);
// 充电/放电时发送
filter.addAction(BatteryManager.ACTION_CHARGING);
filter.addAction(BatteryManager.ACTION_DISCHARGING);
mContext.registerReceiver(this, filter);
BatteryManagerInternal batteryManagerInternal =
LocalServices.getService(BatteryManagerInternal.class);
// 是否处于低电量模式
mBatteryHealthy = !batteryManagerInternal.getBatteryLevelLow();
// 是否在进行充电
mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
}
在其广播接收器中,调用maybeReportNewChargingStateLocked()
方法:
private void maybeReportNewChargingStateLocked() {
// 是否 充电且不处于低电量模式
final boolean stablePower = mChargeTracker.isOnStablePower();
// 是否 不处于低电量模式
final boolean batteryNotLow = mChargeTracker.isBatteryNotLow();
boolean reportChange = false;
// 遍历跟踪的Job列表
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
// 设置充电约束条件
boolean previous = ts.setChargingConstraintSatisfied(stablePower);
if (previous != stablePower) {
reportChange = true;
}
// 设置低电量约束条件
previous = ts.setBatteryNotLowConstraintSatisfied(batteryNotLow);
if (previous != batteryNotLow) {
reportChange = true;
}
}
if (stablePower || batteryNotLow) {
// 电源状态稳定后,立即调度执行Job
mStateChangedListener.onRunJobNow(null);
} else if (reportChange) {
// 状态发生变化,通知JSS
mStateChangedListener.onControllerStateChanged();
}
}
在以上方法中,将根据当前电池状态,分别调用JobStatus的setChargingConstraintSatisfied()
和setBatteryNotLowConstraintSatisfied()
来设置充电约束和低电量约束:
boolean setChargingConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_CHARGING, state);
}
boolean setBatteryNotLowConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, state);
}
最终,将标记值CONSTRAINT_CHARGING
和CONSTRAINT_BATTERY_NOT_LOW
设置给satisfiedConstraints
。
BackgroundJobsController用来控制Job后台的运行。由于AppStandby机制,当应用处于后台时,会进行一些功能的限制,以达到优化功耗的目的,BackgroundJobsController中正是对"当应用处于后台时限制它的Job"实现的地方。
和前几个控制器不同的是,BackgroundJobsController中监测某应用能否在后台运行Job,不是通过广播实现的,而是通过AppStateTracker和其内部接口AppStateTracker.Listener实现,而AppStateTracker是和AppStandby相关联的用来记录应用状态的类,当AppStandby中状态发生变化时,AppStateTracker.Listener的几个方法将被回调,从而更新各个应用的Job的后台约束:
//frameworks/base/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
private final Listener mForceAppStandbyListener = new Listener() {
@Override
public void updateAllJobs() {
synchronized (mLock) {
updateAllJobRestrictionsLocked();
}
}
@Override
public void updateJobsForUid(int uid, boolean isActive) {
synchronized (mLock) {
updateJobRestrictionsForUidLocked(uid, isActive);
}
}
@Override
public void updateJobsForUidPackage(int uid, String packageName, boolean isActive) {
synchronized (mLock) {
updateJobRestrictionsForUidLocked(uid, isActive);
}
}
};
}
而这几个方法中,最终都会调到updateSingleJobRestrictionLocked()
中,来看看这个方法:
boolean updateSingleJobRestrictionLocked(JobStatus jobStatus, int activeState) {
final int uid = jobStatus.getSourceUid();
final String packageName = jobStatus.getSourcePackageName();
// AppStandby是否对该Uid的Job有限制
final boolean canRun = !mAppStateTracker.areJobsRestricted(uid, packageName,
(jobStatus.getInternalFlags() & JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION)
!= 0);
final boolean isActive;
if (activeState == UNKNOWN) {
isActive = mAppStateTracker.isUidActive(uid);
} else {
isActive = (activeState == KNOWN_ACTIVE);
}
//设置后台约束
boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
//设置Uid是否处于活动状态
didChange |= jobStatus.setUidActive(isActive);
return didChange;
}
最终,将会在setBackgroundNotRestrictedConstraintSatisfied()
设置标记CONSTRAINT_BACKGROUND_NOT_RESTRICTED
给satisfiedConstraints
:
boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state);
}
ConnectivityController用来控制网络对Job的约束,仅仅对设置过setRequiredNetwork()
或setRequiredNetworkType()
的Job有效。
ConnectivityController中通过接口回调的方式获取新的网络状态:
public ConnectivityController(JobSchedulerService service) {
super(service);
// ConnectivityService提供数据连接管理服务
// NetworkPolicyManagerService提供网络策略管理服务
mConnManager = mContext.getSystemService(ConnectivityManager.class);
mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
// 注册回调接口
final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
mConnManager.registerNetworkCallback(request, mNetworkCallback);
mNetPolicyManager.registerListener(mNetPolicyListener);
}
如此一来,当网络状态发生变化后,将回调方法从而通知ConnectivityController,关于何时触发毁掉,我不清楚,这里直接来看它回调后的内容,最终会进入updateConstraintsSatisfied()
中:
private boolean updateConstraintsSatisfied(JobStatus jobStatus, Network network,
NetworkCapabilities capabilities) {
// ......
final boolean changed = jobStatus
.setConnectivityConstraintSatisfied(connected && satisfied);
jobStatus.network = network;
return changed;
}
关于这部分内容,我也是不知道啊,留着以后补充吧。总之,最后通过setConnectivityConstraintSatisfied()
设置标记CONSTRAINT_CONNECTIVITY
给satisfiedConstraints
:
boolean setConnectivityConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
}
StorageController用来控制存储空间对Job的约束,仅仅对设置过setRequiresStorageNotLow(true)
的Job有效。其内部也是通过广播的形式获取设备是否处于低存储状态:
public void startTracking() {
IntentFilter filter = new IntentFilter();
// 存储空间不足时发送
filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
// 存储空间正常时发送
filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
mContext.registerReceiver(this, filter);
}
在广播接收器onReceive()
方法中,将通过具体广播,得到表示当前是否处于低存储的bool值mStorageLow
,然后调用maybeReportNewStorageState()
方法来更新Job的存储约束:
private void maybeReportNewStorageState() {
// 是否处于非低存储空间状态
final boolean storageNotLow = mStorageTracker.isStorageNotLow();
boolean reportChange = false;
synchronized (mLock) {
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
// 更新Job约束
boolean previous = ts.setStorageNotLowConstraintSatisfied(storageNotLow);
if (previous != storageNotLow) {
reportChange = true;
}
}
}
if (reportChange) {
// 存储空间发生变化
mStateChangedListener.onControllerStateChanged();
}
if (storageNotLow) {
// 当又低存储恢复正常存储状态后,立即运行Job
mStateChangedListener.onRunJobNow(null);
}
}
最终,在setStorageNotLowConstraintSatisfied()
中设置标记CONSTRAINT_STORAGE_NOT_LOW
给satisfiedConstraints
:
boolean setStorageNotLowConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, state);
}
ContentObserverController用来监测content URIS对Job的约束,仅仅对设置过addTriggerContentUri(Uri)
的Job有效,当该URI发生变化后,将运行Job。这个暂时还不了解,就不分析了。