勿扰模式是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

private boolean shouldRingForContact(Uri contactUri) {

final NotificationManager manager =

(NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);

final Bundle extras = new Bundle();

if (contactUri != null) {

extras.putStringArray(Notification.EXTRA_PEOPLE, new String[] {contactUri.toString()});

}

return manager.matchesCallFilter(extras);

}

使用matchesCallFilter来决定是否响铃。

4.matchesCallFilter流程分析

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

public boolean matchesCallFilter(Bundle extras) {

INotificationManager service = getService();

try {

return service.matchesCallFilter(extras);

} catch (RemoteException e) {

throw e.rethrowFromSystemServer();

}

}

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

public boolean matchesCallFilter(Bundle extras) {

enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");

return mZenModeHelper.matchesCallFilter(

Binder.getCallingUserHandle(),

extras,

mRankingHelper.findExtractor(ValidateNotificationPeople.class),

MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,

MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);

}

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

public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,

ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity) {

synchronized (mConfig) {

return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConfig, userHandle,

extras, validator, contactsTimeoutMs, timeoutAffinity);

}

}

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

public static boolean matchesCallFilter(Context context, int zen, ZenModeConfig config,

UserHandle userHandle, Bundle extras, ValidateNotificationPeople validator,

int contactsTimeoutMs, float timeoutAffinity) {

if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through //勿扰模式判断

if (zen == Global.ZEN_MODE_ALARMS) return false; // not an alarm

if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {

if (config.allowRepeatCallers && REPEAT_CALLERS.isRepeat(context, extras)) {  //短时间内第二次来电放行

return true;

}

if (!config.allowCalls) return false; // no other calls get through //是否禁止全部来电

if (validator != null) {

final float contactAffinity = validator.getContactAffinity(userHandle, extras,

contactsTimeoutMs, timeoutAffinity);

return audienceMatches(config.allowCallsFrom, contactAffinity); //依据联系人判断

}

}

return true;

}

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

private static boolean audienceMatches(int source, float contactAffinity) {

switch (source) {

case ZenModeConfig.SOURCE_ANYONE: //所有人

return true;

case ZenModeConfig.SOURCE_CONTACT:

return contactAffinity >= ValidateNotificationPeople.VALID_CONTACT; //联系人

case ZenModeConfig.SOURCE_STAR:

return contactAffinity >= ValidateNotificationPeople.STARRED_CONTACT; //星标联系人

default:

Slog.w(TAG, "Encountered unknown source: " + source);

return true;

}

}很简单,就是依据配置走不同的分支。接下来继续看核心的getContactAffinity方法

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

public float getContactAffinity(UserHandle userHandle, Bundle extras, int timeoutMs,

float timeoutAffinity) {

...

final PeopleRankingReconsideration prr = validatePeople(context, key, extras, affinityOut); //从缓存中读取值

float affinity = affinityOut[0];

if (prr != null) {

// Perform the heavy work on a background thread so we can abort when we hit the

// timeout.

final Semaphore s = new Semaphore(0);

AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {

@Override

public void run() {

prr.work(); //开线程查询数据库

s.release();

}

});

try {

if (!s.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {

Slog.w(TAG, "Timeout while waiting for affinity: " + key + ". "

+ "Returning timeoutAffinity=" + timeoutAffinity);

return timeoutAffinity; //超时后直接返回

}

} catch (InterruptedException e) {

Slog.w(TAG, "InterruptedException while waiting for affinity: " + key + ". "

+ "Returning affinity=" + affinity, e);

return affinity; //线程被中断后返回缓存结果

}

affinity = Math.max(prr.getContactAffinity(), affinity); //正常情况下返回的结果

}

return affinity;

}

线程工作方法work如下:

public void work() {

long start = SystemClock.elapsedRealtime();

if (VERBOSE) Slog.i(TAG, "Executing: validation for: " + mKey);

long timeStartMs = System.currentTimeMillis();

for (final String handle: mPendingLookups) {

LookupResult lookupResult = null;

final Uri uri = Uri.parse(handle);

if ("tel".equals(uri.getScheme())) {   //处理电话号码类型的uri

if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);

lookupResult = resolvePhoneContact(mContext, uri.getSchemeSpecificPart());

} else if ("mailto".equals(uri.getScheme())) { //处理电子邮件类型的uri

if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);

lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());

} else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) { //处理联系人lookup_uri

if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);

lookupResult = searchContacts(mContext, uri);

} else { //非法的uri,生成默认的结果

lookupResult = new LookupResult();  // invalid person for the cache

Slog.w(TAG, "unsupported URI " + handle);

}

if (lookupResult != null) {

synchronized (mPeopleCache) {

final String cacheKey = getCacheKey(mContext.getUserId(), handle);

mPeopleCache.put(cacheKey, lookupResult); //查询存储到缓存

}

if (DEBUG) Slog.d(TAG, "lookup contactAffinity is " + lookupResult.getAffinity());

mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());  //保存结果值

} else {

if (DEBUG) Slog.d(TAG, "lookupResult is null");

}

}

...

}以电话号码分支为例:

private LookupResult resolvePhoneContact(Context context, final String number) {

Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,

Uri.encode(number));

return searchContacts(context, phoneUri);

}

private LookupResult searchContacts(Context context, Uri lookupUri) {

LookupResult lookupResult = new LookupResult();

Cursor c = null;

try {

c = context.getContentResolver().query(lookupUri, LOOKUP_PROJECTION, null, null, null);

if (c == null) {

Slog.w(TAG, "Null cursor from contacts query.");

return lookupResult;

}

while (c.moveToNext()) {

lookupResult.mergeContact(c);

}

} catch (Throwable t) {

Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);

} finally {

if (c != null) {

c.close();

}

}

return lookupResult;

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

public void mergeContact(Cursor cursor) {

mAffinity = Math.max(mAffinity, VALID_CONTACT);

// Contact ID

int id;

final int idIdx = cursor.getColumnIndex(Contacts._ID);

if (idIdx >= 0) {

id = cursor.getInt(idIdx);

if (DEBUG) Slog.d(TAG, "contact _ID is: " + id);

} else {

id = -1;

Slog.i(TAG, "invalid cursor: no _ID");

}

// Starred

final int starIdx = cursor.getColumnIndex(Contacts.STARRED);

if (starIdx >= 0) {

boolean isStarred = cursor.getInt(starIdx) != 0;

if (isStarred) {

mAffinity = Math.max(mAffinity, STARRED_CONTACT);

}

if (DEBUG) Slog.d(TAG, "contact STARRED is: " + isStarred);

} else {

if (DEBUG) Slog.d(TAG, "invalid cursor: no STARRED");

}

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

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

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

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

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

  2. 【Android项目】本地FM收音机开发及源码简析

    [Android项目]本地FM收音机开发及源码简析 目录 1.概述 2.收音机的基本原理 3.收音机其他信息 RDS功能 4.Android开发FM收音机源码解析 5.App层如何设计本地FM应用 6 ...

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

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

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

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

  5. android 代码 升级rom,ROM之家简析:Android手机系统怎么升级

    Android系统现在基本上每年都会有一次大的升级,以及很多次小的升级,对于手机用户来说,当然希望自己能用上最新版本的系统.除了手机厂商也会在适当时间发布官方版本系统升级,也可以自己在网上下载安装第三 ...

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

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

  7. 间歇性郁闷症发作期间,生人勿扰,熟人勿找(冷笑的幽默)

    1.人不犯我,我不犯人:人若犯我,礼让三分:人再犯我,我还一针:人还犯我,斩草除根. 2.我允许你走进我的世界,但不许你在我的世界里走来走去. 3.明骚易躲,暗贱难防. 4.天没降大任于我,照样苦我心 ...

  8. android 工厂方法模式应用场景,Android设计模式(四)- 工厂方法模式

    目录 定义 使用场景 UML 模式1 模式2 模式3 简单工厂模式静态工厂模式 简单实现 Android源码中的工厂方法 总结 优点 缺点 工厂方法模式也是创建型模式. 工厂模式根据抽象程度可以分为三 ...

  9. Android源码学习之工厂方法模式应用

    主要内容: 工厂方法模式定义 工厂方法模式优势 工厂方法模式在Android源码中的应用 一.工厂方法模式定义 工厂方法模式定义: Define an interface for creating a ...

最新文章

  1. Logback 配置文件这样优化,TPS提高 10 倍
  2. 10道海量数据处理的面试题
  3. 上海广电电气集团选用Ultimus 提升企业管理
  4. boost::math::tools::simple_continued_fraction用法的测试程序
  5. 洛谷P1919 【模板】A*B Problem升级版(FFT快速傅里叶)
  6. php+mysql+into_PHP+Mysql 如何实现数据库增删改查
  7. Maven的单元测试没有执行的问题
  8. Javascript学习数据结构--字典
  9. Intel Core Enhanced Core架构/微架构/流水线 (10) - 先进存储器访问
  10. python学习之路第四周汇总
  11. 关于一直卡死的两段代码,望对LDD3有兴趣者戳开这个blog : )
  12. W25Q64内部结构
  13. 计算机应用维护师实习周记,计算机系统维护专业毕业实习周记
  14. 小米路由器的服务器无响应怎么回事,小米路由器常见问题与解决方法(高级功能)...
  15. TextCnn原理及实践
  16. 路由器计算机无法上网,电脑可以上网路由器不能上网怎么回事?
  17. python 不安全的包或方法_Python中的10个常见安全漏洞及修复方法
  18. 【Android驱动】aw9106驱动代码流程
  19. ps -aux | grep 用法
  20. python3 分离gif

热门文章

  1. HTML5+CSS3实现哔哩哔哩首页(完整版)
  2. js attr和removeAttr
  3. 焊接机器人结构设计(毕业论文52页+CAD图纸+CAXA图纸+开题报告+任务书)
  4. 【交通运输与新基建(四)】交通运输新型基础建设重点方向
  5. 禁用谷歌浏览器的双手指作向右滑动不后退网页
  6. HTTP 中为什么会有 OPTIONS 请求?
  7. 项目总结一:串口通信 || 串口接收数据和写入的数据不一致
  8. AOE工程实践-银行卡OCR里的图像处理
  9. python如何测试仪器_Python中的测试工具
  10. 链上价值新主张,DeFIL 2.0发布会暨DeFIL全球超级节点招募计划圆满结束