勿扰模式代码结构简析

标签: 勿扰模式
2017-08-08 11:05  60人阅读  评论(0)  收藏  举报
  分类:
android(59) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

目录(?)[+]

勿扰模式是Android 7.0开始加入的功能。它的核心思想是屏蔽了通知的铃声、振动和展示。

代码分散在几部分。

1.设置代码在Settings中,ZenMode开头的一系列文件

/packages/apps/Settings/src/com/android/settings/notification/ZenModeSettings.java
/packages/apps/Settings/src/com/android/settings/notification/ZenModeSettingsBase.java
/packages/apps/Settings/src/com/android/settings/notification/ZenModeVoiceActivity.java
/packages/apps/Settings/src/com/android/settings/notification/ZenModePrioritySettings.java
/packages/apps/Settings/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
/packages/apps/Settings/src/com/android/settings/notification/ZenModeEventRuleSettings.java
/packages/apps/Settings/src/com/android/settings/notification/ZenModeAutomationSettings.java
/packages/apps/Settings/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
/packages/apps/Settings/src/com/android/settings/notification/ZenModeScheduleDaysSelection.java
/packages/apps/Settings/src/com/android/settings/notification/ZenModeVisualInterruptionSettings.java

2.framework中关于勿扰的文件

2.1 勿扰模式设置,可以在进程间传递

/frameworks/base/core/java/android/service/notification/ZenModeConfig.aidl
/frameworks/base/core/java/android/service/notification/ZenModeConfig.java

2.2 勿扰模式使用相关文件

/frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java
/frameworks/base/services/core/java/com/android/server/notification/ZenModeFiltering.java
/frameworks/base/services/core/java/com/android/server/notification/ZenModeConditions.java

2.3 systemUI中,有勿扰模式UI和功能两个部分构成。因为最终的目的是处理通知,所以实现是在systemUI中。

/frameworks/base/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
/frameworks/base/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java

3.各个apk对勿扰模式的处理,以来电铃声为例

/packages/services/Telecomm/src/com/android/server/telecom/Ringer.java

[java]  view plain copy
  1. private boolean shouldRingForContact(Uri contactUri) {
  2. final NotificationManager manager =
  3. (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
  4. final Bundle extras = new Bundle();
  5. if (contactUri != null) {
  6. extras.putStringArray(Notification.EXTRA_PEOPLE, new String[] {contactUri.toString()});
  7. }
  8. return manager.matchesCallFilter(extras);
  9. }

使用matchesCallFilter来决定是否响铃。

4.matchesCallFilter流程分析

frameworks/base/core/java/android/app/NotificationManager.java

[java]  view plain copy
  1. public boolean matchesCallFilter(Bundle extras) {
  2. INotificationManager service = getService();
  3. try {
  4. return service.matchesCallFilter(extras);
  5. } catch (RemoteException e) {
  6. throw e.rethrowFromSystemServer();
  7. }
  8. }

frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

[java]  view plain copy
  1. public boolean matchesCallFilter(Bundle extras) {
  2. enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
  3. return mZenModeHelper.matchesCallFilter(
  4. Binder.getCallingUserHandle(),
  5. extras,
  6. mRankingHelper.findExtractor(ValidateNotificationPeople.class),
  7. MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
  8. MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
  9. }

android/frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java

[java]  view plain copy
  1. public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
  2. ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) {
  3. synchronized (mConfig) {
  4. return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConfig, userHandle,
  5. extras, validator, contactsTimeoutMs, timeoutAffinity);
  6. }
  7. }

android/frameworks/base/services/core/java/com/android/server/notification/ZenModeFiltering.java

[java]  view plain copy
  1. public static boolean matchesCallFilter(Context context, int zen, ZenModeConfig config,
  2. UserHandle userHandle, Bundle extras, ValidateNotificationPeople validator,
  3. int contactsTimeoutMs, float timeoutAffinity) {
  4. if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through //勿扰模式判断
  5. if (zen == Global.ZEN_MODE_ALARMS) return false; // not an alarm
  6. if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
  7. if (config.allowRepeatCallers && REPEAT_CALLERS.isRepeat(context, extras)) {  //短时间内第二次来电放行
  8. return true;
  9. }
  10. if (!config.allowCalls) return false; // no other calls get through //是否禁止全部来电
  11. if (validator != null) {
  12. final float contactAffinity = validator.getContactAffinity(userHandle, extras,
  13. contactsTimeoutMs, timeoutAffinity);
  14. return audienceMatches(config.allowCallsFrom, contactAffinity); //依据联系人判断
  15. }
  16. }
  17. return true;
  18. }

先来看返回结果调用的方法:

[java]  view plain copy
  1. private static boolean audienceMatches(int source, float contactAffinity) {
  2. switch (source) {
  3. case ZenModeConfig.SOURCE_ANYONE: //所有人
  4. return true;
  5. case ZenModeConfig.SOURCE_CONTACT:
  6. return contactAffinity >= ValidateNotificationPeople.VALID_CONTACT; //联系人
  7. case ZenModeConfig.SOURCE_STAR:
  8. return contactAffinity >= ValidateNotificationPeople.STARRED_CONTACT; //星标联系人
  9. default:
  10. Slog.w(TAG, "Encountered unknown source: " + source);
  11. return true;
  12. }
  13. }

很简单,就是依据配置走不同的分支。接下来继续看核心的getContactAffinity方法
frameworks/base/services/core/java/com/android/server/notification/ValidateNotificationPeople.java

[java]  view plain copy
  1. public float getContactAffinity(UserHandle userHandle, Bundle extras, int timeoutMs,
  2. float timeoutAffinity) {
  3. ...
  4. final PeopleRankingReconsideration prr = validatePeople(context, key, extras, affinityOut); //从缓存中读取值
  5. float affinity = affinityOut[0];
  6. if (prr != null) {
  7. // Perform the heavy work on a background thread so we can abort when we hit the
  8. // timeout.
  9. final Semaphore s = new Semaphore(0);
  10. AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
  11. @Override
  12. public void run() {
  13. prr.work(); //开线程查询数据库
  14. s.release();
  15. }
  16. });
  17. try {
  18. if (!s.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
  19. Slog.w(TAG, "Timeout while waiting for affinity: " + key + ". "
  20. + "Returning timeoutAffinity=" + timeoutAffinity);
  21. return timeoutAffinity; //超时后直接返回
  22. }
  23. } catch (InterruptedException e) {
  24. Slog.w(TAG, "InterruptedException while waiting for affinity: " + key + ". "
  25. + "Returning affinity=" + affinity, e);
  26. return affinity; //线程被中断后返回缓存结果
  27. }
  28. affinity = Math.max(prr.getContactAffinity(), affinity); //正常情况下返回的结果
  29. }
  30. return affinity;
  31. }

线程工作方法work如下:

[java]  view plain copy
  1. public void work() {
  2. long start = SystemClock.elapsedRealtime();
  3. if (VERBOSE) Slog.i(TAG, "Executing: validation for: " + mKey);
  4. long timeStartMs = System.currentTimeMillis();
  5. for (final String handle: mPendingLookups) {
  6. LookupResult lookupResult = null;
  7. final Uri uri = Uri.parse(handle);
  8. if ("tel".equals(uri.getScheme())) {   //处理电话号码类型的uri
  9. if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
  10. lookupResult = resolvePhoneContact(mContext, uri.getSchemeSpecificPart());
  11. } else if ("mailto".equals(uri.getScheme())) { //处理电子邮件类型的uri
  12. if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
  13. lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());
  14. } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) { //处理联系人lookup_uri
  15. if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
  16. lookupResult = searchContacts(mContext, uri);
  17. } else { //非法的uri,生成默认的结果
  18. lookupResult = new LookupResult();  // invalid person for the cache
  19. Slog.w(TAG, "unsupported URI " + handle);
  20. }
  21. if (lookupResult != null) {
  22. synchronized (mPeopleCache) {
  23. final String cacheKey = getCacheKey(mContext.getUserId(), handle);
  24. mPeopleCache.put(cacheKey, lookupResult); //查询存储到缓存
  25. }
  26. if (DEBUG) Slog.d(TAG, "lookup contactAffinity is " + lookupResult.getAffinity());
  27. mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());  //保存结果值
  28. } else {
  29. if (DEBUG) Slog.d(TAG, "lookupResult is null");
  30. }
  31. }
  32. ...
  33. }

以电话号码分支为例:

[java]  view plain copy
  1. private LookupResult resolvePhoneContact(Context context, final String number) {
  2. Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
  3. Uri.encode(number));
  4. return searchContacts(context, phoneUri);
  5. }
[java]  view plain copy
  1. private LookupResult searchContacts(Context context, Uri lookupUri) {
  2. LookupResult lookupResult = new LookupResult();
  3. Cursor c = null;
  4. try {
  5. c = context.getContentResolver().query(lookupUri, LOOKUP_PROJECTION, null, null, null);
  6. if (c == null) {
  7. Slog.w(TAG, "Null cursor from contacts query.");
  8. return lookupResult;
  9. }
  10. while (c.moveToNext()) {
  11. lookupResult.mergeContact(c);
  12. }
  13. } catch (Throwable t) {
  14. Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
  15. } finally {
  16. if (c != null) {
  17. c.close();
  18. }
  19. }
  20. return lookupResult;
  21. }

上述两个方法就是查询联系人数据库,其中生成结果的方法mergeContact如下:

[java]  view plain copy
  1. public void mergeContact(Cursor cursor) {
  2. mAffinity = Math.max(mAffinity, VALID_CONTACT);
  3. // Contact ID
  4. int id;
  5. final int idIdx = cursor.getColumnIndex(Contacts._ID);
  6. if (idIdx >= 0) {
  7. id = cursor.getInt(idIdx);
  8. if (DEBUG) Slog.d(TAG, "contact _ID is: " + id);
  9. } else {
  10. id = -1;
  11. Slog.i(TAG, "invalid cursor: no _ID");
  12. }
  13. // Starred
  14. final int starIdx = cursor.getColumnIndex(Contacts.STARRED);
  15. if (starIdx >= 0) {
  16. boolean isStarred = cursor.getInt(starIdx) != 0;
  17. if (isStarred) {
  18. mAffinity = Math.max(mAffinity, STARRED_CONTACT);
  19. }
  20. if (DEBUG) Slog.d(TAG, "contact STARRED is: " + isStarred);
  21. } else {
  22. if (DEBUG) Slog.d(TAG, "invalid cursor: no STARRED");
  23. }
  24. }

实际上就是给mAffinity赋值,标记为普通联系人或者星标联系人。

如果要加入指定联系人在勿扰模式中优先,从流程分析看只要修改searchContacts方法就可以了。

android 勿扰模式代码结构简析相关推荐

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

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

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

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

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

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

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

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

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

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

  6. android 勿扰模式代码,android机勿扰模式代码是什么

    手机格式化代码!!!!!!!!!! BEGBEGIN:IMELODY VERSION:1.2 FORMAT:CLASS1.0 COMPOSER:MIK(23)Fomat BEAT:180 MELODY ...

  7. android 勿扰代码,Android7.1勿扰功能简析

    Android系统在5.1系统开始增加勿扰模式,渐渐的有着取代静音模式的趋势,最新的系统已经更新到7.1.1,我们来看一下最新的原生勿扰有哪些功能. 首先在屏幕下滑出来的快捷开关界面中,我们可以看到勿 ...

  8. Android Zenmode/DND(勿扰模式) 实现原理剖析

    引言 Android手机越来越多的向着用户体验提升方面靠近,那么Zenmode就会变得越来越重要. 近年来,也有很多的新功能依赖于ZenMode去实现,也有很多专利在这个方面申请成功. 举两个简单的例 ...

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

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

最新文章

  1. java myeclipse jar 导出问题
  2. R使用pROC和ggplot2包绘制ROC曲线
  3. 港府拟修例禁止电子烟入口及销售 保障市民健康
  4. centos 防火墙位置
  5. vue中v-for循环如何将变量带入class的属性名中
  6. 区块链项目开发区块链应用场景需满足3个
  7. oracle数据库sql查询,oracle数据库中常用经典SQL查询
  8. Linux下gcc与g++用法以及编写makefile
  9. 【C语言入门教程】4.7 指针的地址分配 - mallocl(), free()
  10. 报错:[Microsoft][ODBC 驱动程序管理器] 无效的字符串或缓冲区长度
  11. 计算机网络自顶向下方法 【第一章 计算机网络及因特网】
  12. BF算法及KMP算法
  13. python画结构图_【实战案例】五分钟!用python绘制系统架构图
  14. 西门子实数转整数_怎样将实数转换成整数
  15. 游戏原画和3D游戏建模,哪个更胜一筹?
  16. 当clipper遇到重复裁剪框
  17. int3断点指令的原理和示例
  18. 创新药、大协同,科天云助力信达生物全球创新研发
  19. 电脑没有声音怎么办?
  20. 记一次robotframework-ride快捷方式打不开的坑!!

热门文章

  1. matlab课程的评价反馈,对老师授课能力的评语
  2. 2021 极客大挑战 web
  3. 债券价格和到期收益率的关系_[债券知识]什么是到期收益率?到期收益率与债券价格的关系 - 南方财富网...
  4. JS实现一键复制功能
  5. 单点登录SSO,统一认证oAuth名词解释
  6. linux安装基于PHP的web软件,Linux下Web服务器应用之基于LAMP构建phpwind论坛
  7. 怎么将pdf文件免费转为扫描件
  8. windows查看硬盘序列号
  9. 备考PMP的一些心得
  10. python将tif从wgs84转gcj02