标签: 勿扰模式
2017-08-08 11:05 60人阅读 收藏 举报
勿扰模式是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;
- if (zen == Global.ZEN_MODE_ALARMS) return false;
- if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
- if (config.allowRepeatCallers && REPEAT_CALLERS.isRepeat(context, extras)) {
- return true;
- }
- if (!config.allowCalls) return false;
- 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) {
-
-
- 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())) {
- if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
- lookupResult = resolvePhoneContact(mContext, uri.getSchemeSpecificPart());
- } else if ("mailto".equals(uri.getScheme())) {
- if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
- lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());
- } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
- if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
- lookupResult = searchContacts(mContext, uri);
- } else {
- lookupResult = new LookupResult();
- 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);
-
-
- 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");
- }
-
-
- 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方法就可以了。