之前也写过勿扰模式的大概分析文章勿扰模式代码结构简析
不过只是大概讲了下相关文件的位置和作用,具体只分析了勿扰模式对来电铃声影响的代码。在做具体需求的时候发现勿扰模式远比我想象的复杂,本文记录下几个开发中遇到的勿扰模式相关知识。
系统通知拦截的实现
勿扰模式控制的就是通知的行为,发送通知的时候控制通知铃声是否响,通知的UI是否展现等等。入口的方法就在发送通知的时候
frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
final int callingPid, final String tag, final int id, final Notification notification,
int[] idOut, int incomingUserId) {
...
final NotificationRecord r = mNotificationList.get(i);
...
mHandler.post(new Runnable() {
@Override
public void run() {
synchronized (mNotificationList) {
...
applyZenModeLocked(r);
...
}
}
});
...
}
通知的发送流程可以网上搜索。applyZenModeLocked就是依据通知信息确定通知在勿扰模式下行为。
private void applyZenModeLocked(NotificationRecord record) {
record.setIntercepted(mZenModeHelper.shouldIntercept(record));
}
frameworks/base/services/core/java/com/android/server/notification/NotificationRecord.java
public boolean setIntercepted(boolean intercept) {
mIntercept = intercept;
return mIntercept;
}
其中setIntercepted非常简单,就是设置一个成员变量表示该通知是否要拦截,拦截的通知不会响铃
那么勿扰模式判断的核心方法就是shouldIntercept
frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java
public boolean shouldIntercept(NotificationRecord record) {
return mFiltering.shouldIntercept(mZenMode, mConfig, record);
}
frameworks/base/services/core/java/com/android/server/notification/ZenModeFiltering.java
public boolean shouldIntercept(int zen, ZenModeConfig config, NotificationRecord record) {
if (isSystem(record)) { //勿扰拦截模式不控制系统通知
return false;
}
switch (zen) {
case Global.ZEN_MODE_NO_INTERRUPTIONS: //不拦截
// #notevenalarms
ZenLog.traceIntercepted(record, "none");
return true;
case Global.ZEN_MODE_ALARMS: //控制alarm,如闹钟
if (isAlarm(record)) {
// Alarms only
return false;
}
ZenLog.traceIntercepted(record, "alarmsOnly");
return true;
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: //优先勿扰模式,特定联系人不拦截
if (isAlarm(record)) { //优先勿扰模式不拦截闹钟
// Alarms are always priority
return false;
}
// allow user-prioritized packages through in priority mode
if (record.getPackagePriority() == Notification.PRIORITY_MAX) {//最高优先级通知不拦截
ZenLog.traceNotIntercepted(record, "priorityApp");
return false;
}
if (isCall(record)) { //通话通知
if (config.allowRepeatCallers
&& REPEAT_CALLERS.isRepeat(mContext, extras(record))) {
ZenLog.traceNotIntercepted(record, "repeatCaller");
return false;
} //短时间重复来电不拦截,这个在勿扰模式设置中可以开启或者关闭
if (!config.allowCalls) {
ZenLog.traceIntercepted(record, "!allowCalls");
return true;
}
return shouldInterceptAudience(config.allowCallsFrom, record);
}
if (isMessage(record)) { //短信通知
if (!config.allowMessages) {
ZenLog.traceIntercepted(record, "!allowMessages");
return true;
}
return shouldInterceptAudience(config.allowMessagesFrom, record);
}
if (isEvent(record)) { //日程通知
if (!config.allowEvents) {
ZenLog.traceIntercepted(record, "!allowEvents");
return true;
}
return false;
}
if (isReminder(record)) { //提醒通知
if (!config.allowReminders) {
ZenLog.traceIntercepted(record, "!allowReminders");
return true;
}
return false;
}
ZenLog.traceIntercepted(record, "!priority");
return true;
default:
return false;
}
}
shouldIntercept依据勿扰模式设置进行处理,这里已通话通知为例,这里shouldInterceptAudience方法在之前的文章已经讲过,就是依据联系人的设置确认是否拦截。
Android系统默认有“来自联系人”,“来自收藏联系人”等过滤选项,例如设置中选择“来自联系人”,那么优先勿扰模式下来自联系人的来电不会拦截,但是陌生号码来电不会响铃和显示UI。
勿扰模式的手动开关和自动规则
勿扰模式有手动开关和自动规则两种方式,我一开始偏执的认为勿扰模式设置中的开关是个总开关,自动规则要在总开关开启后才有效。不过实际验证和阅读代码后才知晓。手动开关和自动规则是互斥的关系,手动开关开启后自动规则无效,手动开关关闭后自动规则才有效,当然如果全部的自动规则都关闭或者规则数为0勿扰模式就完全无效了。
手动开启入口代码如下:
frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java
setManualZenMode
private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller,
boolean setRingerMode) {
ZenModeConfig newConfig;
synchronized (mConfig) {
if (mConfig == null) return;
if (!Global.isValidZenMode(zenMode)) return;
if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode)
+ " conditionId=" + conditionId + " reason=" + reason
+ " setRingerMode=" + setRingerMode);
newConfig = mConfig.copy();
if (zenMode == Global.ZEN_MODE_OFF) {
newConfig.manualRule = null;
for (ZenRule automaticRule : newConfig.automaticRules.values()) {
if (automaticRule.isAutomaticActive()) {
automaticRule.snoozing = true;
}
}
} else {
final ZenRule newRule = new ZenRule();
newRule.enabled = true;
newRule.zenMode = zenMode;
newRule.conditionId = conditionId;
newRule.enabler = caller;
newConfig.manualRule = newRule;
}
setConfigLocked(newConfig, reason, setRingerMode);
}
}
可以看出手动开启就是生成了一条ZenRule,赋值给了manualRule。与之对应的自动规则成员定义如下
public ZenRule manualRule;
public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
自动规则是个容器,而手动规则只有一条。
setConfigLocked
frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java
private boolean setConfigLocked(ZenModeConfig config, String reason, boolean setRingerMode) {
final long identity = Binder.clearCallingIdentity();
try {
...
mConditions.evaluateConfig(config, false /*processSubscriptions*/); // may modify config
mConfigs.put(config.user, config);
...
final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
getNotificationPolicy(config));
if (!config.equals(mConfig)) {
dispatchOnConfigChanged();
}
if (policyChanged) {
dispatchOnPolicyChanged();
}
mConfig = config;
mHandler.postApplyConfig(config, reason, setRingerMode);
return true;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private void applyConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
if (!evaluateZenMode(reason, setRingerMode)) {
applyRestrictions(); // evaluateZenMode will also apply restrictions if changed
}
mConditions.evaluateConfig(config, true /*processSubscriptions*/);
}
其中evaluateConfig是自动规则起作用的核心方法,后面讲自动规则的时候详细分析,mConfigs保存ZenModeConfig数据,这段代码要关注的核心方法是evaluateZenMode,该方法依据各种设置更新勿扰相关变量
evaluateZenMode
private boolean evaluateZenMode(String reason, boolean setRingerMode) {
if (DEBUG) Log.d(TAG, "evaluateZenMode");
final ArraySet<ZenRule> automaticRules = new ArraySet<ZenRule>();
final int zen = computeZenMode(automaticRules); //确定当前勿扰模式
if (zen == mZenMode) return false;
ZenLog.traceSetZenMode(zen, reason);
mZenMode = zen; //mZenMode是最根本的设置,例如不拦截,拦截所有,优先勿扰等等
updateRingerModeAffectedStreams(); //更新音频系统设置
setZenModeSetting(mZenMode); //保存到SystemProvider
if (setRingerMode) {
applyZenToRingerMode(); //设置铃声模式
}
applyRestrictions();
mHandler.postDispatchOnZenModeChanged(); //通知观察者勿扰模式变化
return true;
}
mZenMode的值有四个,见
frameworks/base/core/java/android/provider/Settings.java
/** @hide */ public static final int ZEN_MODE_OFF = 0; //勿扰模式关闭
/** @hide */ public static final int ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1; //优先勿扰模式
/** @hide */ public static final int ZEN_MODE_NO_INTERRUPTIONS = 2; //不拦截
/** @hide */ public static final int ZEN_MODE_ALARMS = 3; //闹铃勿扰模式
其中第三个值和第一个值的区别是,第三个值表示自动规则在当前时间不拦截。而第一个是勿扰模式完全不起作用
computeZenMode
computeZenMode又是evaluateZenMode方法的核心,代码如下:
private int computeZenMode(ArraySet<ZenRule> automaticRulesOut) {
if (mConfig == null) return Global.ZEN_MODE_OFF;
if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
int zen = Global.ZEN_MODE_OFF;
for (ZenRule automaticRule : mConfig.automaticRules.values()) {
if (automaticRule.isAutomaticActive()) {
if (zenSeverity(automaticRule.zenMode) > zenSeverity(zen)) {
zen = automaticRule.zenMode;
}
}
}
return zen;
}
从代码可看出,如果mConfig中的manualRule不为空的话,则直接返回manualRule.zenMode。manualRule为空则依据当前启用的自动规则获取勿扰模式设置值
isAutomaticActive
frameworks/base/core/java/android/service/notification/ZenModeConfig.java
public boolean isAutomaticActive() {
return enabled && !snoozing && component != null && isTrueOrUnknown();
}
自动规则判断有四个条件:
1.该规则是否启用,就是勿扰模式每条自动规则设置中最上面的switch,表示该规则开启或者关闭
2.snooze英文意思是打瞌睡,它值为true的话自动规则不起作用,查看代码,它在用户手动关闭勿扰模式的时候设置为true,在ZenModeConfig有变化或者condition有变化的时候把它设置为false
frameworks/base/services/core/java/com/android/server/notification/ZenModeConditions.java
private boolean updateSnoozing(ZenRule rule) {
if (rule != null && rule.snoozing && (mFirstEvaluation || !rule.isTrueOrUnknown())) {
rule.snoozing = false;
if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId);
return true;
}
return false;
}
代码有些不好理解,我的理解是如果自动规则生效时间包含手动关闭勿扰模式的时间点,那么用户手动关闭勿扰模式同时使对应生效的自动规则也暂时失效,在下次的时间范围内自动规则又会恢复。所以起名为snoozing,正在打盹,打盹总不会很久。
3.规则的component不能为空,该值是在创建规则时传入的,例如ZenModeConfig中的
public static ComponentName getScheduleConditionProvider() {
return new ComponentName(SYSTEM_AUTHORITY, "ScheduleConditionProvider");
}
4.condition的状态要符合要求,状态在ConditionProvider中设置。是判断当前时间勿扰模式是否启用,例如规则时间段是22:00~7:00,那么在23:00勿扰模式起作用,在8:00勿扰模式就不起作用。
总结
这里可以看出手动和自动规则其实都是ZenRule,不过手动只有一条,而自动规则可以有多条。从computeZenMode方法看到手动规则是优先自动规则的。
自动规则详解
ZenRule
定义于frameworks/base/core/java/android/service/notification/ZenModeConfig.java
public static class ZenRule implements Parcelable {
public boolean enabled;
public boolean snoozing; // user manually disabled this instance
public String name; // required for automatic
public int zenMode;
public Uri conditionId; // required for automatic
public Condition condition; // optional
public ComponentName component; // optional
public String id; // required for automatic (unique)
public long creationTime; // required for automatic
public String enabler; // package name, only used for manual rules.
...
}
enabled 规则是否手动启用
snoozing 规则是否暂时不可用,具体含义见上面小节,表示用户手动关闭勿扰模式时附带的暂时关闭某些自动规则
name 规则名称
zenMode 规则模式
conditionId 和 condition,conditionId定义了规则,例如
public static Uri toScheduleConditionId(ScheduleInfo schedule) {
return new Uri.Builder().scheme(Condition.SCHEME)
.authority(SYSTEM_AUTHORITY)
.appendPath(SCHEDULE_PATH)
.appendQueryParameter("days", toDayList(schedule.days))
.appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute)
.appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute)
.appendQueryParameter("exitAtAlarm", String.valueOf(schedule.exitAtAlarm))
.build();
}
勿扰模式原生代码中的条件就是指时间,当然这个可以扩展,例如可以写一个游戏中勿扰模式开启的Condition。condition是对conditionId的包装,它最主要的作用是表明当前规则是否有用。
component是ConditionProvider的名字
id 唯一表ZenRule的数字
creationTime 创建时间
enableer 启用手动规则的package名称,三方app可以设定勿扰模式规则
ConditionProviderService
frameworks/base/core/java/android/service/notification/ConditionProviderService.java
public abstract class ConditionProviderService extends Service {
...
private final H mHandler = new H();
private Provider mProvider;
private INotificationManager mNoMan;
...
//三个接口,服务调用方可触发
abstract public void onConnected();
abstract public void onSubscribe(Uri conditionId);
abstract public void onUnsubscribe(Uri conditionId);
//通知condition变化给NotificationManagerService
public final void notifyCondition(Condition condition) {
if (condition == null) return;
notifyConditions(new Condition[]{ condition });
}
public final void notifyConditions(Condition... conditions) {
if (!isBound() || conditions == null) return;
try {
getNotificationInterface().notifyConditions(getPackageName(), mProvider, conditions);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
}
@Override
public IBinder onBind(Intent intent) {
if (mProvider == null) {
mProvider = new Provider();
}
return mProvider;
}
private final class Provider extends IConditionProvider.Stub {
...
}
}
frameworks/base/services/core/java/com/android/server/notification/SystemConditionProviderService.java
该类继承ConditionProviderService并加入了一些接口,而继承SystemConditionProviderService的类有三个
frameworks/base/services/core/java/com/android/server/notification/CountdownConditionProvider.java
frameworks/base/services/core/java/com/android/server/notification/EventConditionProvider.java
frameworks/base/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
frameworks/base/services/core/java/com/android/server/notification/EventConditionProvider.java
frameworks/base/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
CountdownConditionProvider是倒计时条件,例如倒计时两个小时内勿扰模式启用;EventConditionProvider对应日程条件,如某日15:00~16:00开会,期间勿扰模式开启;ScheduleConditionProvider对应周期性的时间段勿扰模式开启,如每周一~周五的晚上22:00~次日早7:00勿扰模式开启。
名字起的有点儿误导,叫做Provider,但其实是Service,它们负责了真正的ZenRule规则的执行。
ConditionProviderService的创建
SystemConditionProviderService的三个实例初始化都在ZenModeConditions中
frameworks/base/services/core/java/com/android/server/notification/ZenModeConditions.java
public ZenModeConditions(ZenModeHelper helper, ConditionProviders conditionProviders) {
mHelper = helper;
mConditionProviders = conditionProviders;
if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.COUNTDOWN_PATH)) {
mConditionProviders.addSystemProvider(new CountdownConditionProvider());
}
if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.SCHEDULE_PATH)) {
mConditionProviders.addSystemProvider(new ScheduleConditionProvider());
}
if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.EVENT_PATH)) {
mConditionProviders.addSystemProvider(new EventConditionProvider());
}
mConditionProviders.setCallback(this);
}
mConditionProviders的类型是ConditionProviders
frameworks/base/services/core/java/com/android/server/notification/ConditionProviders.java
public void addSystemProvider(SystemConditionProviderService service) {
mSystemConditionProviders.add(service);
service.attachBase(mContext);
registerService(service.asInterface(), service.getComponent(), UserHandle.USER_SYSTEM);
}
mSystemConditionProviders是个容器,registerService使用了父类ManagedServices的方法
frameworks/base/services/core/java/com/android/server/notification/ManagedServices.java
ManagedServices和名字一样,作用就是管理service
public void registerService(IInterface service, ComponentName component, int userid) {
checkNotNull(service);
ManagedServiceInfo info = registerServiceImpl(service, component, userid);
if (info != null) {
onServiceAdded(info);
}
}
最终一路调用到
private ManagedServiceInfo registerServiceImpl(ManagedServiceInfo info) {
synchronized (mMutex) {
try {
info.service.asBinder().linkToDeath(info, 0);
mServices.add(info);
return info;
} catch (RemoteException e) {
// already dead
}
}
return null;
}
这里有个很平常不大一样的地方,平常绑定服务是使用bindService,使用ServiceConnection的回调获取binder对象。而代码中也的确有bindService的代码
private void rebindServices(boolean forceRebind)
private void registerService(final ComponentName name, final int userid) {
synchronized (mMutex) {
registerServiceLocked(name, userid);
}
}
public void registerSystemService(final ComponentName name, final int userid) {
synchronized (mMutex) {
registerServiceLocked(name, userid, true /* isSystem */);
}
}
private void registerServiceLocked(final ComponentName name, final int userid,
final boolean isSystem) {
...
if (!mContext.bindServiceAsUser(intent,
serviceConnection,
BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT,
new UserHandle(userid))) {
mServicesBinding.remove(servicesBindingTag);
Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
return;
}
...
}
只有registerServiceLocked会使用bindServiceAsUser方法,而调用路径只有两条:
1.rebindServices,例如SettingsProvider变化,包安装移除和User切换的时候会重新绑定服务
2.registerSystemService这个public方法是供三方app调用的
也就是说registerService整个过程并没有通过正常的途径绑定服务。
其实秘密在参数传递过程中service.asInterface(),SystemConditionProviderService中定义了抽象方法
abstract public IConditionProvider asInterface();
子类例如ScheduleConditionProvider
@Override
public IConditionProvider asInterface() {
return (IConditionProvider) onBind(null);
}
@Override
public IBinder onBind(Intent intent) {
if (mProvider == null) {
mProvider = new Provider();
}
return mProvider;
}
这里的Provider是ConditionProviderService的内部类,
private final class Provider extends IConditionProvider.Stub {
@Override
public void onConnected() {
mHandler.obtainMessage(H.ON_CONNECTED).sendToTarget();
}
@Override
public void onSubscribe(Uri conditionId) { //这是通知conditions变化的起点
mHandler.obtainMessage(H.ON_SUBSCRIBE, conditionId).sendToTarget();
}
@Override
public void onUnsubscribe(Uri conditionId) {
mHandler.obtainMessage(H.ON_UNSUBSCRIBE, conditionId).sendToTarget();
}
}
可见直接传递的就是一个服务的实现,那么registerService方法一开始就获取了服务接口(因为本来都在一个进程内,代码都在一个包下)。registerService最终干的工作是用linkToDeath添加服务终止的处理代码,和字面意思有点偏差。
ZenModeConditions是ZenModeHelper的成员变量
public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
...
mConditions = new ZenModeConditions(this, conditionProviders);
...
}
而ConditionProviders是作为参数传进来的,代码在NotificationManagerService
public void onStart() {
...
mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
}
ConditionProviderService的使用
还记得前面讲过的“evaluateConfig是自动规则起作用的核心方法“”吗?不记得可以搜下本文章哦。
frameworks/base/services/core/java/com/android/server/notification/ZenModeConditions.java
public void evaluateConfig(ZenModeConfig config, boolean processSubscriptions) {
if (config == null) return;
if (config.manualRule != null && config.manualRule.condition != null
&& !config.manualRule.isTrueOrUnknown()) {
if (DEBUG) Log.d(TAG, "evaluateConfig: clearing manual rule");
config.manualRule = null; //会清除不在使用的手动规则,不然自动规则永远无法启用
}
final ArraySet<Uri> current = new ArraySet<>();
//先分析手动规则,在再分析自动规则
evaluateRule(config.manualRule, current, processSubscriptions);
for (ZenRule automaticRule : config.automaticRules.values()) {
evaluateRule(automaticRule, current, processSubscriptions);
//更新自动规则的打盹状态,是否要重新启用自动规则
updateSnoozing(automaticRule);
}
final int N = mSubscriptions.size();
for (int i = N - 1; i >= 0; i--) {
final Uri id = mSubscriptions.keyAt(i);
final ComponentName component = mSubscriptions.valueAt(i);
if (processSubscriptions) {
if (!current.contains(id)) {
//移除无用的condition
mConditionProviders.unsubscribeIfNecessary(component, id);
mSubscriptions.removeAt(i);
}
}
}
mFirstEvaluation = false;
}
evaluateRule是核心的方法
private void evaluateRule(ZenRule rule, ArraySet<Uri> current, boolean processSubscriptions) {
if (rule == null || rule.conditionId == null) return;
final Uri id = rule.conditionId;
boolean isSystemCondition = false;
for (SystemConditionProviderService sp : mConditionProviders.getSystemProviders()) {
if (sp.isValidConditionId(id)) {
mConditionProviders.ensureRecordExists(sp.getComponent(), id, sp.asInterface());
//ensureRecordExists实际是生成了ConditionRecord
rule.component = sp.getComponent();
isSystemCondition = true;
}
}
...
if (processSubscriptions) {
//condition生效的代码
if (mConditionProviders.subscribeIfNecessary(rule.component, rule.conditionId)) {
mSubscriptions.put(rule.conditionId, rule.component);
} else {
rule.condition = null;
if (DEBUG) Log.d(TAG, "zmc failed to subscribe");
}
}
...
}
接下来跳转到ConditionProviders.java中
public boolean subscribeIfNecessary(ComponentName component, Uri conditionId) {
synchronized (mMutex) {
final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
...
subscribeLocked(r);
return r.subscribed;
}
}
private void subscribeLocked(ConditionRecord r) {
if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
final IConditionProvider provider = provider(r);
...
provider.onSubscribe(r.id);
...
}
onSubscribe最终跳转到SystemConditionProviderService的onSubscribe方法中,下面以常用的ScheduleConditionProvider为例
public void onSubscribe(Uri conditionId) {
...
evaluateSubscriptions();
}
private void evaluateSubscriptions() {
if (mAlarmManager == null) {
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
}
final long now = System.currentTimeMillis();
synchronized (mSubscriptions) {
setRegistered(!mSubscriptions.isEmpty());
mNextAlarmTime = 0;
long nextUserAlarmTime = getNextAlarm();
for (Uri conditionId : mSubscriptions.keySet()) {
final ScheduleCalendar cal = mSubscriptions.get(conditionId);
if (cal != null && cal.isInSchedule(now)) { //如果在规则时间范围内
if (conditionSnoozed(conditionId) || cal.shouldExitForAlarm(now)) {
notifyCondition(conditionId, Condition.STATE_FALSE, "alarmCanceled");
addSnoozed(conditionId);
} else {
//通知condition变化
notifyCondition(conditionId, Condition.STATE_TRUE, "meetsSchedule");
}
cal.maybeSetNextAlarm(now, nextUserAlarmTime);
} else {
notifyCondition(conditionId, Condition.STATE_FALSE, "!meetsSchedule");
removeSnoozed(conditionId);
if (nextUserAlarmTime == 0) {
cal.maybeSetNextAlarm(now, nextUserAlarmTime);
} else {
notifyCondition(conditionId, Condition.STATE_FALSE, "!meetsSchedule");
if ((cal != null) && (nextUserAlarmTime == 0)) {
cal.maybeSetNextAlarm(now, nextUserAlarmTime);
}
}
if (cal != null) {
final long nextChangeTime = cal.getNextChangeTime(now);
if (nextChangeTime > 0 && nextChangeTime > now) {
if (mNextAlarmTime == 0 || nextChangeTime < mNextAlarmTime) {
mNextAlarmTime = nextChangeTime;
}
}
}
}
}
}
updateAlarm(now, mNextAlarmTime);
}
可见除了核心的通知condtion变化外大部分代码都和Alarm有关,那么alarm到底做了什么事情?
private void updateAlarm(long now, long time) {
final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
REQUEST_CODE_EVALUATE,
new Intent(ACTION_EVALUATE)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
.putExtra(EXTRA_TIME, time),
PendingIntent.FLAG_UPDATE_CURRENT);
alarms.cancel(pendingIntent);
if (time > now) {
if (DEBUG) Slog.d(TAG, String.format("Scheduling evaluate for %s, in %s, now=%s",
ts(time), formatDuration(time - now), ts(now)));
alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
} else {
if (DEBUG) Slog.d(TAG, "Not scheduling evaluate");
}
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Slog.d(TAG, "onReceive " + intent.getAction());
synchronized (mSubscriptions) {
if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
...
}
}
evaluateSubscriptions();
}
};
可见alarm只不过是调用evaluateSubscriptions,可见规则时间段对勿扰模式的影响是通过alarm来进行的。
后续notifyConditions消息传递流程
frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
ConditionProviderService中的notifyConditions调用的实际是NotificationManagerService的对应方法
public void notifyConditions(final String pkg, IConditionProvider provider,
final Condition[] conditions) {
final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
checkCallerIsSystemOrSameApp(pkg);
mHandler.post(new Runnable() {
@Override
public void run() {
mConditionProviders.notifyConditions(pkg, info, conditions);
}
});
}
frameworks/base/services/core/java/com/android/server/notification/ConditionProviders.java
public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
...
final int N = conditions.length;
for (int i = 0; i < N; i++) {
final Condition c = conditions[i];
if (mCallback != null) {
mCallback.onConditionChanged(c.id, c);
}
}
}
android/frameworks/base/services/core/java/com/android/server/notification/ZenModeConditions.java
@Override
public void onConditionChanged(Uri id, Condition condition) {
if (DEBUG) Log.d(TAG, "onConditionChanged " + id + " " + condition);
ZenModeConfig config = mHelper.getConfig();
if (config == null) return;
//更新Condition
boolean updated = updateCondition(id, condition, config.manualRule);
for (ZenRule automaticRule : config.automaticRules.values()) {
updated |= updateCondition(id, condition, automaticRule);
updated |= updateSnoozing(automaticRule);
}
if (updated) {
//更新设置
mHelper.setConfig(config, "conditionChanged");
}
}
最后的mHelper就是ZenModeHelper,它的后续流程和手动设置一样的
public void setConfig(ZenModeConfig config, String reason) {
synchronized (mConfig) {
setConfigLocked(config, reason);
}
}
至此ConditionProviderService的上报流程完毕,勿扰模式通过alram不断的更新ZenModeConfig值。
规则的保存
还有个小问题就是规则是如何存储的,因为不可能只在内存中有,不然systemUI进程死了规则就没有了。
NotificationManagerService.java
private void handleSavePolicyFile() {
if (DBG) Slog.d(TAG, "handleSavePolicyFile");
synchronized (mPolicyFile) {
final FileOutputStream stream;
try {
stream = mPolicyFile.startWrite();
} catch (IOException e) {
Slog.w(TAG, "Failed to save policy file", e);
return;
}
try {
writePolicyXml(stream, false /*forBackup*/);
mPolicyFile.finishWrite(stream);
} catch (IOException e) {
Slog.w(TAG, "Failed to save policy file, restoring backup", e);
mPolicyFile.failWrite(stream);
}
}
BackupManager.dataChanged(getContext().getPackageName());
}
private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
final XmlSerializer out = new FastXmlSerializer();
out.setOutput(stream, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
out.startTag(null, TAG_NOTIFICATION_POLICY);
out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
mZenModeHelper.writeXml(out, forBackup);
mRankingHelper.writeXml(out, forBackup);
out.endTag(null, TAG_NOTIFICATION_POLICY);
out.endDocument();
}
其中
final File systemDir = new File(Environment.getDataDirectory(), "system");
mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
可以看出规则保存在/data/system/notification_policy.xml中
ZenModeHelper.java
public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
final int N = mConfigs.size();
for (int i = 0; i < N; i++) {
//TODO: http://b/22388012
if (forBackup && mConfigs.keyAt(i) != UserHandle.USER_SYSTEM) {
continue;
}
mConfigs.valueAt(i).writeXml(out);
}
}
ZenModeConfig
public void writeXml(XmlSerializer out) throws IOException {
out.startTag(null, ZEN_TAG);
out.attribute(null, ZEN_ATT_VERSION, Integer.toString(XML_VERSION));
out.attribute(null, ZEN_ATT_USER, Integer.toString(user));
out.startTag(null, ALLOW_TAG);
out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers));
out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders));
out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents));
out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom));
out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
out.attribute(null, ALLOW_ATT_SCREEN_OFF, Boolean.toString(allowWhenScreenOff));
out.attribute(null, ALLOW_ATT_SCREEN_ON, Boolean.toString(allowWhenScreenOn));
out.endTag(null, ALLOW_TAG);
if (manualRule != null) {
out.startTag(null, MANUAL_TAG);
writeRuleXml(manualRule, out);
out.endTag(null, MANUAL_TAG);
}
final int N = automaticRules.size();
for (int i = 0; i < N; i++) {
final String id = automaticRules.keyAt(i);
final ZenRule automaticRule = automaticRules.valueAt(i);
out.startTag(null, AUTOMATIC_TAG);
out.attribute(null, RULE_ATT_ID, id);
writeRuleXml(automaticRule, out);
out.endTag(null, AUTOMATIC_TAG);
}
out.endTag(null, ZEN_TAG);
}
最终会调用writeRuleXml 方法
public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException {
out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled));
out.attribute(null, RULE_ATT_SNOOZING, Boolean.toString(rule.snoozing));
if (rule.name != null) {
out.attribute(null, RULE_ATT_NAME, rule.name);
}
out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode));
if (rule.component != null) {
out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString());
}
if (rule.conditionId != null) {
out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
}
out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime));
if (rule.enabler != null) {
out.attribute(null, RULE_ATT_ENABLER, rule.enabler);
}
if (rule.condition != null) {
writeConditionXml(rule.condition, out);
}
}