勿扰模式代码结构简析

来源:互联网 发布:黑色玫瑰是什么软件 编辑:程序博客网 时间:2024/06/14 17:56

勿扰模式是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方法就可以了。


原创粉丝点击