勿扰模式详细分析
来源:互联网 发布: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
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
- 勿扰模式详细分析
- 详细跨境电商模式分析
- 动态代理模式(详细分析)
- 设计模式之工厂模式 c++实现和详细分析
- 设计模式之装饰模式 c++实现和详细分析
- 386保护模式中段选择子的详细的分析
- java中的23中设计模式详细分析
- 单例模式详细分析&7种实现方式
- gem5中O3模式下fetch_impl.hh源代码详细分析
- 设计模式之模板方法模式 c++实现和详细分析
- 设计模式之建造者模式 c++实现和详细分析
- FFMpeg分析详细分析
- FFMpeg分析详细分析
- FFMpeg分析详细分析
- 【Java】单例模式的5种形式(详细分析)
- SDWebImage分析--源代码详细分析
- dump分析超级详细分析
- Fragroute原理详细分析
- shell脚本命令 运行python文件&python命令行运行python代码
- 凤凰牌老熊对支付的系统讲解
- MVC表单提交提示:已经加入含有相同索引键的项目
- Unity3d使用UGUI实现长按功能
- QAbstractItemModel,QAbstractTableModel
- 勿扰模式详细分析
- 获取一个数二进制序列中所有的偶数位和奇数位,分别输出二进制序列
- Spark性能优化之道——解决Spark数据倾斜(Data Skew)的N种姿势
- [转]nexus3.3.1上传第三方jar包
- FastDFS连接池实现
- 最大数maxnumber BZOJ
- Linux下SVN源码编译安装配置
- sql
- EPG 在 CMMI 中的缩写含义 EPG (Engineering Process Group) 在“能力成熟度模型集成”中,是“过程改进小组”的缩写. 是指决策层面的LEADER组成的委