android多用户切换时notification的更新

描述

General description:
Music displyed in notification bar after switch New user mode to owner user.


Reproducibility:
10/10


Precondition:
Insert SD card with some song.
Add a New user.


Step:
1.Main Menu->switch to new user->music->play a song->drag down notification bar->click owner switch owner user->drag down notification bar
2.Check the phone display.


Actual result:
Music displyed in notification bar after switch New user mode to owner user.


Expect result:
Music should not displyed in notification bar after switch New user mode to owner user.

发现源头

我们需要考虑到Notification属于SystemUI那一部分,实际是SystemUIApplication.java已经有下列定义。

  private final Class<?>[] SERVICES = new Class[] {
            com.android.systemui.tuner.TunerService.class,
            com.android.systemui.keyguard.KeyguardViewMediator.class,
            com.android.systemui.recents.Recents.class,
            com.android.systemui.volume.VolumeUI.class,
            Divider.class,
            **com.android.systemui.statusbar.SystemBars.class,**
            com.android.systemui.usb.StorageNotification.class,
            com.android.systemui.power.PowerUI.class,
            com.android.systemui.media.RingtonePlayer.class,
            com.android.systemui.keyboard.KeyboardUI.class,
            com.android.systemui.tv.pip.PipUI.class,
            com.android.systemui.shortcut.ShortcutKeyDispatcher.class
    };

目前关注于SystemBars这个类,会调用


        mStatusBar.start();

PhoneStatusBar.java是BaseStatusBar.java的子类,我们看看它的start方法

   @Override
    public void start() {
    ....
     super.start();
    ....
    }
很遗憾,又回到了父类BaseStatusBar
public void start() {
....
mNotificationData = new NotificationData(this);
....
createAndAddWindows();
....
}
 protected abstract void createAndAddWindows();

嗯,这是个抽象的方法,又回到了子类PhoneStatusBar.java,这是一种代码风格吧!

 @Override
    public void createAndAddWindows() {
        addStatusBarWindow();
    }

    private void addStatusBarWindow() {
        makeStatusBarView();
        mStatusBarWindowManager = new StatusBarWindowManager(mContext);
        mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
                mHeadsUpManager);
        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
    }

好吧,我编不下去了。我在PhoneStatusbar.java中发现了一段代码


    /**
     * Updates System UI resources that can be skinned
     */
    @Override
    public void updateSkinnedResources() {
        if (mNavigationBarView != null) {
            mNavigationBarView.updateNavigationBarResources();
        }

        mStatusBarView.updateStatusBarResources();

        updateSkinnedResourcesForLockscreen();
        refreshBrightnessMirror();
    }

这是一段ProgressBar更新的UI模块,想必于我前面找到的SERVICES 数组一定是都有这个方法的,
这里写图片描述
看看 updateSkinnedResourcesForLockscreen();的实现

 /**
     * Updates System UI resources that can be skinned for lockscreen
     */
    private void updateSkinnedResourcesForLockscreen() {
        final Resources res = mContext.getResources();

        // Apply skin resources to bouncer if keyguard is showing.
        if (mStatusBarKeyguardViewManager != null) {
           mStatusBarKeyguardViewManager.resetBouncerView();
        }

        // Inform lockscreen it needs to update.
        SomcLockscreenRuntimeThemeUpdater.newThemeConfiguration(this, (ViewGroup) mStatusBarWindow,
                res);
        updateNotificationSkinnedResources();

        mIconController.updateSkinnedResources();
        mIconController.setIconsDarkKeyguard(mState == StatusBarState.KEYGUARD);

        ((KeyguardStatusView)mKeyguardStatusView).loadClockPluginView(true);
    }

根据字面意思的理解,看看updateNotificationSkinnedResources();方法

  // all notifications
    protected NotificationData mNotificationData;
 private void updateNotificationSkinnedResources() {

        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
        if (activeNotifications != null) {
            final int n = activeNotifications.size();
            for (int i = 0; i < n; i++) {
                NotificationData.Entry entry = activeNotifications.get(i);
                entry.row.updateSkinnedResources();
            }
        }

        mKeyguardIconOverflowContainer.updateSkinnedResources();
        mStackScroller.updateSkinnedResources();
    }

在这里ArrayList类型 activeNotifications表明了用户切换后应该显示的Notification,我们看看这个集合是在哪里获取值的,打开NotificationData.java文件

private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>();
public ArrayList<Entry> getActiveNotifications() {
        return mSortedAndFiltered;
    }
// anything changed, and this class should call back the UI so it updates itself.
    public void filterAndSort(final boolean filterOutOldNotifications) {
        mSortedAndFiltered.clear();
        resetHideNotificationState();

        synchronized (mEntries) {
            final int N = mEntries.size();
            for (int i = 0; i < N; i++) {
                Entry entry = mEntries.valueAt(i);
                StatusBarNotification sbn = entry.notification;

                if (shouldFilterOut(sbn, filterOutOldNotifications)) {
                    continue;
                }

                mSortedAndFiltered.add(entry);
            }
        }

        Collections.sort(mSortedAndFiltered, mRankingComparator);
    }

正如注释所描述的那样,这个类会进行UI回调,说明我们前面的流程是正确的,大家可能会好奇,为什么你一下就走对了,其实我也是打了日志,一步步抓log,编译测试,判断下来的。这里面有个if语句,会对我们的Notification进行一个过滤,看看它的具体实现,可能音乐播放的Notification没有被过滤掉。

  boolean shouldFilterOut(StatusBarNotification sbn, final boolean filterOutOldNotifications) {
        **if (isMediaNotification(sbn)) {
            // Exit immediately if this is a media notification, we need to keep it!
            return false;
        }**

        if (!(mEnvironment.isDeviceProvisioned() ||
                showNotificationEvenIfUnprovisioned(sbn))) {
            return true;
        }

        if (!mEnvironment.isNotificationForCurrentProfiles(sbn)) {
            return true;
        }

        if (mEnvironment.onSecureLockScreen() &&
                (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
                        || mEnvironment.shouldHideNotifications(sbn.getUserId())
                        || mEnvironment.shouldHideNotifications(sbn.getKey()))) {
            return true;
        }

        if (!BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
                && mGroupManager.isChildInGroupWithSummary(sbn)) {
            return true;
        }

        if (filterOutOldNotifications && (mTimeNotificationsWereLastViewed > sbn.getPostTime())) {
            mNotificationsHidden = true;
            return true;
        }

        return false;
    }

    /**
     * Return whether there are any clearable notifications (that aren't errors).
     */
    public boolean hasActiveClearableNotifications() {
        for (Entry e : mSortedAndFiltered) {
            if (e.getContentView() != null) { // the view successfully inflated
                if (e.notification.isClearable()) {
                    return true;
                }
            }
        }
        return false;
    }

果然,当Notification是媒体类型时直接返回了FALSE,也就是说不过滤掉。不知道是谁改了,只要把这一段代码注释掉就可以了。

/*
if (isMediaNotification(sbn)) {
            // Exit immediately if this is a media notification, we need to keep it!
            return false;
        }
*/

这是最终的方法,意思就是说,媒体类型不做特殊处理,我根据自己的理解,改成这样,也能通过

 if (isMediaNotification(sbn)) {
        Log.d("elliot_log","ActivityManager.getCurrentUser():"+ActivityManager.getCurrentUser());
        Log.d("elliot_log","sbn.getUserId():"+sbn.getUserId());

            if(ActivityManager.getCurrentUser()!=sbn.getUserId()){
               return true;
               }
            return false;
        }

如果当前用户id不等于Notification所属于的id,则过滤掉。

心得

这个bug让我知道,把原理弄清楚,还是很简单的,没有想象的那么难。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值