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
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 43315f6..8576a28 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 @@
mHandler.removeCallbacks(mAutoDim);
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mHandler.removeCallbacks(mEnableLayoutTransitions);
+ mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
mFrame = null;
mNavigationBarView = null;
mOrientationHandle = null;
@@ -1391,10 +1392,11 @@
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 ed3c473..36228dc 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.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 @@
EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
@Mock
EdgeBackGestureHandler mEdgeBackGestureHandler;
+ @Mock
+ NavigationBarA11yHelper mNavigationBarA11yHelper;
@Rule
public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
@@ -256,6 +259,20 @@
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 @@
mHandler,
mock(NavigationBarOverlayController.class),
mUiEventLogger,
- mock(NavigationBarA11yHelper.class),
+ mNavigationBarA11yHelper,
mock(UserTracker.class)));
}