勿扰模式详细分析

来源:互联网 发布:139端口入侵教程 编辑:程序博客网 时间:2024/05/18 03:38

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

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

系统通知拦截的实现

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






阅读全文
0 0
原创粉丝点击