android多用户切换时notification的更新

来源:互联网 发布:自主车企实力排名 知乎 编辑:程序博客网 时间:2024/06/06 01:29

描述

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 0
原创粉丝点击