diff options
Diffstat (limited to 'libs')
46 files changed, 917 insertions, 505 deletions
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 856c9c2484f3..96e0559b0df6 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -122,6 +122,7 @@ android_library { "kotlinx-coroutines-android", "kotlinx-coroutines-core", "iconloader_base", + "jsr330", "protolog-lib", "SettingsLib", "WindowManager-Shell-proto", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java index ada7e1a9e3ab..45948dd9e800 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java @@ -19,6 +19,7 @@ package com.android.wm.shell; import android.view.Gravity; import com.android.wm.shell.apppairs.AppPairs; +import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.letterbox.LetterboxConfigController; import com.android.wm.shell.onehanded.OneHanded; @@ -61,6 +62,7 @@ public final class ShellCommandHandler { } /** Dumps WM Shell internal state. */ + @ExternalThread public void dump(PrintWriter pw) { mShellTaskOrganizer.dump(pw, ""); pw.println(); @@ -76,6 +78,7 @@ public final class ShellCommandHandler { /** Returns {@code true} if command was found and executed. */ + @ExternalThread public boolean handleCommand(String[] args, PrintWriter pw) { if (args.length < 2) { // Argument at position 0 is "WMShell". diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java index 118f189866eb..94555de4f05c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java @@ -21,6 +21,7 @@ import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_LETTERB import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.letterbox.LetterboxTaskListener; import com.android.wm.shell.splitscreen.SplitScreen; @@ -56,6 +57,7 @@ public class ShellInit { mFullscreenTaskListener = fullscreenTaskListener; } + @ExternalThread public void init() { // Start listening for display changes mDisplayImeController.startMonitorDisplays(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index beb9690b5cbc..174c16afc75f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -31,12 +31,14 @@ import android.os.Binder; import android.os.IBinder; import android.util.ArrayMap; import android.util.Log; +import android.util.Slog; import android.util.SparseArray; import android.view.SurfaceControl; import android.window.ITaskOrganizerController; import android.window.TaskAppearedInfo; import android.window.TaskOrganizer; +import androidx.annotation.BinderThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -45,6 +47,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.startingsurface.StartingSurfaceDrawer; import java.io.PrintWriter; @@ -119,7 +122,7 @@ public class ShellTaskOrganizer extends TaskOrganizer { ShellExecutor mainExecutor, ShellExecutor animExecutor, Context context) { super(taskOrganizerController, mainExecutor); mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor); - if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions); + if (Transitions.ENABLE_SHELL_TRANSITIONS) mTransitions.register(this); // TODO(b/131727939) temporarily live here, the starting surface drawer should be controlled // by a controller, that class should be create while porting // ActivityRecord#addStartingWindow to WMShell. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java index 120039de1240..10195b6a26b2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java @@ -23,9 +23,9 @@ import static android.window.TransitionInfo.TRANSIT_SHOW; import android.animation.Animator; import android.animation.ValueAnimator; -import android.annotation.MainThread; import android.annotation.NonNull; import android.os.IBinder; +import android.os.RemoteException; import android.os.SystemProperties; import android.util.ArrayMap; import android.util.Slog; @@ -35,15 +35,18 @@ import android.window.ITransitionPlayer; import android.window.TransitionInfo; import android.window.WindowOrganizer; +import androidx.annotation.BinderThread; + import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.util.ArrayList; /** Plays transition animations */ -public class Transitions extends ITransitionPlayer.Stub { +public class Transitions { private static final String TAG = "ShellTransitions"; /** Set to {@code true} to enable shell transitions. */ @@ -54,6 +57,7 @@ public class Transitions extends ITransitionPlayer.Stub { private final TransactionPool mTransactionPool; private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; + private final TransitionPlayerImpl mPlayerImpl; /** Keeps track of currently tracked transitions and all the animations associated with each */ private final ArrayMap<IBinder, ArrayList<Animator>> mActiveTransitions = new ArrayMap<>(); @@ -64,6 +68,11 @@ public class Transitions extends ITransitionPlayer.Stub { mTransactionPool = pool; mMainExecutor = mainExecutor; mAnimExecutor = animExecutor; + mPlayerImpl = new TransitionPlayerImpl(); + } + + public void register(ShellTaskOrganizer taskOrganizer) { + taskOrganizer.registerTransitionPlayer(mPlayerImpl); } // TODO(shell-transitions): real animations @@ -115,77 +124,73 @@ public class Transitions extends ITransitionPlayer.Stub { || type == WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; } - @Override - public void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, + private void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s", transitionToken, info); // start task - mMainExecutor.execute(() -> { - if (!mActiveTransitions.containsKey(transitionToken)) { - Slog.e(TAG, "Got transitionReady for non-active transition " + transitionToken - + " expecting one of " + mActiveTransitions.keySet()); - } - if (mActiveTransitions.get(transitionToken) != null) { - throw new IllegalStateException("Got a duplicate onTransitionReady call for " - + transitionToken); - } - mActiveTransitions.put(transitionToken, new ArrayList<>()); - boolean isOpening = isOpeningType(info.getType()); - if (info.getRootLeash().isValid()) { - t.show(info.getRootLeash()); - } - // changes should be ordered top-to-bottom in z - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final SurfaceControl leash = change.getLeash(); - final int mode = info.getChanges().get(i).getMode(); - - // Don't animate anything with an animating parent - if (change.getParent() != null) { - if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) { - t.show(leash); - t.setMatrix(leash, 1, 0, 0, 1); - } - continue; - } - - t.reparent(leash, info.getRootLeash()); - t.setPosition(leash, change.getEndAbsBounds().left - info.getRootOffset().x, - change.getEndAbsBounds().top - info.getRootOffset().y); - // Put all the OPEN/SHOW on top + if (!mActiveTransitions.containsKey(transitionToken)) { + Slog.e(TAG, "Got transitionReady for non-active transition " + transitionToken + + " expecting one of " + mActiveTransitions.keySet()); + } + if (mActiveTransitions.get(transitionToken) != null) { + throw new IllegalStateException("Got a duplicate onTransitionReady call for " + + transitionToken); + } + mActiveTransitions.put(transitionToken, new ArrayList<>()); + boolean isOpening = isOpeningType(info.getType()); + if (info.getRootLeash().isValid()) { + t.show(info.getRootLeash()); + } + // changes should be ordered top-to-bottom in z + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + final SurfaceControl leash = change.getLeash(); + final int mode = info.getChanges().get(i).getMode(); + + // Don't animate anything with an animating parent + if (change.getParent() != null) { if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) { t.show(leash); t.setMatrix(leash, 1, 0, 0, 1); - if (isOpening) { - // put on top and fade in - t.setLayer(leash, info.getChanges().size() - i); - t.setAlpha(leash, 0.f); - startExampleAnimation(transitionToken, leash, true /* show */); - } else { - // put on bottom and leave it visible without fade - t.setLayer(leash, -i); - t.setAlpha(leash, 1.f); - } - } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_HIDE) { - if (isOpening) { - // put on bottom and leave visible without fade - t.setLayer(leash, -i); - } else { - // put on top and fade out - t.setLayer(leash, info.getChanges().size() - i); - startExampleAnimation(transitionToken, leash, false /* show */); - } + } + continue; + } + + t.reparent(leash, info.getRootLeash()); + t.setPosition(leash, change.getEndAbsBounds().left - info.getRootOffset().x, + change.getEndAbsBounds().top - info.getRootOffset().y); + // Put all the OPEN/SHOW on top + if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) { + t.show(leash); + t.setMatrix(leash, 1, 0, 0, 1); + if (isOpening) { + // put on top and fade in + t.setLayer(leash, info.getChanges().size() - i); + t.setAlpha(leash, 0.f); + startExampleAnimation(transitionToken, leash, true /* show */); } else { + // put on bottom and leave it visible without fade + t.setLayer(leash, -i); + t.setAlpha(leash, 1.f); + } + } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_HIDE) { + if (isOpening) { + // put on bottom and leave visible without fade + t.setLayer(leash, -i); + } else { + // put on top and fade out t.setLayer(leash, info.getChanges().size() - i); + startExampleAnimation(transitionToken, leash, false /* show */); } + } else { + t.setLayer(leash, info.getChanges().size() - i); } - t.apply(); - onFinish(transitionToken); - }); + } + t.apply(); + onFinish(transitionToken); } - @MainThread private void onFinish(IBinder transition) { if (!mActiveTransitions.get(transition).isEmpty()) return; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, @@ -194,16 +199,32 @@ public class Transitions extends ITransitionPlayer.Stub { mOrganizer.finishTransition(transition, null, null); } - @Override - public void requestStartTransition(int type, @NonNull IBinder transitionToken) { + private void requestStartTransition(int type, @NonNull IBinder transitionToken) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: type=%d %s", type, transitionToken); - mMainExecutor.execute(() -> { - if (mActiveTransitions.containsKey(transitionToken)) { - throw new RuntimeException("Transition already started " + transitionToken); - } - IBinder transition = mOrganizer.startTransition(type, transitionToken, null /* wct */); - mActiveTransitions.put(transition, null); - }); + + if (mActiveTransitions.containsKey(transitionToken)) { + throw new RuntimeException("Transition already started " + transitionToken); + } + IBinder transition = mOrganizer.startTransition(type, transitionToken, null /* wct */); + mActiveTransitions.put(transition, null); + } + + @BinderThread + private class TransitionPlayerImpl extends ITransitionPlayer.Stub { + @Override + public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo, + SurfaceControl.Transaction transaction) throws RemoteException { + mMainExecutor.execute(() -> { + Transitions.this.onTransitionReady(iBinder, transitionInfo, transaction); + }); + } + + @Override + public void requestStartTransition(int i, IBinder iBinder) throws RemoteException { + mMainExecutor.execute(() -> { + Transitions.this.requestStartTransition(i, iBinder); + }); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java index acb9a5dae78c..834de3f15b1d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java @@ -18,11 +18,11 @@ package com.android.wm.shell; import static android.view.Display.DEFAULT_DISPLAY; -import android.app.WindowConfiguration; import android.os.RemoteException; -import android.view.WindowManagerGlobal; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PinnedStackListenerForwarder; +import com.android.wm.shell.pip.PinnedStackListenerForwarder.PinnedStackListener; /** * The singleton wrapper to communicate between WindowManagerService and WMShell features @@ -31,32 +31,30 @@ import com.android.wm.shell.pip.PinnedStackListenerForwarder; public class WindowManagerShellWrapper { private static final String TAG = WindowManagerShellWrapper.class.getSimpleName(); - public static final int WINDOWING_MODE_PINNED = WindowConfiguration.WINDOWING_MODE_PINNED; - /** * Forwarder to which we can add multiple pinned stack listeners. Each listener will receive * updates from the window manager service. */ - private PinnedStackListenerForwarder mPinnedStackListenerForwarder = - new PinnedStackListenerForwarder(); + private final PinnedStackListenerForwarder mPinnedStackListenerForwarder; + + public WindowManagerShellWrapper(ShellExecutor shellMainExecutor) { + mPinnedStackListenerForwarder = new PinnedStackListenerForwarder(shellMainExecutor); + } /** * Adds a pinned stack listener, which will receive updates from the window manager service * along with any other pinned stack listeners that were added via this method. */ - public void addPinnedStackListener(PinnedStackListenerForwarder.PinnedStackListener listener) - throws - RemoteException { + public void addPinnedStackListener(PinnedStackListener listener) + throws RemoteException { mPinnedStackListenerForwarder.addListener(listener); - WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener( - DEFAULT_DISPLAY, mPinnedStackListenerForwarder); + mPinnedStackListenerForwarder.register(DEFAULT_DISPLAY); } /** * Removes a pinned stack listener. */ - public void removePinnedStackListener( - PinnedStackListenerForwarder.PinnedStackListener listener) { + public void removePinnedStackListener(PinnedStackListener listener) { mPinnedStackListenerForwarder.removeListener(listener); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java index ef3e3e0220e7..f5aa852c87ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java @@ -20,11 +20,14 @@ import android.app.ActivityManager; import androidx.annotation.NonNull; +import com.android.wm.shell.common.annotations.ExternalThread; + import java.io.PrintWriter; /** * Interface to engage app pairs feature. */ +@ExternalThread public interface AppPairs { /** Pairs indicated tasks. */ boolean pair(int task1, int task2); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index aa7355b61eda..40b41e11c8aa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1213,7 +1213,7 @@ public class BubbleController implements Bubbles { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { if (mStackView != null) { - mStackView.post(() -> mStackView.onImeVisibilityChanged(imeVisible, imeHeight)); + mStackView.onImeVisibilityChanged(imeVisible, imeHeight); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index 92d15c5feaca..fa5ac449cd54 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -30,6 +30,8 @@ import android.view.View; import androidx.annotation.IntDef; import androidx.annotation.Nullable; +import com.android.wm.shell.common.annotations.ExternalThread; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -40,6 +42,7 @@ import java.util.function.IntConsumer; /** * Interface to engage bubbles feature. */ +@ExternalThread public interface Bubbles { @Retention(SOURCE) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/AnimationThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/AnimationThread.java deleted file mode 100644 index 96b9f86673fc..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/AnimationThread.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2020 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 com.android.wm.shell.common; - -import static android.os.Process.THREAD_PRIORITY_DISPLAY; - -import android.annotation.NonNull; -import android.os.HandlerThread; -import android.util.Singleton; - -/** - * A singleton thread for Shell to run animations on. - */ -public class AnimationThread extends HandlerThread { - private ShellExecutor mExecutor; - - private AnimationThread() { - super("wmshell.anim", THREAD_PRIORITY_DISPLAY); - } - - /** Get the singleton instance of this thread */ - public static AnimationThread instance() { - return sAnimationThreadSingleton.get(); - } - - /** - * @return a shared {@link ShellExecutor} associated with this thread - * @hide - */ - @NonNull - public ShellExecutor getExecutor() { - if (mExecutor == null) { - mExecutor = new HandlerExecutor(getThreadHandler()); - } - return mExecutor; - } - - private static final Singleton<AnimationThread> sAnimationThreadSingleton = - new Singleton<AnimationThread>() { - @Override - protected AnimationThread create() { - final AnimationThread animThread = new AnimationThread(); - animThread.start(); - return animThread; - } - }; -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java index 3263f79888d6..cb4584c41184 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java @@ -23,6 +23,10 @@ import android.view.IDisplayWindowRotationController; import android.view.IWindowManager; import android.window.WindowContainerTransaction; +import androidx.annotation.BinderThread; + +import com.android.wm.shell.common.annotations.ShellMainThread; + import java.util.ArrayList; /** @@ -35,39 +39,18 @@ public class DisplayChangeController { private final Handler mHandler; private final IWindowManager mWmService; + private final IDisplayWindowRotationController mControllerImpl; private final ArrayList<OnDisplayChangingListener> mRotationListener = new ArrayList<>(); private final ArrayList<OnDisplayChangingListener> mTmpListeners = new ArrayList<>(); - private final IDisplayWindowRotationController mDisplayRotationController = - new IDisplayWindowRotationController.Stub() { - @Override - public void onRotateDisplay(int displayId, final int fromRotation, - final int toRotation, IDisplayWindowRotationCallback callback) { - mHandler.post(() -> { - WindowContainerTransaction t = new WindowContainerTransaction(); - synchronized (mRotationListener) { - mTmpListeners.clear(); - // Make a local copy in case the handlers add/remove themselves. - mTmpListeners.addAll(mRotationListener); - } - for (OnDisplayChangingListener c : mTmpListeners) { - c.onRotateDisplay(displayId, fromRotation, toRotation, t); - } - try { - callback.continueRotateDisplay(toRotation, t); - } catch (RemoteException e) { - } - }); - } - }; - public DisplayChangeController(Handler mainHandler, IWindowManager wmService) { mHandler = mainHandler; mWmService = wmService; + mControllerImpl = new DisplayWindowRotationControllerImpl(); try { - mWmService.setDisplayWindowRotationController(mDisplayRotationController); + mWmService.setDisplayWindowRotationController(mControllerImpl); } catch (RemoteException e) { throw new RuntimeException("Unable to register rotation controller"); } @@ -91,10 +74,41 @@ public class DisplayChangeController { } } + private void onRotateDisplay(int displayId, final int fromRotation, final int toRotation, + IDisplayWindowRotationCallback callback) { + WindowContainerTransaction t = new WindowContainerTransaction(); + synchronized (mRotationListener) { + mTmpListeners.clear(); + // Make a local copy in case the handlers add/remove themselves. + mTmpListeners.addAll(mRotationListener); + } + for (OnDisplayChangingListener c : mTmpListeners) { + c.onRotateDisplay(displayId, fromRotation, toRotation, t); + } + try { + callback.continueRotateDisplay(toRotation, t); + } catch (RemoteException e) { + } + } + + @BinderThread + private class DisplayWindowRotationControllerImpl + extends IDisplayWindowRotationController.Stub { + @Override + public void onRotateDisplay(int displayId, final int fromRotation, + final int toRotation, IDisplayWindowRotationCallback callback) { + mHandler.post(() -> { + DisplayChangeController.this.onRotateDisplay(displayId, fromRotation, toRotation, + callback); + }); + } + } + /** * Give a listener a chance to queue up configuration changes to execute as part of a * display rotation. The contents of {@link #onRotateDisplay} must run synchronously. */ + @ShellMainThread public interface OnDisplayChangingListener { /** * Called before the display is rotated. Contents of this method must run synchronously. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java index 418973204add..a413c052cb6c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java @@ -28,7 +28,10 @@ import android.view.Display; import android.view.IDisplayWindowListener; import android.view.IWindowManager; +import androidx.annotation.BinderThread; + import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener; +import com.android.wm.shell.common.annotations.ShellMainThread; import java.util.ArrayList; @@ -45,6 +48,7 @@ public class DisplayController { private final Context mContext; private final IWindowManager mWmService; private final DisplayChangeController mChangeController; + private final IDisplayWindowListener mDisplayContainerListener; private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>(); private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>(); @@ -57,119 +61,13 @@ public class DisplayController { return displayManager.getDisplay(displayId); } - private final IDisplayWindowListener mDisplayContainerListener = - new IDisplayWindowListener.Stub() { - @Override - public void onDisplayAdded(int displayId) { - mHandler.post(() -> { - synchronized (mDisplays) { - if (mDisplays.get(displayId) != null) { - return; - } - Display display = getDisplay(displayId); - if (display == null) { - // It's likely that the display is private to some app and thus not - // accessible by system-ui. - return; - } - DisplayRecord record = new DisplayRecord(); - record.mDisplayId = displayId; - record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext - : mContext.createDisplayContext(display); - record.mDisplayLayout = new DisplayLayout(record.mContext, display); - mDisplays.put(displayId, record); - for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { - mDisplayChangedListeners.get(i).onDisplayAdded(displayId); - } - } - }); - } - - @Override - public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { - mHandler.post(() -> { - synchronized (mDisplays) { - DisplayRecord dr = mDisplays.get(displayId); - if (dr == null) { - Slog.w(TAG, "Skipping Display Configuration change on non-added" - + " display."); - return; - } - Display display = getDisplay(displayId); - if (display == null) { - Slog.w(TAG, "Skipping Display Configuration change on invalid" - + " display. It may have been removed."); - return; - } - Context perDisplayContext = mContext; - if (displayId != Display.DEFAULT_DISPLAY) { - perDisplayContext = mContext.createDisplayContext(display); - } - dr.mContext = perDisplayContext.createConfigurationContext(newConfig); - dr.mDisplayLayout = new DisplayLayout(dr.mContext, display); - for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { - mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( - displayId, newConfig); - } - } - }); - } - - @Override - public void onDisplayRemoved(int displayId) { - mHandler.post(() -> { - synchronized (mDisplays) { - if (mDisplays.get(displayId) == null) { - return; - } - for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { - mDisplayChangedListeners.get(i).onDisplayRemoved(displayId); - } - mDisplays.remove(displayId); - } - }); - } - - @Override - public void onFixedRotationStarted(int displayId, int newRotation) { - mHandler.post(() -> { - synchronized (mDisplays) { - if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { - Slog.w(TAG, "Skipping onFixedRotationStarted on unknown" - + " display, displayId=" + displayId); - return; - } - for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { - mDisplayChangedListeners.get(i).onFixedRotationStarted( - displayId, newRotation); - } - } - }); - } - - @Override - public void onFixedRotationFinished(int displayId) { - mHandler.post(() -> { - synchronized (mDisplays) { - if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { - Slog.w(TAG, "Skipping onFixedRotationFinished on unknown" - + " display, displayId=" + displayId); - return; - } - for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { - mDisplayChangedListeners.get(i).onFixedRotationFinished(displayId); - } - } - }); - } - }; - public DisplayController(Context context, Handler handler, IWindowManager wmService) { mHandler = handler; mContext = context; mWmService = wmService; mChangeController = new DisplayChangeController(mHandler, mWmService); + mDisplayContainerListener = new DisplayWindowListenerImpl(); try { mWmService.registerDisplayWindowListener(mDisplayContainerListener); } catch (RemoteException e) { @@ -232,18 +130,146 @@ public class DisplayController { mChangeController.removeRotationListener(controller); } + private void onDisplayAdded(int displayId) { + synchronized (mDisplays) { + if (mDisplays.get(displayId) != null) { + return; + } + Display display = getDisplay(displayId); + if (display == null) { + // It's likely that the display is private to some app and thus not + // accessible by system-ui. + return; + } + DisplayRecord record = new DisplayRecord(); + record.mDisplayId = displayId; + record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext + : mContext.createDisplayContext(display); + record.mDisplayLayout = new DisplayLayout(record.mContext, display); + mDisplays.put(displayId, record); + for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { + mDisplayChangedListeners.get(i).onDisplayAdded(displayId); + } + } + } + + private void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + synchronized (mDisplays) { + DisplayRecord dr = mDisplays.get(displayId); + if (dr == null) { + Slog.w(TAG, "Skipping Display Configuration change on non-added" + + " display."); + return; + } + Display display = getDisplay(displayId); + if (display == null) { + Slog.w(TAG, "Skipping Display Configuration change on invalid" + + " display. It may have been removed."); + return; + } + Context perDisplayContext = mContext; + if (displayId != Display.DEFAULT_DISPLAY) { + perDisplayContext = mContext.createDisplayContext(display); + } + dr.mContext = perDisplayContext.createConfigurationContext(newConfig); + dr.mDisplayLayout = new DisplayLayout(dr.mContext, display); + for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { + mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( + displayId, newConfig); + } + } + } + + private void onDisplayRemoved(int displayId) { + synchronized (mDisplays) { + if (mDisplays.get(displayId) == null) { + return; + } + for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { + mDisplayChangedListeners.get(i).onDisplayRemoved(displayId); + } + mDisplays.remove(displayId); + } + } + + private void onFixedRotationStarted(int displayId, int newRotation) { + synchronized (mDisplays) { + if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { + Slog.w(TAG, "Skipping onFixedRotationStarted on unknown" + + " display, displayId=" + displayId); + return; + } + for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { + mDisplayChangedListeners.get(i).onFixedRotationStarted( + displayId, newRotation); + } + } + } + + private void onFixedRotationFinished(int displayId) { + synchronized (mDisplays) { + if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { + Slog.w(TAG, "Skipping onFixedRotationFinished on unknown" + + " display, displayId=" + displayId); + return; + } + for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { + mDisplayChangedListeners.get(i).onFixedRotationFinished(displayId); + } + } + } + private static class DisplayRecord { int mDisplayId; Context mContext; DisplayLayout mDisplayLayout; } + @BinderThread + private class DisplayWindowListenerImpl extends IDisplayWindowListener.Stub { + @Override + public void onDisplayAdded(int displayId) { + mHandler.post(() -> { + DisplayController.this.onDisplayAdded(displayId); + }); + } + + @Override + public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + mHandler.post(() -> { + DisplayController.this.onDisplayConfigurationChanged(displayId, newConfig); + }); + } + + @Override + public void onDisplayRemoved(int displayId) { + mHandler.post(() -> { + DisplayController.this.onDisplayRemoved(displayId); + }); + } + + @Override + public void onFixedRotationStarted(int displayId, int newRotation) { + mHandler.post(() -> { + DisplayController.this.onFixedRotationStarted(displayId, newRotation); + }); + } + + @Override + public void onFixedRotationFinished(int displayId) { + mHandler.post(() -> { + DisplayController.this.onFixedRotationFinished(displayId); + }); + } + } + /** * Gets notified when a display is added/removed to the WM hierarchy and when a display's * window-configuration changes. * * @see IDisplayWindowListener */ + @ShellMainThread public interface OnDisplaysChangedListener { /** * Called when a display has been added to the WM hierarchy. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index ea18a19c2ee5..3fbd7ed0ec5d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -24,7 +24,6 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; -import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; @@ -40,6 +39,8 @@ import android.view.WindowInsets; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; +import androidx.annotation.BinderThread; + import com.android.internal.view.IInputMethodManager; import java.util.ArrayList; @@ -197,6 +198,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mRotation = initialRotation; } + @BinderThread @Override public void insetsChanged(InsetsState insetsState) { mExecutor.execute(() -> { @@ -204,6 +206,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged return; } + mImeShowing = insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME); + final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME); final Rect newFrame = newSource.getFrame(); final Rect oldFrame = mInsetsState.getSource(InsetsState.ITYPE_IME).getFrame(); @@ -216,6 +220,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged }); } + @BinderThread @Override public void insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls) { @@ -266,6 +271,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } + @BinderThread @Override public void showInsets(int types, boolean fromIme) { if ((types & WindowInsets.Type.ime()) == 0) { @@ -275,6 +281,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mExecutor.execute(() -> startAnimation(true /* show */, false /* forceRestart */)); } + @BinderThread @Override public void hideInsets(int types, boolean fromIme) { if ((types & WindowInsets.Type.ime()) == 0) { @@ -284,6 +291,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mExecutor.execute(() -> startAnimation(false /* show */, false /* forceRestart */)); } + @BinderThread @Override public void topFocusedWindowChanged(String packageName) { // no-op diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java index cd75840b8c71..fa0a75c2d364 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java @@ -28,6 +28,17 @@ public class HandlerExecutor implements ShellExecutor { } @Override + public void execute(@NonNull Runnable command) { + if (mHandler.getLooper().isCurrentThread()) { + command.run(); + return; + } + if (!mHandler.post(command)) { + throw new RuntimeException(mHandler + " is probably exiting"); + } + } + + @Override public void executeDelayed(@NonNull Runnable r, long delayMillis) { if (!mHandler.postDelayed(r, delayMillis)) { throw new RuntimeException(mHandler + " is probably exiting"); @@ -38,11 +49,4 @@ public class HandlerExecutor implements ShellExecutor { public void removeCallbacks(@NonNull Runnable r) { mHandler.removeCallbacks(r); } - - @Override - public void execute(@NonNull Runnable command) { - if (!mHandler.post(command)) { - throw new RuntimeException(mHandler + " is probably exiting"); - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java index aafe2407a1ea..22b831b7565e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java @@ -16,13 +16,40 @@ package com.android.wm.shell.common; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; /** * Super basic Executor interface that adds support for delayed execution and removing callbacks. * Intended to wrap Handler while better-supporting testing. */ public interface ShellExecutor extends Executor { + + /** + * Executes the given runnable. If the caller is running on the same looper as this executor, + * the runnable must be executed immediately. + */ + @Override + void execute(Runnable runnable); + + /** + * Executes the given runnable in a blocking call. If the caller is running on the same looper + * as this executor, the runnable must be executed immediately. + * + * @throws InterruptedException if runnable does not return in the time specified by + * {@param waitTimeout} + */ + default void executeBlocking(Runnable runnable, int waitTimeout, TimeUnit waitTimeUnit) + throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + execute(() -> { + runnable.run(); + latch.countDown(); + }); + latch.await(waitTimeout, waitTimeUnit); + } + /** * See {@link android.os.Handler#postDelayed(Runnable, long)}. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java index 9cb125087cd9..7321dc88770d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java @@ -24,6 +24,10 @@ import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import android.window.WindowOrganizer; +import androidx.annotation.BinderThread; + +import com.android.wm.shell.common.annotations.ShellMainThread; + import java.util.ArrayList; /** @@ -151,6 +155,7 @@ public final class SyncTransactionQueue { mHandler.postDelayed(mOnReplyTimeout, REPLY_TIMEOUT); } + @BinderThread @Override public void onTransactionReady(int id, @NonNull SurfaceControl.Transaction t) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java index 0f6dd93f9c16..5e077188c415 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java @@ -19,12 +19,10 @@ package com.android.wm.shell.common; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ITaskStackListener; -import android.app.TaskInfo; import android.content.ComponentName; import android.os.IBinder; import androidx.annotation.BinderThread; -import androidx.annotation.MainThread; /** * An interface to track task stack changes. Classes should implement this instead of @@ -32,85 +30,61 @@ import androidx.annotation.MainThread; */ public interface TaskStackListenerCallback { - @MainThread default void onRecentTaskListUpdated() { } - @MainThread default void onRecentTaskListFrozenChanged(boolean frozen) { } @BinderThread default void onTaskStackChangedBackground() { } - @MainThread default void onTaskStackChanged() { } - @MainThread default void onTaskProfileLocked(int taskId, int userId) { } - @MainThread default void onTaskDisplayChanged(int taskId, int newDisplayId) { } - @MainThread default void onTaskCreated(int taskId, ComponentName componentName) { } - @MainThread default void onTaskRemoved(int taskId) { } - @MainThread default void onTaskMovedToFront(int taskId) { } - @MainThread default void onTaskMovedToFront(RunningTaskInfo taskInfo) { onTaskMovedToFront(taskInfo.taskId); } - @MainThread default void onTaskDescriptionChanged(RunningTaskInfo taskInfo) { } - @MainThread default void onTaskSnapshotChanged(int taskId, ActivityManager.TaskSnapshot snapshot) { } - @MainThread default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { } - @MainThread default void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { } - @MainThread default void onActivityPinned(String packageName, int userId, int taskId, int stackId) { } - @MainThread default void onActivityUnpinned() { } - @MainThread default void onActivityForcedResizable(String packageName, int taskId, int reason) { } - @MainThread default void onActivityDismissingDockedStack() { } - @MainThread default void onActivityLaunchOnSecondaryDisplayFailed() { } - @MainThread default void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo) { onActivityLaunchOnSecondaryDisplayFailed(); } - @MainThread default void onActivityLaunchOnSecondaryDisplayRerouted() { } - @MainThread default void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo) { onActivityLaunchOnSecondaryDisplayRerouted(); } - @MainThread default void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { } - @MainThread default void onActivityRotation(int displayId) { } - @MainThread default void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ChoreographerSfVsync.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ChoreographerSfVsync.java new file mode 100644 index 000000000000..4009ad21b9b8 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ChoreographerSfVsync.java @@ -0,0 +1,18 @@ +package com.android.wm.shell.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.inject.Qualifier; + +/** + * Annotates a method that or qualifies a provider runs aligned to the Choreographer SF vsync + * instead of the app vsync. + */ +@Documented +@Inherited +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface ChoreographerSfVsync {}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalThread.java new file mode 100644 index 000000000000..7560f71d1f98 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalThread.java @@ -0,0 +1,15 @@ +package com.android.wm.shell.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.inject.Qualifier; + +/** Annotates a method or class that is called from an external thread to the Shell threads. */ +@Documented +@Inherited +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface ExternalThread {}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellAnimationThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellAnimationThread.java new file mode 100644 index 000000000000..0479f8780c79 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellAnimationThread.java @@ -0,0 +1,15 @@ +package com.android.wm.shell.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.inject.Qualifier; + +/** Annotates a method or qualifies a provider that runs on the Shell animation-thread */ +@Documented +@Inherited +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface ShellAnimationThread {}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellMainThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellMainThread.java new file mode 100644 index 000000000000..423f4ce3bfd4 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellMainThread.java @@ -0,0 +1,15 @@ +package com.android.wm.shell.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.inject.Qualifier; + +/** Annotates a method or qualifies a provider that runs on the Shell main-thread */ +@Documented +@Inherited +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface ShellMainThread {}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java index 38e0519b7a90..3a2f0da6bf03 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java @@ -20,11 +20,14 @@ import android.content.res.Configuration; import androidx.annotation.NonNull; +import com.android.wm.shell.common.annotations.ExternalThread; + import java.io.PrintWriter; /** * Interface to engage hide display cutout feature. */ +@ExternalThread public interface HideDisplayCutout { /** * Notifies {@link Configuration} changed. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java index 9bb709f9a82a..821a00703adf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java @@ -18,6 +18,7 @@ package com.android.wm.shell.onehanded; import androidx.annotation.NonNull; +import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback; import java.io.PrintWriter; @@ -25,6 +26,7 @@ import java.io.PrintWriter; /** * Interface to engage one handed feature. */ +@ExternalThread public interface OneHanded { /** * Return one handed settings enabled or not. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java index 993e0e7ed016..5593268588fd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java @@ -19,12 +19,16 @@ package com.android.wm.shell.pip; import android.app.RemoteAction; import android.content.ComponentName; import android.content.pm.ParceledListSlice; +import android.os.RemoteException; import android.view.DisplayInfo; -import android.view.IPinnedStackController; import android.view.IPinnedStackListener; +import android.view.WindowManagerGlobal; + +import androidx.annotation.BinderThread; + +import com.android.wm.shell.common.ShellExecutor; import java.util.ArrayList; -import java.util.List; /** * PinnedStackListener that simply forwards all calls to each listener added via @@ -32,8 +36,15 @@ import java.util.List; * {@link com.android.server.wm.WindowManagerService#registerPinnedStackListener} replaces any * previously set listener. */ -public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { - private List<PinnedStackListener> mListeners = new ArrayList<>(); +public class PinnedStackListenerForwarder { + + private final IPinnedStackListener mListenerImpl = new PinnedStackListenerImpl(); + private final ShellExecutor mShellMainExecutor; + private final ArrayList<PinnedStackListener> mListeners = new ArrayList<>(); + + public PinnedStackListenerForwarder(ShellExecutor shellMainExecutor) { + mShellMainExecutor = shellMainExecutor; + } /** Adds a listener to receive updates from the WindowManagerService. */ public void addListener(PinnedStackListener listener) { @@ -45,69 +56,110 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { mListeners.remove(listener); } - @Override - public void onListenerRegistered(IPinnedStackController controller) { - for (PinnedStackListener listener : mListeners) { - listener.onListenerRegistered(controller); - } + public void register(int displayId) throws RemoteException { + WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener( + displayId, mListenerImpl); } - @Override - public void onMovementBoundsChanged(boolean fromImeAdjustment) { + private void onMovementBoundsChanged(boolean fromImeAdjustment) { for (PinnedStackListener listener : mListeners) { listener.onMovementBoundsChanged(fromImeAdjustment); } } - @Override - public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { + private void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { for (PinnedStackListener listener : mListeners) { listener.onImeVisibilityChanged(imeVisible, imeHeight); } } - @Override - public void onActionsChanged(ParceledListSlice<RemoteAction> actions) { + private void onActionsChanged(ParceledListSlice<RemoteAction> actions) { for (PinnedStackListener listener : mListeners) { listener.onActionsChanged(actions); } } - @Override - public void onActivityHidden(ComponentName componentName) { + private void onActivityHidden(ComponentName componentName) { for (PinnedStackListener listener : mListeners) { listener.onActivityHidden(componentName); } } - @Override - public void onDisplayInfoChanged(DisplayInfo displayInfo) { + private void onDisplayInfoChanged(DisplayInfo displayInfo) { for (PinnedStackListener listener : mListeners) { listener.onDisplayInfoChanged(displayInfo); } } - @Override - public void onConfigurationChanged() { + private void onConfigurationChanged() { for (PinnedStackListener listener : mListeners) { listener.onConfigurationChanged(); } } - @Override - public void onAspectRatioChanged(float aspectRatio) { + private void onAspectRatioChanged(float aspectRatio) { for (PinnedStackListener listener : mListeners) { listener.onAspectRatioChanged(aspectRatio); } } + @BinderThread + private class PinnedStackListenerImpl extends IPinnedStackListener.Stub { + @Override + public void onMovementBoundsChanged(boolean fromImeAdjustment) { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onMovementBoundsChanged(fromImeAdjustment); + }); + } + + @Override + public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onImeVisibilityChanged(imeVisible, imeHeight); + }); + } + + @Override + public void onActionsChanged(ParceledListSlice<RemoteAction> actions) { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onActionsChanged(actions); + }); + } + + @Override + public void onActivityHidden(ComponentName componentName) { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onActivityHidden(componentName); + }); + } + + @Override + public void onDisplayInfoChanged(DisplayInfo displayInfo) { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onDisplayInfoChanged(displayInfo); + }); + } + + @Override + public void onConfigurationChanged() { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onConfigurationChanged(); + }); + } + + @Override + public void onAspectRatioChanged(float aspectRatio) { + mShellMainExecutor.execute(() -> { + PinnedStackListenerForwarder.this.onAspectRatioChanged(aspectRatio); + }); + } + } + /** * A counterpart of {@link IPinnedStackListener} with empty implementations. * Subclasses can ignore those methods they do not intend to take action upon. */ public static class PinnedStackListener { - public void onListenerRegistered(IPinnedStackController controller) {} - public void onMovementBoundsChanged(boolean fromImeAdjustment) {} public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java index 9fa222ad4fdd..da9ce0aacedc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java @@ -17,12 +17,12 @@ package com.android.wm.shell.pip; import android.annotation.Nullable; -import android.app.ActivityManager; import android.app.PictureInPictureParams; import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.graphics.Rect; +import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.pip.phone.PipTouchHandler; import java.io.PrintWriter; @@ -31,6 +31,7 @@ import java.util.function.Consumer; /** * Interface to engage picture in picture feature. */ +@ExternalThread public interface Pip { /** * Closes PIP (PIPed activity and PIP system UI). diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java index f153aa5a1beb..7194fc70025c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java @@ -20,15 +20,18 @@ import android.content.Context; import android.graphics.Rect; import android.graphics.Region; import android.os.Bundle; -import android.os.Handler; import android.os.RemoteException; import android.view.MagnificationSpec; +import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; +import androidx.annotation.BinderThread; + import com.android.wm.shell.R; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipTaskOrganizer; @@ -40,8 +43,7 @@ import java.util.List; * Expose the touch actions to accessibility as if this object were a window with a single view. * That pseudo-view exposes all of the actions this object can perform. */ -public class PipAccessibilityInteractionConnection - extends IAccessibilityInteractionConnection.Stub { +public class PipAccessibilityInteractionConnection { public interface AccessibilityCallbacks { void onAccessibilityShowMenu(); @@ -50,14 +52,15 @@ public class PipAccessibilityInteractionConnection private static final long ACCESSIBILITY_NODE_ID = 1; private List<AccessibilityNodeInfo> mAccessibilityNodeInfoList; - private Context mContext; - private Handler mHandler; + private final Context mContext; + private final ShellExecutor mShellMainExcutor; private final @NonNull PipBoundsState mPipBoundsState; - private PipMotionHelper mMotionHelper; - private PipTaskOrganizer mTaskOrganizer; - private PipSnapAlgorithm mSnapAlgorithm; - private Runnable mUpdateMovementBoundCallback; - private AccessibilityCallbacks mCallbacks; + private final PipMotionHelper mMotionHelper; + private final PipTaskOrganizer mTaskOrganizer; + private final PipSnapAlgorithm mSnapAlgorithm; + private final Runnable mUpdateMovementBoundCallback; + private final AccessibilityCallbacks mCallbacks; + private final IAccessibilityInteractionConnection mConnectionImpl; private final Rect mNormalBounds = new Rect(); private final Rect mExpandedBounds = new Rect(); @@ -69,19 +72,23 @@ public class PipAccessibilityInteractionConnection @NonNull PipBoundsState pipBoundsState, PipMotionHelper motionHelper, PipTaskOrganizer taskOrganizer, PipSnapAlgorithm snapAlgorithm, AccessibilityCallbacks callbacks, Runnable updateMovementBoundCallback, - Handler handler) { + ShellExecutor shellMainExcutor) { mContext = context; - mHandler = handler; + mShellMainExcutor = shellMainExcutor; mPipBoundsState = pipBoundsState; mMotionHelper = motionHelper; mTaskOrganizer = taskOrganizer; mSnapAlgorithm = snapAlgorithm; mUpdateMovementBoundCallback = updateMovementBoundCallback; mCallbacks = callbacks; + mConnectionImpl = new PipAccessibilityInteractionConnectionImpl(); + } + + public void register(AccessibilityManager am) { + am.setPictureInPictureActionReplacingConnection(mConnectionImpl); } - @Override - public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, + private void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) { @@ -94,8 +101,7 @@ public class PipAccessibilityInteractionConnection } } - @Override - public void performAccessibilityAction(long accessibilityNodeId, int action, + private void performAccessibilityAction(long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid) { @@ -115,9 +121,7 @@ public class PipAccessibilityInteractionConnection } else { switch (action) { case AccessibilityNodeInfo.ACTION_CLICK: - mHandler.post(() -> { - mCallbacks.onAccessibilityShowMenu(); - }); + mCallbacks.onAccessibilityShowMenu(); result = true; break; case AccessibilityNodeInfo.ACTION_DISMISS: @@ -172,8 +176,7 @@ public class PipAccessibilityInteractionConnection }); } - @Override - public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, + private void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { @@ -185,8 +188,7 @@ public class PipAccessibilityInteractionConnection } } - @Override - public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, + private void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { @@ -198,8 +200,7 @@ public class PipAccessibilityInteractionConnection } } - @Override - public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion, + private void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { // We have no view that can take focus @@ -210,8 +211,7 @@ public class PipAccessibilityInteractionConnection } } - @Override - public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion, + private void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { // We have no view that can take focus @@ -222,16 +222,6 @@ public class PipAccessibilityInteractionConnection } } - @Override - public void clearAccessibilityFocus() { - // We should not be here. - } - - @Override - public void notifyOutsideTouch() { - // Do nothing. - } - /** * Update the normal and expanded bounds so they can be used for Resize. */ @@ -271,4 +261,95 @@ public class PipAccessibilityInteractionConnection mAccessibilityNodeInfoList.add(info); return mAccessibilityNodeInfoList; } + + @BinderThread + private class PipAccessibilityInteractionConnectionImpl + extends IAccessibilityInteractionConnection.Stub { + @Override + public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, + Region bounds, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid, MagnificationSpec spec, + Bundle arguments) throws RemoteException { + mShellMainExcutor.execute(() -> { + PipAccessibilityInteractionConnection.this + .findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, bounds, + interactionId, callback, flags, interrogatingPid, interrogatingTid, + spec, arguments); + }); + } + + @Override + public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, + Region bounds, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) + throws RemoteException { + mShellMainExcutor.execute(() -> { + PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByViewId( + accessibilityNodeId, viewId, bounds, interactionId, callback, flags, + interrogatingPid, interrogatingTid, spec); + }); + } + + @Override + public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, + Region bounds, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) + throws RemoteException { + mShellMainExcutor.execute(() -> { + PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByText( + accessibilityNodeId, text, bounds, interactionId, callback, flags, + interrogatingPid, interrogatingTid, spec); + }); + } + + @Override + public void findFocus(long accessibilityNodeId, int focusType, Region bounds, + int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) + throws RemoteException { + mShellMainExcutor.execute(() -> { + PipAccessibilityInteractionConnection.this.findFocus(accessibilityNodeId, focusType, + bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid, + spec); + }); + } + + @Override + public void focusSearch(long accessibilityNodeId, int direction, Region bounds, + int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) + throws RemoteException { + mShellMainExcutor.execute(() -> { + PipAccessibilityInteractionConnection.this.focusSearch(accessibilityNodeId, + direction, + bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid, + spec); + }); + } + + @Override + public void performAccessibilityAction(long accessibilityNodeId, int action, + Bundle arguments, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int flags, + int interrogatingPid, long interrogatingTid) throws RemoteException { + mShellMainExcutor.execute(() -> { + PipAccessibilityInteractionConnection.this.performAccessibilityAction( + accessibilityNodeId, action, arguments, interactionId, callback, flags, + interrogatingPid, interrogatingTid); + }); + } + + @Override + public void clearAccessibilityFocus() throws RemoteException { + // Do nothing + } + + @Override + public void notifyOutsideTouch() throws RemoteException { + // Do nothing + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index db02e284731e..3234ef6ccf66 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -41,7 +41,6 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.view.DisplayInfo; -import android.view.IPinnedStackController; import android.view.WindowManagerGlobal; import android.window.WindowContainerTransaction; @@ -52,7 +51,6 @@ import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.common.PipInputConsumer; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; @@ -163,63 +161,50 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private class PipControllerPinnedStackListener extends PinnedStackListenerForwarder.PinnedStackListener { @Override - public void onListenerRegistered(IPinnedStackController controller) { - mMainExecutor.execute(() -> mTouchHandler.setPinnedStackController(controller)); - } - - @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - mMainExecutor.execute(() -> { - mPipBoundsState.setImeVisibility(imeVisible, imeHeight); - mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight); - }); + mPipBoundsState.setImeVisibility(imeVisible, imeHeight); + mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight); } @Override public void onMovementBoundsChanged(boolean fromImeAdjustment) { - mMainExecutor.execute(() -> updateMovementBounds(null /* toBounds */, + updateMovementBounds(null /* toBounds */, false /* fromRotation */, fromImeAdjustment, false /* fromShelfAdjustment */, - null /* windowContainerTransaction */)); + null /* windowContainerTransaction */); } @Override public void onActionsChanged(ParceledListSlice<RemoteAction> actions) { - mMainExecutor.execute(() -> mMenuController.setAppActions(actions)); + mMenuController.setAppActions(actions); } @Override public void onActivityHidden(ComponentName componentName) { - mMainExecutor.execute(() -> { - if (componentName.equals(mPipBoundsState.getLastPipComponentName())) { - // The activity was removed, we don't want to restore to the reentry state - // saved for this component anymore. - mPipBoundsState.setLastPipComponentName(null); - } - }); + if (componentName.equals(mPipBoundsState.getLastPipComponentName())) { + // The activity was removed, we don't want to restore to the reentry state + // saved for this component anymore. + mPipBoundsState.setLastPipComponentName(null); + } } @Override public void onDisplayInfoChanged(DisplayInfo displayInfo) { - mMainExecutor.execute(() -> mPipBoundsState.setDisplayInfo(displayInfo)); + mPipBoundsState.setDisplayInfo(displayInfo); } @Override public void onConfigurationChanged() { - mMainExecutor.execute(() -> { - mPipBoundsAlgorithm.onConfigurationChanged(mContext); - mTouchHandler.onConfigurationChanged(); - mPipBoundsState.onConfigurationChanged(); - }); + mPipBoundsAlgorithm.onConfigurationChanged(mContext); + mTouchHandler.onConfigurationChanged(); + mPipBoundsState.onConfigurationChanged(); } @Override public void onAspectRatioChanged(float aspectRatio) { // TODO(b/169373982): Remove this callback as it is redundant with PipTaskOrg params // change. - mMainExecutor.execute(() -> { - mPipBoundsState.setAspectRatio(aspectRatio); - mTouchHandler.onAspectRatioChanged(); - }); + mPipBoundsState.setAspectRatio(aspectRatio); + mTouchHandler.onAspectRatioChanged(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java index 87ddb181dcce..0c64c8ca9b64 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/PipInputConsumer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java @@ -14,11 +14,9 @@ * limitations under the License. */ -package com.android.wm.shell.common; +package com.android.wm.shell.pip.phone; import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowManager.INPUT_CONSUMER_PIP; -import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import android.os.Binder; import android.os.IBinder; @@ -30,7 +28,6 @@ import android.view.Choreographer; import android.view.IWindowManager; import android.view.InputChannel; import android.view.InputEvent; -import android.view.WindowManagerGlobal; import java.io.PrintWriter; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 4e991f2e919b..2e10fc93cafb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -475,7 +475,7 @@ public class PipMenuView extends FrameLayout { final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS, Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null)); settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); - mContext.startActivityAsUser(settingsIntent, UserHandle.CURRENT); + mContext.startActivityAsUser(settingsIntent, UserHandle.of(topPipActivityInfo.second)); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 99177016c30c..b7cfad9030f7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -31,11 +31,8 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Handler; -import android.os.RemoteException; import android.provider.DeviceConfig; -import android.util.Log; import android.util.Size; -import android.view.IPinnedStackController; import android.view.InputEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -47,6 +44,7 @@ import android.view.accessibility.AccessibilityWindowInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; import com.android.wm.shell.common.FloatingContentCoordinator; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; @@ -75,7 +73,6 @@ public class PipTouchHandler { private final PipDismissTargetHandler mPipDismissTargetHandler; private PipResizeGestureHandler mPipResizeGestureHandler; - private IPinnedStackController mPinnedStackController; private WeakReference<Consumer<Rect>> mPipExclusionBoundsChangeListener; private final PhonePipMenuController mMenuController; @@ -157,7 +154,8 @@ public class PipTouchHandler { @NonNull PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer, FloatingContentCoordinator floatingContentCoordinator, - PipUiEventLogger pipUiEventLogger) { + PipUiEventLogger pipUiEventLogger, + ShellExecutor shellMainExecutor) { // Initialize the Pip input consumer mContext = context; mAccessibilityManager = context.getSystemService(AccessibilityManager.class); @@ -188,7 +186,7 @@ public class PipTouchHandler { mFloatingContentCoordinator = floatingContentCoordinator; mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState, mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(), - this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler); + this::onAccessibilityShowMenu, this::updateMovementBounds, shellMainExecutor); mPipUiEventLogger = pipUiEventLogger; @@ -436,8 +434,11 @@ public class PipTouchHandler { * TODO Add appropriate description */ public void onRegistrationChanged(boolean isRegistered) { - mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered - ? mConnection : null); + if (isRegistered) { + mConnection.register(mAccessibilityManager); + } else { + mAccessibilityManager.setPictureInPictureActionReplacingConnection(null); + } if (!isRegistered && mTouchState.isUserInteracting()) { // If the input consumer is unregistered while the user is interacting, then we may not // get the final TOUCH_UP event, so clean up the dismiss target as well @@ -459,10 +460,6 @@ public class PipTouchHandler { if (!(inputEvent instanceof MotionEvent)) { return true; } - // Skip touch handling until we are bound to the controller - if (mPinnedStackController == null) { - return true; - } MotionEvent ev = (MotionEvent) inputEvent; if (!mPipBoundsState.isStashed() && mPipResizeGestureHandler.willStartResizeGesture(ev)) { @@ -586,13 +583,6 @@ public class PipTouchHandler { } /** - * Sets the controller to update the system of changes from user interaction. - */ - void setPinnedStackController(IPinnedStackController controller) { - mPinnedStackController = controller; - } - - /** * Sets the menu visibility. */ private void setMenuState(int menuState, boolean resize, Runnable callback) { @@ -620,13 +610,9 @@ public class PipTouchHandler { // bounds which are now stale. In such a case we defer the animation to the // normal bounds until after the next onMovementBoundsChanged() call to get the // bounds in the new orientation - try { - int displayRotation = mPinnedStackController.getDisplayRotation(); - if (mDisplayRotation != displayRotation) { - mDeferResizeToNormalBoundsUntilRotation = displayRotation; - } - } catch (RemoteException e) { - Log.e(TAG, "Could not get display rotation from controller"); + int displayRotation = mContext.getDisplay().getRotation(); + if (mDisplayRotation != displayRotation) { + mDeferResizeToNormalBoundsUntilRotation = displayRotation; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java index 5d8d5e621846..763370bec1c9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java @@ -179,40 +179,33 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PinnedStackListenerForwarder.PinnedStackListener { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - mHandler.post(() -> { - mPipBoundsState.setImeVisibility(imeVisible, imeHeight); - if (mState == STATE_PIP) { - if (mImeVisible != imeVisible) { - if (imeVisible) { - // Save the IME height adjustment, and offset to not occlude the IME - mPipBoundsState.getNormalBounds().offset(0, -imeHeight); - mImeHeightAdjustment = imeHeight; - } else { - // Apply the inverse adjustment when the IME is hidden - mPipBoundsState.getNormalBounds().offset(0, mImeHeightAdjustment); - } - mImeVisible = imeVisible; - resizePinnedStack(STATE_PIP); + mPipBoundsState.setImeVisibility(imeVisible, imeHeight); + if (mState == STATE_PIP) { + if (mImeVisible != imeVisible) { + if (imeVisible) { + // Save the IME height adjustment, and offset to not occlude the IME + mPipBoundsState.getNormalBounds().offset(0, -imeHeight); + mImeHeightAdjustment = imeHeight; + } else { + // Apply the inverse adjustment when the IME is hidden + mPipBoundsState.getNormalBounds().offset(0, mImeHeightAdjustment); } + mImeVisible = imeVisible; + resizePinnedStack(STATE_PIP); } - }); + } } @Override public void onMovementBoundsChanged(boolean fromImeAdjustment) { - mHandler.post(() -> { - mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo()); - - mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds); - }); + mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo()); + mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds); } @Override public void onActionsChanged(ParceledListSlice<RemoteAction> actions) { mCustomActions = actions; - mHandler.post(() -> { - mTvPipMenuController.setAppActions(mCustomActions); - }); + mTvPipMenuController.setAppActions(mCustomActions); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index e55f065c1bb2..7c70a4efad91 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -19,6 +19,8 @@ package com.android.wm.shell.splitscreen; import android.graphics.Rect; import android.window.WindowContainerToken; +import com.android.wm.shell.common.annotations.ExternalThread; + import java.io.PrintWriter; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -26,6 +28,7 @@ import java.util.function.Consumer; /** * Interface to engage split screen feature. */ +@ExternalThread public interface SplitScreen { /** Called when keyguard showing state changed. */ void onKeyguardVisibilityChanged(boolean isShowing); diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml index 101b5bf27c77..1054c4345891 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml @@ -21,6 +21,8 @@ <!-- Read and write traces from external storage --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <!-- Write secure settings --> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <!-- Capture screen contents --> @@ -38,7 +40,8 @@ <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/> <!-- ATM.removeRootTasksWithActivityTypes() --> <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> - <application> + <!-- Allow the test to write directly to /sdcard/ --> + <application android:requestLegacyExternalStorage="true"> <uses-library android:name="android.test.runner"/> <service android:name=".NotificationListener" diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml index 9dd9f42bdf81..23d7021baffb 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml @@ -34,7 +34,7 @@ <option name="hidden-api-checks" value="false" /> </test> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> - <option name="directory-keys" value="/storage/emulated/0/Android/data/com.android.wm.shell.flicker/files" /> + <option name="directory-keys" value="/sdcard/flicker" /> <option name="collect-on-run-ended-only" value="true" /> <option name="clean-up" value="true" /> </metrics_collector> diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml index afb1166415fc..073860875004 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml @@ -34,7 +34,7 @@ <option name="hidden-api-checks" value="false" /> </test> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> - <option name="directory-keys" value="/storage/emulated/0/Android/data/com.android.wm.shell.flicker/files" /> + <option name="directory-keys" value="/sdcard/flicker" /> <option name="collect-on-run-ended-only" value="true" /> <option name="clean-up" value="true" /> </metrics_collector> diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java index 080cddc58a09..5e0d51809d44 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java @@ -20,12 +20,15 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.InsetsState.ITYPE_IME; import static android.view.Surface.ROTATION_0; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import android.graphics.Point; +import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.SurfaceControl; @@ -68,19 +71,31 @@ public class DisplayImeControllerTest { @Test public void reappliesVisibilityToChangedLeash() { verifyZeroInteractions(mT); + mPerDisplay.mImeShowing = true; - mPerDisplay.mImeShowing = false; - mPerDisplay.insetsControlChanged(new InsetsState(), new InsetsSourceControl[] { - new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0)) - }); + mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl()); + assertFalse(mPerDisplay.mImeShowing); verify(mT).hide(any()); mPerDisplay.mImeShowing = true; - mPerDisplay.insetsControlChanged(new InsetsState(), new InsetsSourceControl[] { - new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0)) - }); + mPerDisplay.insetsControlChanged(insetsStateWithIme(true), insetsSourceControl()); + assertTrue(mPerDisplay.mImeShowing); verify(mT).show(any()); } + + private InsetsSourceControl[] insetsSourceControl() { + return new InsetsSourceControl[]{ + new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0)) + }; + } + + private InsetsState insetsStateWithIme(boolean visible) { + InsetsState state = new InsetsState(); + state.addSource(new InsetsSource(ITYPE_IME)); + state.setSourceVisible(ITYPE_IME, visible); + return state; + } + } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java index e60221943898..4efaebf96c2b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java @@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.R; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.FloatingContentCoordinator; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipSnapAlgorithm; @@ -71,6 +72,9 @@ public class PipTouchHandlerTest extends ShellTestCase { @Mock private PipUiEventLogger mPipUiEventLogger; + @Mock + private ShellExecutor mShellMainExecutor; + private PipBoundsState mPipBoundsState; private PipBoundsAlgorithm mPipBoundsAlgorithm; private PipSnapAlgorithm mPipSnapAlgorithm; @@ -94,7 +98,7 @@ public class PipTouchHandlerTest extends ShellTestCase { mPipSnapAlgorithm = new PipSnapAlgorithm(); mPipTouchHandler = new PipTouchHandler(mContext, mPhonePipMenuController, mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer, - mFloatingContentCoordinator, mPipUiEventLogger); + mFloatingContentCoordinator, mPipUiEventLogger, mShellMainExecutor); mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper()); mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler()); mPipTouchHandler.setPipMotionHelper(mMotionHelper); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index cd53217d2924..1ff1978044b9 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -333,6 +333,7 @@ cc_defaults { "jni/YuvToJpegEncoder.cpp", "jni/fonts/Font.cpp", "jni/fonts/FontFamily.cpp", + "jni/fonts/NativeFont.cpp", "jni/text/LineBreaker.cpp", "jni/text/MeasuredText.cpp", "jni/text/TextShaper.cpp", diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index c174c240ff22..f4c633fbe58f 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -18,6 +18,7 @@ #include "renderstate/RenderState.h" #include "utils/Color.h" +#include "utils/MathUtils.h" namespace android { namespace uirenderer { @@ -52,5 +53,90 @@ SkBlendMode Layer::getMode() const { } } +static inline SkScalar isIntegerAligned(SkScalar x) { + return fabsf(roundf(x) - x) <= NON_ZERO_EPSILON; +} + +// Disable filtering when there is no scaling in screen coordinates and the corners have the same +// fraction (for translate) or zero fraction (for any other rect-to-rect transform). +static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, const SkRect& dstRect) { + if (!matrix.rectStaysRect()) return true; + SkRect dstDevRect = matrix.mapRect(dstRect); + float dstW, dstH; + if (MathUtils::isZero(matrix.getScaleX()) && MathUtils::isZero(matrix.getScaleY())) { + // Has a 90 or 270 degree rotation, although total matrix may also have scale factors + // in m10 and m01. Those scalings are automatically handled by mapRect so comparing + // dimensions is sufficient, but swap width and height comparison. + dstW = dstDevRect.height(); + dstH = dstDevRect.width(); + } else { + // Handle H/V flips or 180 rotation matrices. Axes may have been mirrored, but + // dimensions are still safe to compare directly. + dstW = dstDevRect.width(); + dstH = dstDevRect.height(); + } + if (!(MathUtils::areEqual(dstW, srcRect.width()) && + MathUtils::areEqual(dstH, srcRect.height()))) { + return true; + } + // Device rect and source rect should be integer aligned to ensure there's no difference + // in how nearest-neighbor sampling is resolved. + return !(isIntegerAligned(srcRect.x()) && + isIntegerAligned(srcRect.y()) && + isIntegerAligned(dstDevRect.x()) && + isIntegerAligned(dstDevRect.y())); +} + +void Layer::draw(SkCanvas* canvas) { + GrRecordingContext* context = canvas->recordingContext(); + if (context == nullptr) { + SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface")); + return; + } + SkMatrix layerTransform = getTransform(); + //sk_sp<SkImage> layerImage = getImage(); + const int layerWidth = getWidth(); + const int layerHeight = getHeight(); + if (layerImage) { + SkMatrix textureMatrixInv; + textureMatrixInv = getTexTransform(); + // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed + // use bottom left origin and remove flipV and invert transformations. + SkMatrix flipV; + flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1); + textureMatrixInv.preConcat(flipV); + textureMatrixInv.preScale(1.0f / layerWidth, 1.0f / layerHeight); + textureMatrixInv.postScale(layerImage->width(), layerImage->height()); + SkMatrix textureMatrix; + if (!textureMatrixInv.invert(&textureMatrix)) { + textureMatrix = textureMatrixInv; + } + + SkMatrix matrix; + matrix = SkMatrix::Concat(layerTransform, textureMatrix); + + SkPaint paint; + paint.setAlpha(getAlpha()); + paint.setBlendMode(getMode()); + paint.setColorFilter(getColorFilter()); + const bool nonIdentityMatrix = !matrix.isIdentity(); + if (nonIdentityMatrix) { + canvas->save(); + canvas->concat(matrix); + } + const SkMatrix& totalMatrix = canvas->getTotalMatrix(); + + SkRect imageRect = SkRect::MakeIWH(layerImage->width(), layerImage->height()); + if (getForceFilter() || shouldFilterRect(totalMatrix, imageRect, imageRect)) { + paint.setFilterQuality(kLow_SkFilterQuality); + } + canvas->drawImage(layerImage.get(), 0, 0, &paint); + // restore the original matrix + if (nonIdentityMatrix) { + canvas->restore(); + } + } +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index ea3bfc9e80cb..e99e76299317 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -21,6 +21,7 @@ #include <SkBlendMode.h> #include <SkColorFilter.h> #include <SkColorSpace.h> +#include <SkCanvas.h> #include <SkPaint.h> #include <SkImage.h> #include <SkMatrix.h> @@ -87,6 +88,8 @@ public: inline sk_sp<SkImage> getImage() const { return this->layerImage; } + void draw(SkCanvas* canvas); + protected: RenderState& mRenderState; diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index e1f5abd786bf..0fad2d58cc8a 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -69,6 +69,7 @@ extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env); extern int register_android_graphics_fonts_Font(JNIEnv* env); extern int register_android_graphics_fonts_FontFamily(JNIEnv* env); +extern int register_android_graphics_fonts_NativeFont(JNIEnv* env); extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env); extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env); extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env); @@ -135,6 +136,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_drawable_VectorDrawable), REG_JNI(register_android_graphics_fonts_Font), REG_JNI(register_android_graphics_fonts_FontFamily), + REG_JNI(register_android_graphics_fonts_NativeFont), REG_JNI(register_android_graphics_pdf_PdfDocument), REG_JNI(register_android_graphics_pdf_PdfEditor), REG_JNI(register_android_graphics_pdf_PdfRenderer), diff --git a/libs/hwui/canvas/CanvasOpRasterizer.cpp b/libs/hwui/canvas/CanvasOpRasterizer.cpp index 25129f641c00..0093c38cf8a8 100644 --- a/libs/hwui/canvas/CanvasOpRasterizer.cpp +++ b/libs/hwui/canvas/CanvasOpRasterizer.cpp @@ -33,7 +33,11 @@ void rasterizeCanvasBuffer(const CanvasOpBuffer& source, SkCanvas* destination) SkMatrix& currentGlobalTransform = globalMatrixStack.emplace_back(SkMatrix::I()); source.for_each([&]<CanvasOpType T>(const CanvasOpContainer<T> * op) { - if constexpr (T == CanvasOpType::BeginZ || T == CanvasOpType::EndZ) { + if constexpr ( + T == CanvasOpType::BeginZ || + T == CanvasOpType::EndZ || + T == CanvasOpType::DrawLayer + ) { // Do beginZ or endZ LOG_ALWAYS_FATAL("TODO"); return; diff --git a/libs/hwui/canvas/CanvasOpTypes.h b/libs/hwui/canvas/CanvasOpTypes.h index f9df2f7aa5ba..2dfddaca0919 100644 --- a/libs/hwui/canvas/CanvasOpTypes.h +++ b/libs/hwui/canvas/CanvasOpTypes.h @@ -55,6 +55,7 @@ enum class CanvasOpType : int8_t { // DrawImageLattice also used to draw 9 patches DrawImageLattice, DrawPicture, + DrawLayer, // TODO: Rest diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h index 8c7113d5d075..b499733757a8 100644 --- a/libs/hwui/canvas/CanvasOps.h +++ b/libs/hwui/canvas/CanvasOps.h @@ -28,6 +28,7 @@ #include "CanvasProperty.h" #include "CanvasOpTypes.h" +#include "Layer.h" #include <experimental/type_traits> #include <utility> @@ -364,6 +365,11 @@ struct CanvasOp<CanvasOpType::DrawPicture> { } }; +template<> +struct CanvasOp<CanvasOpType::DrawLayer> { + sp<Layer> layer; +}; + // cleanup our macros #undef ASSERT_DRAWABLE diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index f0c77930cbe3..f612bce748ff 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -187,38 +187,6 @@ static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong } // Critical Native -static jlong Font_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { - const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); - MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); - - uint64_t result = font->style().weight(); - result |= font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000; - result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32); - result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48); - return result; -} - -// Critical Native -static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) { - const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); - MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); - const minikin::FontVariation& var = minikinSkia->GetAxes().at(index); - uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value); - return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary); -} - -// FastNative -static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontHandle) { - const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); - MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); - const std::string& filePath = minikinSkia->getFilePath(); - if (filePath.empty()) { - return nullptr; - } - return env->NewStringUTF(filePath.c_str()); -} - -// Critical Native static jlong Font_getNativeFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle); return reinterpret_cast<jlong>(font->font.get()); @@ -276,9 +244,6 @@ static const JNINativeMethod gFontBuilderMethods[] = { static const JNINativeMethod gFontMethods[] = { { "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds }, { "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics }, - { "nGetFontInfo", "(J)J", (void*) Font_getFontInfo }, - { "nGetAxisInfo", "(JI)J", (void*) Font_getAxisInfo }, - { "nGetFontPath", "(J)Ljava/lang/String;", (void*) Font_getFontPath }, { "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr }, { "nGetFontBufferAddress", "(J)J", (void*) Font_GetBufferAddress }, }; diff --git a/libs/hwui/jni/fonts/NativeFont.cpp b/libs/hwui/jni/fonts/NativeFont.cpp new file mode 100644 index 000000000000..c5c5d464ccac --- /dev/null +++ b/libs/hwui/jni/fonts/NativeFont.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2018 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. + */ + +#undef LOG_TAG +#define LOG_TAG "Minikin" + +#include "Font.h" +#include "SkData.h" +#include "SkFont.h" +#include "SkFontMetrics.h" +#include "SkFontMgr.h" +#include "SkRefCnt.h" +#include "SkTypeface.h" +#include "GraphicsJNI.h" +#include <nativehelper/ScopedUtfChars.h> +#include "Utils.h" +#include "FontUtils.h" + +#include <hwui/MinikinSkia.h> +#include <hwui/Paint.h> +#include <hwui/Typeface.h> +#include <minikin/FontFamily.h> +#include <minikin/LocaleList.h> +#include <ui/FatVector.h> + +#include <memory> + +namespace android { + +// Critical Native +static jint NativeFont_getFamilyCount(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle) { + Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle); + return tf->fFontCollection->getFamilies().size(); +} + +// Critical Native +static jlong NativeFont_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle, jint index) { + Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle); + return reinterpret_cast<jlong>(tf->fFontCollection->getFamilies()[index].get()); + +} + +// Fast Native +static jstring NativeFont_getLocaleList(JNIEnv* env, jobject, jlong familyHandle) { + minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle); + uint32_t localeListId = family->localeListId(); + return env->NewStringUTF(minikin::getLocaleString(localeListId).c_str()); +} + +// Critical Native +static jint NativeFont_getFontCount(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle) { + minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle); + return family->getNumFonts(); +} + +// Critical Native +static jlong NativeFont_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle, jint index) { + minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle); + return reinterpret_cast<jlong>(family->getFont(index)); +} + +// Critical Native +static jlong NativeFont_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { + const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); + MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); + + uint64_t result = font->style().weight(); + result |= font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000; + result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32); + result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48); + return result; +} + +// Critical Native +static jlong NativeFont_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) { + const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); + MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); + const minikin::FontVariation& var = minikinSkia->GetAxes().at(index); + uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value); + return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary); +} + +// FastNative +static jstring NativeFont_getFontPath(JNIEnv* env, jobject, jlong fontHandle) { + const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); + MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); + const std::string& filePath = minikinSkia->getFilePath(); + if (filePath.empty()) { + return nullptr; + } + return env->NewStringUTF(filePath.c_str()); +} + +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gNativeFontMethods[] = { + { "nGetFamilyCount", "(J)I", (void*) NativeFont_getFamilyCount }, + { "nGetFamily", "(JI)J", (void*) NativeFont_getFamily }, + { "nGetLocaleList", "(J)Ljava/lang/String;", (void*) NativeFont_getLocaleList }, + { "nGetFontCount", "(J)I", (void*) NativeFont_getFontCount }, + { "nGetFont", "(JI)J", (void*) NativeFont_getFont }, + { "nGetFontInfo", "(J)J", (void*) NativeFont_getFontInfo }, + { "nGetAxisInfo", "(JI)J", (void*) NativeFont_getAxisInfo }, + { "nGetFontPath", "(J)Ljava/lang/String;", (void*) NativeFont_getFontPath }, +}; + +int register_android_graphics_fonts_NativeFont(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFont", gNativeFontMethods, + NELEM(gNativeFontMethods)); +} + +} // namespace android |