勿扰模式详细分析

之前也写过勿扰模式的大概分析文章勿扰模式代码结构简析

不过只是大概讲了下相关文件的位置和作用,具体只分析了勿扰模式对来电铃声影响的代码。在做具体需求的时候发现勿扰模式远比我想象的复杂,本文记录下几个开发中遇到的勿扰模式相关知识。

系统通知拦截的实现

勿扰模式控制的就是通知的行为,发送通知的时候控制通知铃声是否响,通知的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
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);
        }
    }






评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值