From eec420a3759fe357afac3e795d946dc7f8f83503 Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Wed, 22 Sep 2021 10:21:17 -0400 Subject: [PATCH] Prevent NPE in NavigationBar When the view detaches, we were left in a state where the view was null but the a11y callbacks were still using it. Test: atest SystemUITests Fixes: 196285098 Change-Id: Ie53ac9d73c22be204d905079773f3f021010fd1d --- .../systemui/navigationbar/NavigationBar.java | 10 ++++++---- .../navigationbar/NavigationBarTest.java | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 43315f605099e..8576a280c5601 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -712,6 +712,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mHandler.removeCallbacks(mAutoDim); mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); mHandler.removeCallbacks(mEnableLayoutTransitions); + mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener); mFrame = null; mNavigationBarView = null; mOrientationHandle = null; @@ -1391,10 +1392,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener, void updateAccessibilityServicesState() { int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState(); - boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; - boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; - mNavigationBarView.setAccessibilityButtonState(clickable, longClickable); - + if (mNavigationBarView != null) { + boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; + boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; + mNavigationBarView.setAccessibilityButtonState(clickable, longClickable); + } updateSystemUiStateFlags(a11yFlags); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index ed3c473aacf3a..36228dcb08c36 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -56,6 +56,7 @@ import android.testing.TestableLooper.RunWithLooper; import android.view.Display; import android.view.DisplayInfo; import android.view.MotionEvent; +import android.view.View; import android.view.WindowManager; import android.view.WindowMetrics; import android.view.accessibility.AccessibilityManager; @@ -122,6 +123,8 @@ public class NavigationBarTest extends SysuiTestCase { EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory; @Mock EdgeBackGestureHandler mEdgeBackGestureHandler; + @Mock + NavigationBarA11yHelper mNavigationBarA11yHelper; @Rule public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck(); @@ -256,6 +259,20 @@ public class NavigationBarTest extends SysuiTestCase { assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0); } + @Test + public void testA11yEventAfterDetach() { + View v = mNavigationBar.createView(null); + mNavigationBar.onViewAttachedToWindow(v); + verify(mNavigationBarA11yHelper).registerA11yEventListener(any( + NavigationBarA11yHelper.NavA11yEventListener.class)); + mNavigationBar.onViewDetachedFromWindow(v); + verify(mNavigationBarA11yHelper).removeA11yEventListener(any( + NavigationBarA11yHelper.NavA11yEventListener.class)); + + // Should be safe even though the internal view is now null. + mNavigationBar.updateAccessibilityServicesState(); + } + private NavigationBar createNavBar(Context context) { DeviceProvisionedController deviceProvisionedController = mock(DeviceProvisionedController.class); @@ -287,7 +304,7 @@ public class NavigationBarTest extends SysuiTestCase { mHandler, mock(NavigationBarOverlayController.class), mUiEventLogger, - mock(NavigationBarA11yHelper.class), + mNavigationBarA11yHelper, mock(UserTracker.class))); }