Android 11 禁用状态栏下拉开关开发

5,620 阅读2分钟

背景

公司是做系统应用的,因此需要提供一个禁止状态栏下拉的接口。这个功能本身已经被实现了。但是在Android 9和Android 10无问题的方案,却在Android 11遭遇了禁止下拉失败的问题。

Android 9 和 10 上的方案(基本相同)

定义一个 字段用于控制下拉开关。注意。使用 persist.sys开头,而不要是sys.*,sys.*参数值保存在内存中重启就会失效。此处定义persist.sys.statusbar.prohibit. frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java

public class DragDownHelper implements Gefingerpoken{
    ...
    //在 onInterceptTouchEvent 第一行进行判断
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (isStatusBarDragDownProhibited()) {
            return false;
        }
        ...
    }
    public boolean onTouchEvent(MotionEvent event) {
        if (!mDraggingDown) {
            return false;
        }
        //代码1
        if (isStatusBarDragDownProhibited()) {
            return false;
        }
    }
    private boolean isStatusBarDragDownProhibited() {
        return SystemProperties.getBoolean("persist.sys.statusbar.prohibit", false);
    }

}

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java

public class StatusBar ..{
    ...
    
    public boolean interceptTouchEvent(MotionEvent event) {
        if (DEBUG_GESTURES) {
            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
                EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
                        event.getActionMasked(), (int) event.getX(), (int) event.getY(),
                        mDisabled1, mDisabled2);
            }

        }

        if (SPEW) {
            Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled1="
                    + mDisabled1 + " mDisabled2=" + mDisabled2);
        } else if (CHATTY) {
            if (event.getAction() != MotionEvent.ACTION_MOVE) {
                Log.d(TAG, String.format(
                            "panel: %s at (%f, %f) mDisabled1=0x%08x mDisabled2=0x%08x",
                            MotionEvent.actionToString(event.getAction()),
                            event.getRawX(), event.getRawY(), mDisabled1, mDisabled2));
            }
        }

        if (DEBUG_GESTURES) {
            mGestureRec.add(event);
        }
        //代码2 拦截事件
        if (isStatusBarDragDownProhibited()) {
                return true;
        }

        if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
            final boolean upOrCancel =
                    event.getAction() == MotionEvent.ACTION_UP ||
                    event.getAction() == MotionEvent.ACTION_CANCEL;
            if (upOrCancel && !mExpandedVisible) {
                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
            } else {
                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
            }
        }
        return false;
    }

    private boolean isStatusBarDragDownProhibited() {
        return SystemProperties.getBoolean("persist.sys.statusbar.prohibit", false);
    }
}

Android 11 的变化

在Android 11 中,以上修改出现一个bug,如果在屏幕中间滑动,状态栏仍然会被下拉成功。 为何会出现该问题?

在屏幕中间下滑,事件将会走到OverviewProxyService.onStatusBarMotionEvent方法中。而该方法,在Android 10和11中存在差异。

android 10 中代码如下

class OverviewProxyService {

     	@Override

        public void onStatusBarMotionEvent(MotionEvent event) {
            if (!verifyCaller("onStatusBarMotionEvent")) {
                return;
            }
            long token = Binder.clearCallingIdentity();
            try {
                // TODO move this logic to message queue
                mHandler.post(()->{
                    StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
                    if (bar != null) {
                        //代码1
                        bar.dispatchNotificationsPanelTouchEvent(event);
                        int action = event.getActionMasked();

                        if (action == ACTION_DOWN) {
                            mStatusBarGestureDownEvent = MotionEvent.obtain(event);
                        }
                        if (action == ACTION_UP || action == ACTION_CANCEL) {
                            mStatusBarGestureDownEvent.recycle();
                            mStatusBarGestureDownEvent = null;
                        }
                        event.recycle();
                    }
                });
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
}

如上所示在 android 10 中 调用了 bar.dispatchNotificationsPanelTouchEvent(event); 该方法最终调用到NotificationsPanelView.onTouchEvent方法,在该方法中,有禁止下拉的拦截处理。

而 Android 11代码如下

class OverviewProxyService{

    @Override
    public void onStatusBarMotionEvent(MotionEvent event) {
        if (!verifyCaller("onStatusBarMotionEvent")) {
            return;
        }
        long token = Binder.clearCallingIdentity();
        try {
            // TODO move this logic to message queue
            mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
                mHandler.post(()-> {
                    StatusBar statusBar = statusBarLazy.get();
                    int action = event.getActionMasked();
                    if (action == ACTION_DOWN) {
                        mInputFocusTransferStarted = true;
                        mInputFocusTransferStartY = event.getY();
                        mInputFocusTransferStartMillis = event.getEventTime();
                        //代码1
                        statusBar.onInputFocusTransfer(
                                mInputFocusTransferStarted, false /* cancel */,
                                0 /* velocity */);
                    }

                    if (action == ACTION_UP || action == ACTION_CANCEL) {

                        mInputFocusTransferStarted = false;

                        statusBar.onInputFocusTransfer(mInputFocusTransferStarted,

                                action == ACTION_CANCEL,

                                (event.getY() - mInputFocusTransferStartY)/ (event.getEventTime() - mInputFocusTransferStartMillis));

                    }
                    event.recycle();
                });
            });
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }
}

android 11中调用了 statusBar.onInputFocusTransfer方法,经过如下流程 StatusBar.onInputFocusTransfer -> NotificationPanelViewController#stopWaitingForOpenPanelGesture -> NotificationPanelViewController#fling -> PanelViewController#fling->NotificationPanelViewController#flingToHeight 其没有经过 onTouch方法,因此没有被拦截。

Android 11 禁止状态栏下拉

修改CommoneQueue上的panelsEnabled 方法

    public boolean panelsEnabled() {
        final int disabled1 = getDisabled1(DEFAULT_DISPLAY);
        final int disabled2 = getDisabled2(DEFAULT_DISPLAY);
        return (disabled1 & StatusBarManager.DISABLE_EXPAND) == 0
                && (disabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
                && !ONLY_CORE_APPS
                && !isStatusBarDragDownProhibited();
    }

    private boolean isStatusBarDragDownProhibited(){
        return SystemProperties.getBoolean("sys.statusbar.dragdown.prohibit", false);
    }

onInputFocusTransfer 方法中,下拉操作就会被拦截。

class SatusBar{
    public void onInputFocusTransfer(boolean start, boolean cancel, float velocity) {
        if (!mCommandQueue.panelsEnabled()) {
            return;
        }
        if (start) {
            mNotificationPanelViewController.startWaitingForOpenPanelGesture();
        } else {
            mNotificationPanelViewController.stopWaitingForOpenPanelGesture(cancel, velocity);
        }
    }
}