引言

Android手机越来越多的向着用户体验提升方面靠近,那么Zenmode就会变得越来越重要。

近年来,也有很多的新功能依赖于ZenMode去实现,也有很多专利在这个方面申请成功。

举两个简单的例子:

  1. 在国内的话,我们是否可以根据日历行程和AI分析,在勿扰模式下进行行程的提醒…
  2. 在国外的话,CMAS报警会很频繁,那么勿扰模式下的CellBroadCast就成为了不同运营商的需求。

本文将会专注于ZenMode的实现,并分析这些内容。


代码结构

1. 设置相关的界面及代码路径:

packages/apps/Settings/src/com/android/settings/notification/zen/

AbstractZenCustomRulePreferenceController.java         ZenModeButtonPreferenceController.java                 ZenModeSliceBuilder.java
AbstractZenModeAutomaticRulePreferenceController.java  ZenModeBypassingAppsPreferenceController.java          ZenModeSoundVibrationPreferenceController.java
AbstractZenModePreferenceController.java               ZenModeBypassingAppsSettings.java                      ZenModeSoundVibrationSettings.java
SettingsZenDurationDialog.java                         ZenModeCallsPreferenceController.java                  ZenModeSystemPreferenceController.java
ZenAccessSettings.java                                 ZenModeCallsSettings.java                              ZenModeVisEffectPreferenceController.java
ZenAutomaticRuleHeaderPreferenceController.java        ZenModeConversationsImagePreferenceController.java     ZenModeVisEffectsAllPreferenceController.java
ZenAutomaticRuleSwitchPreferenceController.java        ZenModeConversationsPreferenceController.java          ZenModeVisEffectsCustomPreferenceController.java
ZenCustomRadioButtonPreference.java                    ZenModeConversationsSettings.java                      ZenModeVisEffectsNonePreferenceController.java
ZenCustomRuleBlockedEffectsSettings.java               ZenModeDurationPreferenceController.java               ZenModeVoiceActivity.java
ZenCustomRuleCallsSettings.java                        ZenModeEventRuleSettings.java                          ZenOnboardingActivity.java
ZenCustomRuleConfigSettings.java                       ZenModeEventsPreferenceController.java                 ZenRuleButtonsPreferenceController.java
ZenCustomRuleMessagesSettings.java                     ZenModeMediaPreferenceController.java                  ZenRuleCallsPreferenceController.java
ZenCustomRuleNotificationsSettings.java                ZenModeMessagesPreferenceController.java               ZenRuleCustomPolicyPreferenceController.java
ZenCustomRuleSettingsBase.java                         ZenModeMessagesSettings.java                           ZenRuleCustomSwitchPreferenceController.java
ZenCustomRuleSettings.java                             ZenModePeoplePreferenceController.java                 ZenRuleDefaultPolicyPreferenceController.java
ZenDeleteRuleDialog.java                               ZenModePeopleSettings.java                             ZenRuleInfo.java
ZenDurationDialogPreference.java                       ZenModePreferenceController.java                       ZenRuleMessagesPreferenceController.java
ZenFooterPreferenceController.java                     ZenModePriorityConversationsPreferenceController.java  ZenRuleNameDialog.java
ZenModeAddAutomaticRulePreferenceController.java       ZenModePrioritySendersPreferenceController.java        ZenRuleNotifFooterPreferenceController.java
ZenModeAddBypassingAppsPreferenceController.java       ZenModeRemindersPreferenceController.java              ZenRulePreference.java
ZenModeAlarmsPreferenceController.java                 ZenModeRepeatCallersPreferenceController.java          ZenRuleRepeatCallersPreferenceController.java
ZenModeAllBypassingAppsPreferenceController.java       ZenModeRestrictNotificationsSettings.java              ZenRuleSelectionDialog.java
ZenModeAutomaticRulesPreferenceController.java         ZenModeRuleSettingsBase.java                           ZenRuleStarredContactsPreferenceController.java
ZenModeAutomationPreferenceController.java             ZenModeScheduleDaysSelection.java                      ZenRuleVisEffectPreferenceController.java
ZenModeAutomationSettings.java                         ZenModeScheduleRuleSettings.java                       ZenRuleVisEffectsAllPreferenceController.java
ZenModeBackend.java                                    ZenModeSendersImagePreferenceController.java           ZenRuleVisEffectsCustomPreferenceController.java
ZenModeBehaviorFooterPreferenceController.java         ZenModeSettingsBase.java                               ZenRuleVisEffectsNonePreferenceController.java
ZenModeBlockedEffectsPreferenceController.java         ZenModeSettingsFooterPreferenceController.java         ZenSuggestionActivity.java
ZenModeBlockedEffectsSettings.java                     ZenModeSettings.java

2. framework notification相关实现

a. 接口类的实现

frameworks/base/core/java/android/app

INotificationManager.aidl
ITransientNotification.aidl
ITransientNotificationCallback.aidl
Notification.aidl
NotificationChannel.aidl
NotificationChannelGroup.aidl
NotificationChannelGroup.java
NotificationChannel.java
NotificationHistory.aidl
NotificationHistory.java
Notification.java
NotificationManager.aidl
NotificationManager.java
b. 具体的实现

frameworks/base/core/java/android/service/notification

Adjustment.aidl                ConversationChannelWrapper.aidl  IStatusBarNotificationHolder.aidl  NotificationStats.aidl  ScheduleCalendar.java       ZenModeConfig.aidl
Adjustment.java                ConversationChannelWrapper.java  NotificationAssistantService.java  NotificationStats.java  SnoozeCriterion.aidl        ZenModeConfig.java
Condition.aidl                 IConditionListener.aidl          NotificationListenerService.java   NotifyingApp.aidl       SnoozeCriterion.java        ZenPolicy.java
Condition.java                 IConditionProvider.aidl          NotificationRankingUpdate.aidl     NotifyingApp.java       StatusBarNotification.aidl
ConditionProviderService.java  INotificationListener.aidl       NotificationRankingUpdate.java     OWNERS                  StatusBarNotification.java
c. ZenMode的配置实现

frameworks/base/core/java/android/service/notification/

frameworks/base/core/java/android/service/notification$ ls |grep Zen
ZenModeConfig.aidl
ZenModeConfig.java
ZenPolicy.javaframeworks/base/services/core/java/com/android/server/notification$ ls |grep Zen
ZenLog.java
ZenModeConditions.java
ZenModeExtractor.java
ZenModeFiltering.java
ZenModeHelper.java

实现逻辑

设置ZenMode调用逻辑分析

当点击启用勿扰模式时,会在settings里面首先改变状态。

packages/apps/Settings/src/com/android/settings/notification/zen/ZenModeButtonPreferenceController.java

    @Overridepublic void updateState(Preference preference) {super.updateState(preference);if (null == mZenButtonOn) {mZenButtonOn = ((LayoutPreference) preference).findViewById(R.id.zen_mode_settings_turn_on_button);updateZenButtonOnClickListener(preference);}if (null == mZenButtonOff) {mZenButtonOff = ((LayoutPreference) preference).findViewById(R.id.zen_mode_settings_turn_off_button);mZenButtonOff.setOnClickListener(v -> {mRefocusButton = true;writeMetrics(preference, false);mBackend.setZenMode(Settings.Global.ZEN_MODE_OFF);});}updatePreference(preference);}

这里的实现相对来说比较简单,设置开和关的button,然后如果打开的情况下那么就会去通过setZenMode向下设置。

    private void updateZenButtonOnClickListener() {int zenDuration = getZenDuration();switch (zenDuration) {case Settings.Secure.ZEN_DURATION_PROMPT:mZenButtonOn.setOnClickListener(v -> {mMetricsFeatureProvider.action(mContext,SettingsEnums.ACTION_ZEN_TOGGLE_DND_BUTTON, false);new SettingsEnableZenModeDialog().show(mFragment, TAG);});break;case Settings.Secure.ZEN_DURATION_FOREVER:mZenButtonOn.setOnClickListener(v -> {mMetricsFeatureProvider.action(mContext,SettingsEnums.ACTION_ZEN_TOGGLE_DND_BUTTON, false);mBackend.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);});break;default:mZenButtonOn.setOnClickListener(v -> {mMetricsFeatureProvider.action(mContext,SettingsEnums.ACTION_ZEN_TOGGLE_DND_BUTTON, false);mBackend.setZenModeForDuration(zenDuration);});}}

当点击了TurnOn的时候,将会进入到下面的逻辑:

            case Settings.Secure.ZEN_DURATION_FOREVER:mZenButtonOn.setOnClickListener(v -> {mMetricsFeatureProvider.action(mContext,SettingsEnums.ACTION_ZEN_TOGGLE_DND_BUTTON, false);mBackend.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);});break;

设置setZenMode为ZEN_MODE_IMPORTANT_INTERRUPTIONS。

    protected void setZenMode(int zenMode) {NotificationManager.from(mContext).setZenMode(zenMode, null, TAG);mZenMode = getZenMode();}

这里会去调用NotificationManager去设置zenmode.

    /*** @hide*/@UnsupportedAppUsagepublic void setZenMode(int mode, Uri conditionId, String reason) {INotificationManager service = getService();try {service.setZenMode(mode, conditionId, reason);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

INotificationManager是一个AIDL的调用, 将会去调用NotificationManagerService去进行设置。

    /*** @hide*/@UnsupportedAppUsagepublic void setZenMode(int mode, Uri conditionId, String reason) {INotificationManager service = getService();try {service.setZenMode(mode, conditionId, reason);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

NotificationManagerServices的实现如下:

@Overridepublic void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {enforceSystemOrSystemUI("INotificationManager.setZenMode");final long identity = Binder.clearCallingIdentity();try {mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);} finally {Binder.restoreCallingIdentity(identity);}}

setManualZenMode的实现已经在ZenModeHelper里面了,具体实现如下:
实现的路径为:frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java

    public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) {setManualZenMode(zenMode, conditionId, reason, caller, true /*setRingerMode*/);Settings.Secure.putInt(mContext.getContentResolver(),Settings.Secure.SHOW_ZEN_SETTINGS_SUGGESTION, 0);}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, null, setRingerMode);}}

在进行了一系列参数保存和传递后,将会在最后setConfig。

    public boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,String reason) {return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/);}

这个只是个封装,真正的实现是一个private函数:

    private boolean setConfigLocked(ZenModeConfig config, String reason,ComponentName triggeringComponent, boolean setRingerMode) {final long identity = Binder.clearCallingIdentity();try {if (config == null || !config.isValid()) {Log.w(TAG, "Invalid config in setConfigLocked; " + config);return false;}if (config.user != mUser) {// simply store away for background usersmConfigs.put(config.user, config);if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);return true;}// handle CPS backed conditions - danger! may modify configmConditions.evaluateConfig(config, null, false /*processSubscriptions*/);mConfigs.put(config.user, config);if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());ZenLog.traceConfig(reason, mConfig, config);// send some broadcastsfinal boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),getNotificationPolicy(config));if (!config.equals(mConfig)) {dispatchOnConfigChanged();updateConsolidatedPolicy(reason);}if (policyChanged) {dispatchOnPolicyChanged();}mConfig = config;mHandler.postApplyConfig(config, reason, triggeringComponent, setRingerMode);return true;} catch (SecurityException e) {Log.wtf(TAG, "Invalid rule in config", e);return false;} finally {Binder.restoreCallingIdentity(identity);}}

在这边如果当config相比于之前有改变的话,最后会发消息通知。
mHandler.postApplyConfig(config, reason, triggeringComponent, setRingerMode);

        private void postApplyConfig(ZenModeConfig config, String reason,ComponentName triggeringComponent, boolean setRingerMode) {sendMessage(obtainMessage(MSG_APPLY_CONFIG,new ConfigMessageData(config, reason, triggeringComponent, setRingerMode)));}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_DISPATCH:dispatchOnZenModeChanged();break;case MSG_METRICS:mMetrics.emit();break;case MSG_APPLY_CONFIG:ConfigMessageData applyConfigData = (ConfigMessageData) msg.obj;applyConfig(applyConfigData.config, applyConfigData.reason,applyConfigData.triggeringComponent, applyConfigData.setRingerMode);}}}

在消息的最后,会发送MSG_APPLY_CONFIG来作为message的主体,在下方的handleMessage中,会applyConfig。

    private void applyConfig(ZenModeConfig config, String reason,ComponentName triggeringComponent, boolean setRingerMode) {final String val = Integer.toString(config.hashCode());Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);evaluateZenMode(reason, setRingerMode);mConditions.evaluateConfig(config, triggeringComponent, true /*processSubscriptions*/);}

evaluateZenMode的实现如下:

    @VisibleForTestingprotected void evaluateZenMode(String reason, boolean setRingerMode) {if (DEBUG) Log.d(TAG, "evaluateZenMode");if (mConfig == null) return;final int policyHashBefore = mConsolidatedPolicy == null ? 0: mConsolidatedPolicy.hashCode();final int zenBefore = mZenMode;final int zen = computeZenMode();ZenLog.traceSetZenMode(zen, reason);mZenMode = zen;setZenModeSetting(mZenMode);updateConsolidatedPolicy(reason);updateRingerModeAffectedStreams();if (setRingerMode && (zen != zenBefore || (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS&& policyHashBefore != mConsolidatedPolicy.hashCode()))) {applyZenToRingerMode();}applyRestrictions();if (zen != zenBefore) {mHandler.postDispatchOnZenModeChanged();}}

然后会去调用applyRestrictions去进行设置。

    @VisibleForTestingprotected void applyRestrictions(boolean zenPriorityOnly, boolean mute, int usage, int code) {final long ident = Binder.clearCallingIdentity();try {mAppOps.setRestriction(code, usage,mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,zenPriorityOnly ? mPriorityOnlyDndExemptPackages : null);} finally {Binder.restoreCallingIdentity(ident);}}

如何适配运营商需求

在某些国家,会有运营商的要求为在设置了DND模式后,要求依然会有震动和响铃。
那么如何实现的呢?
首先在broadcast里面,会有一个KEY_OVERRIDE_DND的定义:

    // Play alert sound in full volume regardless Do Not Disturb is on.public static final String KEY_OVERRIDE_DND = "override_dnd";

然后初始化:

        // retrieve whether to play alert sound in full volume regardless Do Not Disturb is on.mOverrideDnd = intent.getBooleanExtra(ALERT_AUDIO_OVERRIDE_DND_EXTRA, false);

当在静音模式的时候,会进入如下判断:

            case AudioManager.RINGER_MODE_SILENT:if (DBG) log("Ringer mode: silent");if (!mOverrideDnd) {mEnableVibrate = false;}// If the phone is in silent mode, we only enable the audio when override dnd// setting is turned on.mEnableAudio = mOverrideDnd;break;

当两个参数为true的时候,变进行响铃的操作。

if (mEnableAudio || mEnableVibrate) {playAlertTone(mAlertType, mVibrationPattern);} else {if (DBG) log("No audio/vibrate playing. Stop CellBroadcastAlertAudio service");stopSelf();return START_NOT_STICKY;}

那么哪些运营商会重写这个perference为true呢?
我们反编译这个apk,去查看不同mcc,mnc的values配置即可

比如:  values-mcc310-mnc160/

Android Zenmode/DND(勿扰模式) 实现原理剖析相关推荐

  1. Android系统设置之勿扰模式

    项目场景: 智能车载机 问题描述: 车载机使用4G流量,但客户接入的是带通话功能的sim卡,客户测试过程中遇到有人拨号,来电音量不是静音情况下,居然通了.客户提出禁止来电功能. 原因分析: 尽管系统进 ...

  2. android 勿扰模式代码,勿扰模式代码结构简析

    勿扰模式是Android 7.0开始加入的功能.它的核心思想是屏蔽了通知的铃声.振动和展示. 代码分散在几部分. 1.设置代码在Settings中,ZenMode开头的一系列文件 /packages/ ...

  3. android 勿扰模式代码,android 勿扰模式代码结构简析

    勿扰模式是Android 7.0开始加入的功能.它的核心思想是屏蔽了通知的铃声.振动和展示. 代码分散在几部分. 1.设置代码在Settings中,ZenMode开头的一系列文件 /packages/ ...

  4. android 勿扰模式代码结构简析

    勿扰模式代码结构简析 标签: 勿扰模式 2017-08-08 11:05  60人阅读  评论(0)  收藏  举报   分类: android(59)  版权声明:本文为博主原创文章,未经博主允许不 ...

  5. android 勿扰模式代码,android Lollipop勿扰模式

    android的L新版本中增加了"打扰"的新功能,相信很多同学搞不明白.找了一篇介绍勿扰模式很好的文章,可惜是英文的,现翻译如下,相信读完此问,你会理解android对勿扰模式的设 ...

  6. android 勿扰模式代码,Android N Zen Mode (勿扰模式)设置流程

    Android N去除了情景模式,取而代之的是勿扰模式.勿扰模式的入口有两处,下拉栏和设置声音里面.下面我们就从设置声音入口,看看勿扰模式的设置流程. 首先,勿扰模式的首页有三种选项,分别是仅允许优先 ...

  7. android Lollipop勿扰模式

    android的L新版本中增加了"打扰"的新功能,相信很多同学搞不明白.找了一篇介绍勿扰模式很好的文章,可惜是英文的,现翻译如下,相信读完此问,你会理解android对勿扰模式的设 ...

  8. 勿扰模式代码结构简析

    勿扰模式是Android 7.0开始加入的功能.它的核心思想是屏蔽了通知的铃声.振动和展示. 代码分散在几部分. 1.设置代码在Settings中,ZenMode开头的一系列文件 /packages/ ...

  9. RK3568平台开发系列讲解(安卓篇)勿扰模式系统流程

    文章目录 一.勿扰模式 一.勿扰模式 它的核心思想是屏蔽了通知的铃声.振动和展示. 代码分散在几部分. 1.设置代码在Settings中,ZenMode开头的一系列文件 /packages/apps/ ...

最新文章

  1. JAVA怎么创建对象组_java – 如何根据特定字段创建一组有序的对象?
  2. web安全编程——权限的分配和控制
  3. Redis详解(三)
  4. Android中scrollview与webview冲突事件
  5. 【Web API系列教程】3.3 — 实战:处理数据(建立数据库)
  6. Python常见设计模式
  7. R7-5 求矩阵各行元素之和 (15 分)
  8. 数据库-使用DataReader的简单实例(两种办法)
  9. 【转载】一步步构建大型网站架构
  10. tensorflow.python.framework.tensor_shape.is_fully_defined()
  11. 大事化小、小事化了的动态规划
  12. 快速地将SolidWorks模型导入Adams
  13. 知识图谱构建技术一览
  14. 职场篇(一):明哥的职场礼仪七堂课笔记
  15. 《python初级爬虫》(一)
  16. [004]Python数据类型二_python_全栈基础
  17. 阿里视频直播自定义推拉流地址生成
  18. Vue学习7-MinUI组件与项目托管到码云上
  19. Win10快捷键,管多
  20. JavaScript学习二

热门文章

  1. 深信服 行为感知系统 c.php 远程命令执行漏洞
  2. 关于Macbook Pro/Air 键盘输入乱码,重置NVRAM无效
  3. 浙大oj(basic level)1001
  4. 梦回JDBC —— (Statement对象)
  5. RecyclerView实现竖向无限循环滚动的列表
  6. Paperreading之五  Stacked Hourglass Networks(SHN)和源码阅读(PyTorch版本)
  7. 做了五年Android,我顿悟了...
  8. 前端炫酷登录页,拿来就能用
  9. 电视盒子显示服务器未连接,你家的电视盒子直播总是卡,解决方法全都在这里...
  10. OSChina 周六乱弹 ——泡妞指南