Merge "Prevent callback registration when back flag is disabled" into tm-dev

This commit is contained in:
Vadim Caen
2022-06-20 18:53:00 +00:00
committed by Android (Google) Code Review
10 changed files with 113 additions and 27 deletions

View File

@@ -1482,8 +1482,6 @@ public class Dialog implements DialogInterface, Window.Callback,
/** /**
* Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this * Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this
* dialog is attached to. * dialog is attached to.
*
* Returns null if the dialog is not attached to a window with a decor.
*/ */
@NonNull @NonNull
public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {

View File

@@ -135,6 +135,7 @@ import android.widget.FrameLayout;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.window.CompatOnBackInvokedCallback;
import android.window.ImeOnBackInvokedDispatcher; import android.window.ImeOnBackInvokedDispatcher;
import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher; import android.window.OnBackInvokedDispatcher;
@@ -350,7 +351,7 @@ public class InputMethodService extends AbstractInputMethodService {
private RingBuffer<MotionEvent> mPendingEvents; private RingBuffer<MotionEvent> mPendingEvents;
private ImeOnBackInvokedDispatcher mImeDispatcher; private ImeOnBackInvokedDispatcher mImeDispatcher;
private Boolean mBackCallbackRegistered = false; 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 * Returns whether {@link InputMethodService} is responsible for rendering the back button and

View File

@@ -196,6 +196,7 @@ import android.view.contentcapture.MainContentCaptureSession;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller; import android.widget.Scroller;
import android.window.ClientWindowFrames; import android.window.ClientWindowFrames;
import android.window.CompatOnBackInvokedCallback;
import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher; import android.window.OnBackInvokedDispatcher;
import android.window.SurfaceSyncer; import android.window.SurfaceSyncer;
@@ -339,13 +340,12 @@ public final class ViewRootImpl implements ViewParent,
/** /**
* The top level {@link OnBackInvokedDispatcher}. * The top level {@link OnBackInvokedDispatcher}.
*/ */
private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher = private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
new WindowOnBackInvokedDispatcher();
/** /**
* Compatibility {@link OnBackInvokedCallback} that dispatches KEYCODE_BACK events * Compatibility {@link OnBackInvokedCallback} that dispatches KEYCODE_BACK events
* to view root for apps using legacy back behavior. * to view root for apps using legacy back behavior.
*/ */
private OnBackInvokedCallback mCompatOnBackInvokedCallback; private CompatOnBackInvokedCallback mCompatOnBackInvokedCallback;
/** /**
* Callback for notifying about global configuration changes. * Callback for notifying about global configuration changes.
@@ -959,6 +959,8 @@ public final class ViewRootImpl implements ViewParent,
mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled(); mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled();
mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS; mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
mOnBackInvokedDispatcher = new WindowOnBackInvokedDispatcher(
context.getApplicationInfo().isOnBackInvokedCallbackEnabled());
} }
public static void addFirstDrawHandler(Runnable callback) { public static void addFirstDrawHandler(Runnable callback) {
@@ -10836,13 +10838,6 @@ public final class ViewRootImpl implements ViewParent,
OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatOnBackInvokedCallback); OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatOnBackInvokedCallback);
} }
private void unregisterCompatOnBackInvokedCallback() {
if (mCompatOnBackInvokedCallback != null) {
mOnBackInvokedDispatcher.unregisterOnBackInvokedCallback(mCompatOnBackInvokedCallback);
mCompatOnBackInvokedCallback = null;
}
}
@Override @Override
public void setTouchableRegion(Region r) { public void setTouchableRegion(Region r) {
if (r != null) { if (r != null) {

View File

@@ -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();
}

View File

@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.window.WindowOnBackInvokedDispatcher.Checker;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -50,6 +51,11 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
private final Object mLock = new Object(); private final Object mLock = new Object();
private OnBackInvokedDispatcher mActualDispatcher = null; private OnBackInvokedDispatcher mActualDispatcher = null;
private ImeOnBackInvokedDispatcher mImeDispatcher; private ImeOnBackInvokedDispatcher mImeDispatcher;
private final Checker mChecker;
public ProxyOnBackInvokedDispatcher(boolean applicationCallBackEnabled) {
mChecker = new Checker(applicationCallBackEnabled);
}
@Override @Override
public void registerOnBackInvokedCallback( public void registerOnBackInvokedCallback(
@@ -58,11 +64,9 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
Log.v(TAG, String.format("Proxy register %s. mActualDispatcher=%s", callback, Log.v(TAG, String.format("Proxy register %s. mActualDispatcher=%s", callback,
mActualDispatcher)); mActualDispatcher));
} }
if (priority < 0) { if (mChecker.checkApplicationCallbackRegistration(priority, callback)) {
throw new IllegalArgumentException("Application registered OnBackInvokedCallback " registerOnBackInvokedCallbackUnchecked(callback, priority);
+ "cannot have negative priority. Priority: " + priority);
} }
registerOnBackInvokedCallbackUnchecked(callback, priority);
} }
@Override @Override

View File

@@ -30,6 +30,7 @@ import android.view.IWindowSession;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Objects;
import java.util.TreeMap; import java.util.TreeMap;
/** /**
@@ -62,6 +63,11 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
/** Holds all callbacks by priorities. */ /** Holds all callbacks by priorities. */
private final TreeMap<Integer, ArrayList<OnBackInvokedCallback>> private final TreeMap<Integer, ArrayList<OnBackInvokedCallback>>
mOnBackInvokedCallbacks = new TreeMap<>(); 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 * Sends the pending top callback (if one exists) to WM when the view root
@@ -86,14 +92,16 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
@Override @Override
public void registerOnBackInvokedCallback( public void registerOnBackInvokedCallback(
@Priority int priority, @NonNull OnBackInvokedCallback callback) { @Priority int priority, @NonNull OnBackInvokedCallback callback) {
if (priority < 0) { if (mChecker.checkApplicationCallbackRegistration(priority, callback)) {
throw new IllegalArgumentException("Application registered OnBackInvokedCallback " registerOnBackInvokedCallbackUnchecked(callback, priority);
+ "cannot have negative priority. Priority: " + 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) { @NonNull OnBackInvokedCallback callback, @Priority int priority) {
if (mImeDispatcher != null) { if (mImeDispatcher != null) {
mImeDispatcher.registerOnBackInvokedCallback(priority, callback); mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
@@ -203,6 +211,14 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
return null; 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 { static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
private final WeakReference<OnBackInvokedCallback> mCallback; private final WeakReference<OnBackInvokedCallback> mCallback;
@@ -289,4 +305,41 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) { @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
mImeDispatcher = 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;
}
}
} }

View File

@@ -342,8 +342,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
boolean mDecorFitsSystemWindows = true; boolean mDecorFitsSystemWindows = true;
private final ProxyOnBackInvokedDispatcher mProxyOnBackInvokedDispatcher = private final ProxyOnBackInvokedDispatcher mProxyOnBackInvokedDispatcher;
new ProxyOnBackInvokedDispatcher();
static class WindowManagerHolder { static class WindowManagerHolder {
static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface( static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
@@ -358,6 +357,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mLayoutInflater = LayoutInflater.from(context); mLayoutInflater = LayoutInflater.from(context);
mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(), mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(),
DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0; DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0;
mProxyOnBackInvokedDispatcher = new ProxyOnBackInvokedDispatcher(
context.getApplicationInfo().isOnBackInvokedCallbackEnabled());
} }
/** /**

View File

@@ -161,7 +161,10 @@
<!-- ChooserActivityTest permissions--> <!-- ChooserActivityTest permissions-->
<uses-permission android:name="android.permission.SET_CLIP_SOURCE" /> <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="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" /> <uses-library android:name="org.apache.http.legacy" android:required="false" />
<meta-data <meta-data

View File

@@ -64,7 +64,7 @@ public class WindowOnBackInvokedDispatcherTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mDispatcher = new WindowOnBackInvokedDispatcher(); mDispatcher = new WindowOnBackInvokedDispatcher(true /* applicationCallbackEnabled */);
mDispatcher.attachToWindow(mWindowSession, mWindow); mDispatcher.attachToWindow(mWindowSession, mWindow);
} }

View File

@@ -181,7 +181,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
Task task = createTopTaskWithActivity(); Task task = createTopTaskWithActivity();
WindowState appWindow = task.getTopVisibleAppMainWindow(); WindowState appWindow = task.getTopVisibleAppMainWindow();
WindowOnBackInvokedDispatcher dispatcher = new WindowOnBackInvokedDispatcher(); WindowOnBackInvokedDispatcher dispatcher =
new WindowOnBackInvokedDispatcher(true /* applicationCallbackEnabled */);
doAnswer(invocation -> { doAnswer(invocation -> {
appWindow.setOnBackInvokedCallbackInfo(invocation.getArgument(1)); appWindow.setOnBackInvokedCallbackInfo(invocation.getArgument(1));
return null; return null;