android framework notification通知声音流程分析

文章目录

    • 背景
    • 分析

背景

遇到客户反馈问题,问题解决后,梳理一下流程

问题现象:首次开机通知没有声音,重启可以恢复正常,仅仅通知声音有问题,其它声音都是正常的

分析

Notification 的创建(声音有两种方式setDefaults 以及setSound)

    private void showNotification() {
        NotificationManager notificationManager = (NotificationManager) getSystemService
                (NOTIFICATION_SERVICE);
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
        mBuilder.setContentTitle("我是标题")
                .setContentText("我是内容")
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                .setSmallIcon(R.mipmap.ic_launcher_round)
                .setWhen(System.currentTimeMillis())
                .setTicker("我是测试内容")
                //这里通知方式为声音
                .setDefaults(Notification.DEFAULT_SOUND);
        notificationManager.notify(10, mBuilder.build());

    }

最后会调用到notificationManager.notify

    public void notify(String tag, int id, Notification notification)
    {   
        int[] idOut = new int[1];
        INotificationManager service = getService();
        String pkg = mContext.getPackageName();
        if (notification.sound != null) {
            notification.sound = notification.sound.getCanonicalUri();
            if (StrictMode.vmFileUriExposureEnabled()) {
                notification.sound.checkFileUriExposed("Notification.sound");
            }
        }
        fixLegacySmallIcon(notification, pkg);
        if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
            if (notification.getSmallIcon() == null) {
                throw new IllegalArgumentException("Invalid notification (no valid small icon): "
                    + notification);
            }
        }
        if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
        Notification stripped = notification.clone();
        Builder.stripForDelivery(stripped);
        try {
            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id, 
                    stripped, idOut, UserHandle.myUserId());
            if (id != idOut[0]) {
                Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
            }
        } catch (RemoteException e) {
        }
    } 

service.enqueueNotificationWithTag


    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
            final int callingPid, final String tag, final int id, final Notification notification,
            int[] idOut, int incomingUserId) {
        if (DBG) {
            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
                    + " notification=" + notification);
        }
...

        mHandler.post(new Runnable() {
            @Override
            public void run() {

                synchronized (mNotificationList) {

                    // === Scoring ===

                    // 0. Sanitize inputs
                    notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
                            Notification.PRIORITY_MAX);
                    // Migrate notification flags to scores
                    if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {
                        if (notification.priority < Notification.PRIORITY_MAX) {
                            notification.priority = Notification.PRIORITY_MAX;
                        }
                    } else if (SCORE_ONGOING_HIGHER &&
                            0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {
                        if (notification.priority < Notification.PRIORITY_HIGH) {
                            notification.priority = Notification.PRIORITY_HIGH;
                        }
                    }
                    // force no heads up per package config
                    if (!mRankingHelper.getPackagePeekable(pkg, callingUid)) {
                        if (notification.extras == null) {
                            notification.extras = new Bundle();
                        }
                        notification.extras.putInt(Notification.EXTRA_AS_HEADS_UP,
                                Notification.HEADS_UP_NEVER);
                    }

                    // 1. initial score: buckets of 10, around the app [-20..20]
                    int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;

                    // 2. extract ranking signals from the notification data
                    final StatusBarNotification n = new StatusBarNotification(
                            pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
                            user);
                    NotificationRecord r = new NotificationRecord(n, score);
                    NotificationRecord old = mNotificationsByKey.get(n.getKey());
                    if (old != null) {
                        // Retain ranking information from previous record
                        r.copyRankingInformation(old);
                    }

                    // Handle grouped notifications and bail out early if we
                    // can to avoid extracting signals.
                    handleGroupedNotificationLocked(r, old, callingUid, callingPid);
                    boolean ignoreNotification =
                            removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);

                    // This conditional is a dirty hack to limit the logging done on
                    //     behalf of the download manager without affecting other apps.
                    if (!pkg.equals("com.android.providers.downloads")
                            || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
                        int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
                        if (ignoreNotification) {
                            enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
                        } else if (old != null) {
                            enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
                        }
                        EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
                                pkg, id, tag, userId, notification.toString(),
                                enqueueStatus);
                    }

                    if (ignoreNotification) {
                        return;
                    }

                    mRankingHelper.extractSignals(r);

                    // 3. Apply local rules

                    /// M: [Mobile Management] Check if apps are blocked by MoMS
                    if (blockApps(pkg, id, notification)) {
                        score = JUNK_SCORE;
                    }

                    // blocked apps
                    if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
                        if (!isSystemNotification) {
                            r.score = JUNK_SCORE;
                            Slog.e(TAG, "Suppressing notification from package " + pkg
                                    + " by user request.");
                            mUsageStats.registerBlocked(r);
                        }
                    }

                    if (r.score < SCORE_DISPLAY_THRESHOLD) {
                        // Notification will be blocked because the score is too low.
                        return;
                    }

                    int index = indexOfNotificationLocked(n.getKey());
                    if (index < 0) {
                        mNotificationList.add(r);
                        mUsageStats.registerPostedByApp(r);
                    } else {
                        old = mNotificationList.get(index);
                        mNotificationList.set(index, r);
                        mUsageStats.registerUpdatedByApp(r, old);
                        // Make sure we don't lose the foreground service state.
                        notification.flags |=
                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
                        r.isUpdate = true;
                    }

                    mNotificationsByKey.put(n.getKey(), r);

                    // Ensure if this is a foreground service that the proper additional
                    // flags are set.
                    if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
                        notification.flags |= Notification.FLAG_ONGOING_EVENT
                                | Notification.FLAG_NO_CLEAR;
                    }

                    applyZenModeLocked(r);
                    mRankingHelper.sort(mNotificationList);

                    if (notification.getSmallIcon() != null
                        /// M: Do not show notifications if FLAG_HIDE_NOTIFICATION is on
                        && (notification.flags & Notification.FLAG_HIDE_NOTIFICATION) == 0) {
                        StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
                        mListeners.notifyPostedLocked(n, oldSbn);
                    } else {
                        Slog.e(TAG, "Not posting notification without small icon: " + notification);
                        if (old != null && !old.isCanceled) {
                            mListeners.notifyRemovedLocked(n);
                        }
                        // ATTENTION: in a future release we will bail out here
                        // so that we do not play sounds, show lights, etc. for invalid
                        // notifications
                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
                                + n.getPackageName());
                    }

                    buzzBeepBlinkLocked(r);
                }
            }
        });

        idOut[0] = id;

        /// M: Just filter for special test case notification flow, normal can ignore it. @{
        if (foundTarget) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException exception) {
                // ignore it.
            }
        }
        /// @}
    }

进入buzzBeepBlinkLocked


    private void buzzBeepBlinkLocked(NotificationRecord record) {
        boolean buzz = false;
        boolean beep = false;
        boolean blink = false;



        // If we're not supposed to beep, vibrate, etc. then don't.
        final String disableEffects = disableNotificationEffects(record);
        if (disableEffects != null) {
            ZenLog.traceDisableEffects(record, disableEffects);
        }

        /// M: Determine if we should play alerts
        final boolean enableAlerts = (((mDisabledNotifications
                              & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
                              // Phone app will disable alerts but we need the alert of mms
                              || record.sbn.getPackageName().equals("com.android.mms"))
                             && !mDmLock && !mPplLock; // Never play alerts when DM/PPL lock

        // if (disableEffects == null
        if (enableAlerts && disableEffects == null
                && (!(record.isUpdate
                    && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
                && (record.getUserId() == UserHandle.USER_ALL ||
                    record.getUserId() == currentUser ||
                    mUserProfiles.isCurrentProfile(record.getUserId()))
                && canInterrupt
                && mSystemReady
                && mAudioManager != null) {
            if (DBG) Slog.v(TAG, "Interrupting!");

            sendAccessibilityEvent(notification, record.sbn.getPackageName());

            // sound

            // should we use the default notification sound? (indicated either by
            // DEFAULT_SOUND or because notification.sound is pointing at
            // Settings.System.NOTIFICATION_SOUND)
            final boolean useDefaultSound =
                   (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
                           Settings.System.DEFAULT_NOTIFICATION_URI
                                   .equals(notification.sound);

            Uri soundUri = null;
            boolean hasValidSound = false;

            if (useDefaultSound) {
                soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;

                // check to see if the default notification sound is silent
                ContentResolver resolver = getContext().getContentResolver();
                hasValidSound = Settings.System.getString(resolver,
                       Settings.System.NOTIFICATION_SOUND) != null;
            } else if (notification.sound != null) {
                soundUri = notification.sound;
                hasValidSound = (soundUri != null);
            }

            if (hasValidSound) {
                boolean looping =
                        (notification.flags & Notification.FLAG_INSISTENT) != 0;
                AudioAttributes audioAttributes = audioAttributesForNotification(notification);
                mSoundNotificationKey = record.getKey();
                // do not play notifications if stream volume is 0 (typically because
                // ringer mode is silent) or if there is a user of exclusive audio focus
                if ((mAudioManager.getStreamVolume(
                        AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
                            && !mAudioManager.isAudioFocusExclusive()) {
                    final long identity = Binder.clearCallingIdentity();
                    try {
                        final IRingtonePlayer player =
                                mAudioManager.getRingtonePlayer();
                        if (player != null) {
                            if (DBG) Slog.v(TAG, "Playing sound " + soundUri
                                    + " with attributes " + audioAttributes);
                            player.playAsync(soundUri, record.sbn.getUser(), looping,
                                    audioAttributes);
                            beep = true;
                        }
                    } catch (RemoteException e) {
                    } finally {
                        Binder.restoreCallingIdentity(identity);
                    }
                }
            }



        if (buzz || beep || blink) {
            EventLogTags.writeNotificationAlert(record.getKey(),
                    buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
            mHandler.post(mBuzzBeepBlinked);
        }
    }

其中就可以发现关于声音的逻辑,怀疑问题出现在这里,通过添加log判断是否满足逻辑,根据log分析出现首次通知无声的原因就是因为不满足最外面的一层逻辑判断disableEffects == null ,导致的问题的出现


        if (enableAlerts && disableEffects == null
                && (!(record.isUpdate
                    && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
                && (record.getUserId() == UserHandle.USER_ALL ||
                    record.getUserId() == currentUser ||
                    mUserProfiles.isCurrentProfile(record.getUserId()))
                && canInterrupt
                && mSystemReady
                && mAudioManager != null) {
            if (DBG) Slog.v(TAG, "Interrupting!");

            sendAccessibilityEvent(notification, record.sbn.getPackageName());

            // sound

            // should we use the default notification sound? (indicated either by
            // DEFAULT_SOUND or because notification.sound is pointing at
            // Settings.System.NOTIFICATION_SOUND)
            final boolean useDefaultSound =
                   (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
                           Settings.System.DEFAULT_NOTIFICATION_URI
                                   .equals(notification.sound);

            Uri soundUri = null;
            boolean hasValidSound = false;

            if (useDefaultSound) {
                soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;

                // check to see if the default notification sound is silent
                ContentResolver resolver = getContext().getContentResolver();
                hasValidSound = Settings.System.getString(resolver,
                       Settings.System.NOTIFICATION_SOUND) != null;
            } else if (notification.sound != null) {
                soundUri = notification.sound;
                hasValidSound = (soundUri != null);
            }

            if (hasValidSound) {
                boolean looping =
                        (notification.flags & Notification.FLAG_INSISTENT) != 0;
                AudioAttributes audioAttributes = audioAttributesForNotification(notification);
                mSoundNotificationKey = record.getKey();
                // do not play notifications if stream volume is 0 (typically because
                // ringer mode is silent) or if there is a user of exclusive audio focus
                if ((mAudioManager.getStreamVolume(
                        AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
                            && !mAudioManager.isAudioFocusExclusive()) {
                    final long identity = Binder.clearCallingIdentity();
                    try {
                        final IRingtonePlayer player =
                                mAudioManager.getRingtonePlayer();
                        if (player != null) {
                            if (DBG) Slog.v(TAG, "Playing sound " + soundUri
                                    + " with attributes " + audioAttributes);
                            player.playAsync(soundUri, record.sbn.getUser(), looping,
                                    audioAttributes);
                            beep = true;
                        }
                    } catch (RemoteException e) {
                    } finally {
                        Binder.restoreCallingIdentity(identity);
                    }
                }
            }
        }

查到了问题的原因,即可进行修改

参考 https://blog.csdn.net/aaron121314/article/details/77803622

你可能感兴趣的:(#,android,framework)