android 勿扰模式代码结构简析

来源:互联网 发布:windows一键还原官网 编辑:程序博客网 时间:2024/06/04 21:56
 

勿扰模式代码结构简析

标签: 勿扰模式
 60人阅读 评论(0) 收藏 举报
 分类:

目录(?)[+]

勿扰模式是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.   
  7.     if (prr != null) {  
  8.         // Perform the heavy work on a background thread so we can abort when we hit the  
  9.         // timeout.  
  10.         final Semaphore s = new Semaphore(0);  
  11.         AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {  
  12.             @Override  
  13.             public void run() {  
  14.                 prr.work(); //开线程查询数据库  
  15.                 s.release();  
  16.             }  
  17.         });  
  18.   
  19.        try {  
  20.             if (!s.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {  
  21.                 Slog.w(TAG, "Timeout while waiting for affinity: " + key + ". "  
  22.                         + "Returning timeoutAffinity=" + timeoutAffinity);  
  23.                 return timeoutAffinity; //超时后直接返回  
  24.             }  
  25.         } catch (InterruptedException e) {  
  26.             Slog.w(TAG, "InterruptedException while waiting for affinity: " + key + ". "  
  27.                     + "Returning affinity=" + affinity, e);  
  28.             return affinity; //线程被中断后返回缓存结果  
  29.         }  
  30.   
  31.         affinity = Math.max(prr.getContactAffinity(), affinity); //正常情况下返回的结果  
  32.     }  
  33.     return affinity;  
  34. }  
线程工作方法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, nullnullnull);  
  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.   
  4.     // Contact ID  
  5.     int id;  
  6.     final int idIdx = cursor.getColumnIndex(Contacts._ID);  
  7.     if (idIdx >= 0) {  
  8.         id = cursor.getInt(idIdx);  
  9.         if (DEBUG) Slog.d(TAG, "contact _ID is: " + id);  
  10.     } else {  
  11.         id = -1;  
  12.         Slog.i(TAG, "invalid cursor: no _ID");  
  13.     }  
  14.   
  15.     // Starred  
  16.     final int starIdx = cursor.getColumnIndex(Contacts.STARRED);  
  17.     if (starIdx >= 0) {  
  18.         boolean isStarred = cursor.getInt(starIdx) != 0;  
  19.         if (isStarred) {  
  20.             mAffinity = Math.max(mAffinity, STARRED_CONTACT);  
  21.         }  
  22.         if (DEBUG) Slog.d(TAG, "contact STARRED is: " + isStarred);  
  23.     } else {  
  24.         if (DEBUG) Slog.d(TAG, "invalid cursor: no STARRED");  
  25.     }  
  26. }  
实际上就是给mAffinity赋值,标记为普通联系人或者星标联系人。

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

原创粉丝点击