diff options
author | Vadim Caen <caen@google.com> | 2022-03-30 16:52:30 +0200 |
---|---|---|
committer | Vadim Caen <caen@google.com> | 2022-06-20 11:40:18 +0200 |
commit | a2e45f1ed1a3f74ca413f5d4ef815d50f7399c26 (patch) | |
tree | 65d4e6236e153f806081911de1572e0acb94b48e | |
parent | 41bcf6803e641463a0fc850f4463bd314df25e8a (diff) | |
download | base-a2e45f1ed1a3f74ca413f5d4ef815d50f7399c26.tar.gz |
Prevent callback registration when back flag is disabled
When the enableOnBackInvokedCallback is set to false (or not set),
registering an OnBackInvokedCallback should be a no-op to avoid
overriding the default compat callback.
Test: Manual testing registering a callback on an app with the flag
disabled and doing a back gesture. Currently we don't have test
executing a back gesture so automated tests are not possible
Bug: 235206960
Change-Id: I54d843f11130a78ed5a68cbe4722e601a2086ee1
Merged-In: I54d843f11130a78ed5a68cbe4722e601a2086ee1
(cherry picked from commit aa48dc3c2db92189055804878b88a51f0cf8e955)
10 files changed, 113 insertions, 27 deletions
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 33cf71256d51..de0f7522f36d 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -1482,8 +1482,6 @@ public class Dialog implements DialogInterface, Window.Callback, /** * Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this * dialog is attached to. - * - * Returns null if the dialog is not attached to a window with a decor. */ @NonNull public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 334a659d7f23..8e67705c5cf0 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -135,6 +135,7 @@ import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; +import android.window.CompatOnBackInvokedCallback; import android.window.ImeOnBackInvokedDispatcher; import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedDispatcher; @@ -350,7 +351,7 @@ public class InputMethodService extends AbstractInputMethodService { private RingBuffer<MotionEvent> mPendingEvents; private ImeOnBackInvokedDispatcher mImeDispatcher; private Boolean mBackCallbackRegistered = false; - private final OnBackInvokedCallback mCompatBackCallback = this::compatHandleBack; + private final CompatOnBackInvokedCallback mCompatBackCallback = this::compatHandleBack; /** * Returns whether {@link InputMethodService} is responsible for rendering the back button and diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 97e0692a3f3c..e46e44b4115b 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -196,6 +196,7 @@ import android.view.contentcapture.MainContentCaptureSession; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; import android.window.ClientWindowFrames; +import android.window.CompatOnBackInvokedCallback; import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedDispatcher; import android.window.SurfaceSyncer; @@ -339,13 +340,12 @@ public final class ViewRootImpl implements ViewParent, /** * The top level {@link OnBackInvokedDispatcher}. */ - private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher = - new WindowOnBackInvokedDispatcher(); + private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher; /** * Compatibility {@link OnBackInvokedCallback} that dispatches KEYCODE_BACK events * to view root for apps using legacy back behavior. */ - private OnBackInvokedCallback mCompatOnBackInvokedCallback; + private CompatOnBackInvokedCallback mCompatOnBackInvokedCallback; /** * Callback for notifying about global configuration changes. @@ -959,6 +959,8 @@ public final class ViewRootImpl implements ViewParent, mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled(); mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS; + mOnBackInvokedDispatcher = new WindowOnBackInvokedDispatcher( + context.getApplicationInfo().isOnBackInvokedCallbackEnabled()); } public static void addFirstDrawHandler(Runnable callback) { @@ -10836,13 +10838,6 @@ public final class ViewRootImpl implements ViewParent, OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatOnBackInvokedCallback); } - private void unregisterCompatOnBackInvokedCallback() { - if (mCompatOnBackInvokedCallback != null) { - mOnBackInvokedDispatcher.unregisterOnBackInvokedCallback(mCompatOnBackInvokedCallback); - mCompatOnBackInvokedCallback = null; - } - } - @Override public void setTouchableRegion(Region r) { if (r != null) { diff --git a/core/java/android/window/CompatOnBackInvokedCallback.java b/core/java/android/window/CompatOnBackInvokedCallback.java new file mode 100644 index 000000000000..81d7291d9f88 --- /dev/null +++ b/core/java/android/window/CompatOnBackInvokedCallback.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +/** + * Marker interface for {@link OnBackInvokedCallback} used for backward compatibility between the + * new system back and the old back event dispatching. Callbacks implementing this interface are + * allowed to be registered even if <code>enableOnbackInvoked</code> is set to false in the + * application manifest. + * @hide + */ +public interface CompatOnBackInvokedCallback extends OnBackInvokedCallback{ + + @Override + void onBackInvoked(); +} diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java index 10d43e89a09d..8ad109317f4b 100644 --- a/core/java/android/window/ProxyOnBackInvokedDispatcher.java +++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; import android.util.Pair; +import android.window.WindowOnBackInvokedDispatcher.Checker; import java.util.ArrayList; import java.util.List; @@ -50,6 +51,11 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { private final Object mLock = new Object(); private OnBackInvokedDispatcher mActualDispatcher = null; private ImeOnBackInvokedDispatcher mImeDispatcher; + private final Checker mChecker; + + public ProxyOnBackInvokedDispatcher(boolean applicationCallBackEnabled) { + mChecker = new Checker(applicationCallBackEnabled); + } @Override public void registerOnBackInvokedCallback( @@ -58,11 +64,9 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { Log.v(TAG, String.format("Proxy register %s. mActualDispatcher=%s", callback, mActualDispatcher)); } - if (priority < 0) { - throw new IllegalArgumentException("Application registered OnBackInvokedCallback " - + "cannot have negative priority. Priority: " + priority); + if (mChecker.checkApplicationCallbackRegistration(priority, callback)) { + registerOnBackInvokedCallbackUnchecked(callback, priority); } - registerOnBackInvokedCallbackUnchecked(callback, priority); } @Override diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 1d0bc5a6f0ba..d147524d3b3d 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -30,6 +30,7 @@ import android.view.IWindowSession; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; +import java.util.Objects; import java.util.TreeMap; /** @@ -62,6 +63,11 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { /** Holds all callbacks by priorities. */ private final TreeMap<Integer, ArrayList<OnBackInvokedCallback>> mOnBackInvokedCallbacks = new TreeMap<>(); + private final Checker mChecker; + + public WindowOnBackInvokedDispatcher(boolean applicationCallBackEnabled) { + mChecker = new Checker(applicationCallBackEnabled); + } /** * Sends the pending top callback (if one exists) to WM when the view root @@ -86,14 +92,16 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { @Override public void registerOnBackInvokedCallback( @Priority int priority, @NonNull OnBackInvokedCallback callback) { - if (priority < 0) { - throw new IllegalArgumentException("Application registered OnBackInvokedCallback " - + "cannot have negative priority. Priority: " + priority); + if (mChecker.checkApplicationCallbackRegistration(priority, callback)) { + registerOnBackInvokedCallbackUnchecked(callback, priority); } - registerOnBackInvokedCallbackUnchecked(callback, priority); } - private void registerOnBackInvokedCallbackUnchecked( + /** + * Register a callback bypassing platform checks. This is used to register compatibility + * callbacks. + */ + public void registerOnBackInvokedCallbackUnchecked( @NonNull OnBackInvokedCallback callback, @Priority int priority) { if (mImeDispatcher != null) { mImeDispatcher.registerOnBackInvokedCallback(priority, callback); @@ -203,6 +211,14 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { return null; } + /** + * Returns the checker used to check whether a callback can be registered + */ + @NonNull + public Checker getChecker() { + return mChecker; + } + static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub { private final WeakReference<OnBackInvokedCallback> mCallback; @@ -289,4 +305,41 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { mImeDispatcher = imeDispatcher; } + + + /** + * Class used to check whether a callback can be registered or not. This is meant to be + * shared with {@link ProxyOnBackInvokedDispatcher} which needs to do the same checks. + */ + public static class Checker { + + private final boolean mApplicationCallBackEnabled; + + public Checker(boolean applicationCallBackEnabled) { + mApplicationCallBackEnabled = applicationCallBackEnabled; + } + + /** + * Checks whether the given callback can be registered with the given priority. + * @return true if the callback can be added. + * @throws IllegalArgumentException if the priority is negative. + */ + public boolean checkApplicationCallbackRegistration(int priority, + OnBackInvokedCallback callback) { + if (!mApplicationCallBackEnabled + && !(callback instanceof CompatOnBackInvokedCallback)) { + Log.w("OnBackInvokedCallback", + "OnBackInvokedCallback is not enabled for the application." + + "\nSet 'android:enableOnBackInvokedCallback=\"true\"' in the" + + " application manifest."); + return false; + } + if (priority < 0) { + throw new IllegalArgumentException("Application registered OnBackInvokedCallback " + + "cannot have negative priority. Priority: " + priority); + } + Objects.requireNonNull(callback); + return true; + } + } } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index f727d80bba12..9c0fad902a52 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -342,8 +342,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { boolean mDecorFitsSystemWindows = true; - private final ProxyOnBackInvokedDispatcher mProxyOnBackInvokedDispatcher = - new ProxyOnBackInvokedDispatcher(); + private final ProxyOnBackInvokedDispatcher mProxyOnBackInvokedDispatcher; static class WindowManagerHolder { static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface( @@ -358,6 +357,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mLayoutInflater = LayoutInflater.from(context); mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(), DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0; + mProxyOnBackInvokedDispatcher = new ProxyOnBackInvokedDispatcher( + context.getApplicationInfo().isOnBackInvokedCallbackEnabled()); } /** diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 04857ec756d2..3e4b1cc87ef8 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -161,7 +161,10 @@ <!-- ChooserActivityTest permissions--> <uses-permission android:name="android.permission.SET_CLIP_SOURCE" /> - <application android:theme="@style/Theme" android:supportsRtl="true"> + <application + android:theme="@style/Theme" + android:supportsRtl="true" + android:enableOnBackInvokedCallback="true"> <uses-library android:name="android.test.runner" /> <uses-library android:name="org.apache.http.legacy" android:required="false" /> <meta-data diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index c57aa740124c..f448cb3091e7 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -64,7 +64,7 @@ public class WindowOnBackInvokedDispatcherTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mDispatcher = new WindowOnBackInvokedDispatcher(); + mDispatcher = new WindowOnBackInvokedDispatcher(true /* applicationCallbackEnabled */); mDispatcher.attachToWindow(mWindowSession, mWindow); } diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index 2c1c38f3bee8..fc41a94d3355 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -181,7 +181,8 @@ public class BackNavigationControllerTests extends WindowTestsBase { Task task = createTopTaskWithActivity(); WindowState appWindow = task.getTopVisibleAppMainWindow(); - WindowOnBackInvokedDispatcher dispatcher = new WindowOnBackInvokedDispatcher(); + WindowOnBackInvokedDispatcher dispatcher = + new WindowOnBackInvokedDispatcher(true /* applicationCallbackEnabled */); doAnswer(invocation -> { appWindow.setOnBackInvokedCallbackInfo(invocation.getArgument(1)); return null; |