diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b06c3fdb2746f..d46811753e398 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -858,6 +858,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ static boolean sHasFocusableExcludeAutoFocusable; + /** + * Prior to O, auto-focusable didn't exist and views marked as clickable weren't implicitly + * made focusable by default. As a result, apps could (incorrectly) change the clickable + * setting of views off the UI thread. Now that clickable can effect the focusable state, + * changing the clickable attribute off the UI thread will cause an exception (since changing + * the focusable state checks). In order to prevent apps from crashing, we will handle this + * specific case and just not notify parents on new focusables resulting from marking views + * clickable from outside the UI thread. + */ + private static boolean sAutoFocusableOffUIThreadWontNotifyParents; + /** @hide */ @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO}) @Retention(RetentionPolicy.SOURCE) @@ -4182,6 +4193,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, sHasFocusableExcludeAutoFocusable = targetSdkVersion < Build.VERSION_CODES.O; + sAutoFocusableOffUIThreadWontNotifyParents = targetSdkVersion < Build.VERSION_CODES.O; + sCompatibilityDone = true; } } @@ -12135,6 +12148,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int privateFlags = mPrivateFlags; // If focusable is auto, update the FOCUSABLE bit. + int focusableChangedByAuto = 0; if (((mViewFlags & FOCUSABLE_AUTO) != 0) && (changed & (FOCUSABLE_MASK | CLICKABLE | FOCUSABLE_IN_TOUCH_MODE)) != 0) { int newFocus = NOT_FOCUSABLE; @@ -12144,8 +12158,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mViewFlags = (mViewFlags & ~FOCUSABLE_IN_TOUCH_MODE); } mViewFlags = (mViewFlags & ~FOCUSABLE) | newFocus; - int focusChanged = (old & FOCUSABLE) ^ (newFocus & FOCUSABLE); - changed = (changed & ~FOCUSABLE) | focusChanged; + focusableChangedByAuto = (old & FOCUSABLE) ^ (newFocus & FOCUSABLE); + changed = (changed & ~FOCUSABLE) | focusableChangedByAuto; } /* Check if the FOCUSABLE bit has changed */ @@ -12160,7 +12174,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Tell the view system that we are now available to take focus * if no one else already has it. */ - if (mParent != null) mParent.focusableViewAvailable(this); + if (mParent != null) { + ViewRootImpl viewRootImpl = getViewRootImpl(); + if (!sAutoFocusableOffUIThreadWontNotifyParents + || focusableChangedByAuto == 0 + || viewRootImpl == null + || viewRootImpl.mThread == Thread.currentThread()) { + mParent.focusableViewAvailable(this); + } + } } }