diff options
72 files changed, 2304 insertions, 1142 deletions
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java index 906071fe4c7b..3f30d3ea880e 100644 --- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java +++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java @@ -19,11 +19,14 @@ package android.media; import android.annotation.NonNull; import android.content.ContentResolver; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; +import com.android.modules.annotation.MinSdk; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -77,6 +80,7 @@ import java.util.Set; There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform, application will only need to specify individual types they supported. */ +@MinSdk(Build.VERSION_CODES.S) public final class ApplicationMediaCapabilities implements Parcelable { private static final String TAG = "ApplicationMediaCapabilities"; diff --git a/apex/media/framework/java/android/media/MediaFeature.java b/apex/media/framework/java/android/media/MediaFeature.java index 0e461888a0e3..8d1b159cd70b 100644 --- a/apex/media/framework/java/android/media/MediaFeature.java +++ b/apex/media/framework/java/android/media/MediaFeature.java @@ -17,6 +17,9 @@ package android.media; import android.annotation.StringDef; +import android.os.Build; + +import com.android.modules.annotation.MinSdk; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -24,6 +27,7 @@ import java.lang.annotation.RetentionPolicy; /** * MediaFeature defines various media features, e.g. hdr type. */ +@MinSdk(Build.VERSION_CODES.S) public final class MediaFeature { /** * Defines tye type of HDR(high dynamic range) video. diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java index 93328355026e..de2924e160b6 100644 --- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java +++ b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java @@ -21,7 +21,9 @@ import android.annotation.SystemApi; import android.annotation.SystemApi.Client; import android.app.SystemServiceRegistry; import android.content.Context; +import android.os.Build; +import com.android.modules.annotation.MinSdk; import com.android.modules.utils.build.SdkLevel; /** @@ -29,6 +31,7 @@ import com.android.modules.utils.build.SdkLevel; * * @hide */ +@MinSdk(Build.VERSION_CODES.S) @SystemApi(client = Client.MODULE_LIBRARIES) public class MediaFrameworkInitializer { private MediaFrameworkInitializer() { diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java index 30d1896a23ac..84332e5bbcf0 100644 --- a/apex/media/framework/java/android/media/MediaTranscodeManager.java +++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java @@ -26,6 +26,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.net.Uri; +import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -34,6 +35,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.modules.annotation.MinSdk; import java.io.FileNotFoundException; import java.lang.annotation.Retention; @@ -99,6 +101,7 @@ import java.util.concurrent.Executors; TODO(hkuang): Clarify whether supports framerate conversion. @hide */ +@MinSdk(Build.VERSION_CODES.S) @SystemApi public final class MediaTranscodeManager { private static final String TAG = "MediaTranscodeManager"; diff --git a/core/api/current.txt b/core/api/current.txt index 63e35f11ecea..bca2e92d43c3 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -10961,6 +10961,7 @@ package android.content { field public static final String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED"; field public static final String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE"; field public static final String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE"; + field public static final String ACTION_MANAGE_UNUSED_APPS = "android.intent.action.MANAGE_UNUSED_APPS"; field public static final String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL"; field public static final String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON"; field public static final String ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING"; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 5b618358d464..39e259d7db9a 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -5194,6 +5194,21 @@ package android.media { field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce } + public final class MediaRouter2 { + method @NonNull public java.util.List<android.media.MediaRoute2Info> getAllRoutes(); + method @Nullable public String getClientPackageName(); + method @Nullable public android.media.MediaRouter2.RoutingController getController(@NonNull String); + method @Nullable public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context, @NonNull String); + method public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int); + method public void startScan(); + method public void stopScan(); + method public void transfer(@NonNull android.media.MediaRouter2.RoutingController, @NonNull android.media.MediaRoute2Info); + } + + public abstract static class MediaRouter2.RouteCallback { + method public void onPreferredFeaturesChanged(@NonNull java.util.List<java.lang.String>); + } + public class PlayerProxy { method public void pause(); method public void setPan(float); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index c601aabb582b..5cf83acd9a75 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1901,6 +1901,20 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.AUTO_REVOKE_PERMISSIONS"; /** + * Activity action: Launch UI to manage unused apps (hibernated apps). + * + * <p> + * Input: Nothing. + * </p> + * <p> + * Output: Nothing. + * </p> + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MANAGE_UNUSED_APPS = + "android.intent.action.MANAGE_UNUSED_APPS"; + + /** * Activity action: Launch UI to review permissions for an app. * The system uses this intent if permission review for apps not * supporting the new runtime permissions model is enabled. In diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java index d71a8300d9b8..a9348c68165d 100644 --- a/core/java/android/service/timezone/TimeZoneProviderService.java +++ b/core/java/android/service/timezone/TimeZoneProviderService.java @@ -218,7 +218,30 @@ public abstract class TimeZoneProviderService extends Service { } /** - * Starts the provider sending updates. + * Informs the provider that it should start detecting and reporting the detected time zone + * state via the various {@code report} methods. Implementations of {@link + * #onStartUpdates(long)} should return immediately, and will typically be used to start + * worker threads or begin asynchronous location listening. + * + * <p>Between {@link #onStartUpdates(long)} and {@link #onStopUpdates()} calls, the Android + * system server holds the latest report from the provider in memory. After an initial report, + * provider implementations are only required to send a report via {@link + * #reportSuggestion(TimeZoneProviderSuggestion)} or via {@link #reportUncertain()} when it + * differs from the previous report. + * + * <p>{@link #reportPermanentFailure(Throwable)} can also be called by provider implementations + * in rare cases, after which the provider should consider itself stopped and not make any + * further reports. {@link #onStopUpdates()} will not be called in this case. + * + * <p>The {@code initializationTimeoutMillis} parameter indicates how long the provider has been + * granted to call one of the {@code report} methods for the first time. If the provider does + * not call one of the {@code report} methods in this time, it may be judged uncertain and the + * Android system server may move on to use other providers or detection methods. Providers + * should therefore make best efforts during this time to generate a report, which could involve + * increased power usage. Providers should preferably report an explicit {@link + * #reportUncertain()} if the time zone(s) cannot be detected within the initialization timeout. + * + * @see #onStopUpdates() for the signal from the system server to stop sending reports */ public abstract void onStartUpdates(@DurationMillisLong long initializationTimeoutMillis); @@ -228,7 +251,8 @@ public abstract class TimeZoneProviderService extends Service { } /** - * Stops the provider sending updates. + * Stops the provider sending further updates. This will be called after {@link + * #onStartUpdates(long)}. */ public abstract void onStopUpdates(); diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index ee988781e51d..52801faf9c36 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -474,6 +474,12 @@ public final class SystemUiDeviceConfigFlags { public static final String SHARE_USE_SERVICE_TARGETS = "share_use_service_targets"; + /* + * (long) The duration that the home button must be pressed before triggering Assist + */ + public static final String HOME_BUTTON_LONG_PRESS_DURATION_MS = + "home_button_long_press_duration_ms"; + private SystemUiDeviceConfigFlags() { } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index f92276171b26..d783b445515d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2775,11 +2775,11 @@ The app can check whether it has this authorization by calling {@link android.provider.Settings#canDrawOverlays Settings.canDrawOverlays()}. - <p>Protection level: signature|appop|installer|appPredictor|pre23|development --> + <p>Protection level: signature|setup|appop|installer|appPredictor|pre23|development --> <permission android:name="android.permission.SYSTEM_ALERT_WINDOW" android:label="@string/permlab_systemAlertWindow" android:description="@string/permdesc_systemAlertWindow" - android:protectionLevel="signature|appop|installer|appPredictor|pre23|development" /> + android:protectionLevel="signature|setup|appop|installer|appPredictor|pre23|development" /> <!-- @SystemApi @hide Allows an application to create windows using the type {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY}, diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTest.java index a036db224a54..f8dd1538882b 100644 --- a/core/tests/mockingcoretests/src/android/view/DisplayTests.java +++ b/core/tests/mockingcoretests/src/android/view/DisplayTest.java @@ -56,12 +56,15 @@ import java.util.function.Consumer; * * <p>Build/Install/Run: * - * atest FrameworksMockingCoreTests:android.view.DisplayTests + * atest FrameworksMockingCoreTests:android.view.DisplayTest + * + * <p>This test class is a part of Window Manager Service tests and specified in + * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. */ @RunWith(AndroidJUnit4.class) @SmallTest @Presubmit -public class DisplayTests { +public class DisplayTest { private static final int APP_WIDTH = 272; private static final int APP_HEIGHT = 700; diff --git a/data/etc/platform.xml b/data/etc/platform.xml index b3a180d70fe2..b1715991a099 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -155,6 +155,7 @@ <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="media" /> <assign-permission name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" uid="media" /> <assign-permission name="android.permission.REGISTER_STATS_PULL_ATOM" uid="media" /> + <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="media" /> <assign-permission name="android.permission.INTERNET" uid="media" /> diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 3f03302de474..1b5dc8bdbcaa 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -38,14 +38,6 @@ filegroup { path: "src", } -filegroup { - name: "wm_shell-aidls", - srcs: [ - "src/**/*.aidl", - ], - path: "src", -} - // TODO(b/168581922) protologtool do not support kotlin(*.kt) filegroup { name: "wm_shell-sources-kt", @@ -106,7 +98,7 @@ android_library { ":wm_shell_protolog_src", // TODO(b/168581922) protologtool do not support kotlin(*.kt) ":wm_shell-sources-kt", - ":wm_shell-aidls", + "src/**/I*.aidl", ], resource_dirs: [ "res", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java index d451f4a0661b..eaed24d6195a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java @@ -47,7 +47,21 @@ public final class ShellCommandHandlerImpl { private final ShellExecutor mMainExecutor; private final HandlerImpl mImpl = new HandlerImpl(); - public ShellCommandHandlerImpl( + public static ShellCommandHandler create( + ShellTaskOrganizer shellTaskOrganizer, + Optional<LegacySplitScreenController> legacySplitScreenOptional, + Optional<SplitScreenController> splitScreenOptional, + Optional<Pip> pipOptional, + Optional<OneHandedController> oneHandedOptional, + Optional<HideDisplayCutoutController> hideDisplayCutout, + Optional<AppPairsController> appPairsOptional, + ShellExecutor mainExecutor) { + return new ShellCommandHandlerImpl(shellTaskOrganizer, legacySplitScreenOptional, + splitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout, + appPairsOptional, mainExecutor).mImpl; + } + + private ShellCommandHandlerImpl( ShellTaskOrganizer shellTaskOrganizer, Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, @@ -66,10 +80,6 @@ public final class ShellCommandHandlerImpl { mMainExecutor = mainExecutor; } - public ShellCommandHandler asShellCommandHandler() { - return mImpl; - } - /** Dumps WM Shell internal state. */ private void dump(PrintWriter pw) { mShellTaskOrganizer.dump(pw, ""); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java index 6f4550c2a89e..85bd24c1c2bf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java @@ -26,7 +26,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.splitscreen.SplitScreenController; -import com.android.wm.shell.startingsurface.StartingWindowController; +import com.android.wm.shell.startingsurface.StartingSurface; import com.android.wm.shell.transition.Transitions; import java.util.Optional; @@ -47,20 +47,44 @@ public class ShellInitImpl { private final FullscreenTaskListener mFullscreenTaskListener; private final ShellExecutor mMainExecutor; private final Transitions mTransitions; - private final StartingWindowController mStartingWindow; + private final Optional<StartingSurface> mStartingSurfaceOptional; private final InitImpl mImpl = new InitImpl(); - public ShellInitImpl(DisplayImeController displayImeController, + public static ShellInit create(DisplayImeController displayImeController, DragAndDropController dragAndDropController, ShellTaskOrganizer shellTaskOrganizer, Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<AppPairsController> appPairsOptional, + Optional<StartingSurface> startingSurfaceOptional, + Optional<PipTouchHandler> pipTouchHandlerOptional, + FullscreenTaskListener fullscreenTaskListener, + Transitions transitions, + ShellExecutor mainExecutor) { + return new ShellInitImpl(displayImeController, + dragAndDropController, + shellTaskOrganizer, + legacySplitScreenOptional, + splitScreenOptional, + appPairsOptional, + startingSurfaceOptional, + pipTouchHandlerOptional, + fullscreenTaskListener, + transitions, + mainExecutor).mImpl; + } + + private ShellInitImpl(DisplayImeController displayImeController, + DragAndDropController dragAndDropController, + ShellTaskOrganizer shellTaskOrganizer, + Optional<LegacySplitScreenController> legacySplitScreenOptional, + Optional<SplitScreenController> splitScreenOptional, + Optional<AppPairsController> appPairsOptional, + Optional<StartingSurface> startingSurfaceOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, Transitions transitions, - StartingWindowController startingWindow, ShellExecutor mainExecutor) { mDisplayImeController = displayImeController; mDragAndDropController = dragAndDropController; @@ -72,21 +96,17 @@ public class ShellInitImpl { mPipTouchHandlerOptional = pipTouchHandlerOptional; mTransitions = transitions; mMainExecutor = mainExecutor; - mStartingWindow = startingWindow; - } - - public ShellInit asShellInit() { - return mImpl; + mStartingSurfaceOptional = startingSurfaceOptional; } private void init() { // Start listening for display changes mDisplayImeController.startMonitorDisplays(); - // Setup the shell organizer mShellTaskOrganizer.addListenerForType( mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN); - mShellTaskOrganizer.initStartingWindow(mStartingWindow); + mStartingSurfaceOptional.ifPresent(mShellTaskOrganizer::initStartingSurface); + // Register the shell organizer mShellTaskOrganizer.registerOrganizer(); mAppPairsOptional.ifPresent(AppPairsController::onOrganizerRegistered); 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 94d13eab4299..fcb53cd890b7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -48,7 +48,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.sizecompatui.SizeCompatUIController; -import com.android.wm.shell.startingsurface.StartingWindowController; +import com.android.wm.shell.startingsurface.StartingSurface; import java.io.PrintWriter; import java.util.ArrayList; @@ -133,7 +133,7 @@ public class ShellTaskOrganizer extends TaskOrganizer { private final ArraySet<LocusIdListener> mLocusIdListeners = new ArraySet<>(); private final Object mLock = new Object(); - private StartingWindowController mStartingWindow; + private StartingSurface mStartingSurface; /** * In charge of showing size compat UI. Can be {@code null} if device doesn't support size @@ -184,8 +184,8 @@ public class ShellTaskOrganizer extends TaskOrganizer { /** * @hide */ - public void initStartingWindow(StartingWindowController startingWindow) { - mStartingWindow = startingWindow; + public void initStartingSurface(StartingSurface startingSurface) { + mStartingSurface = startingSurface; } /** @@ -302,23 +302,23 @@ public class ShellTaskOrganizer extends TaskOrganizer { @Override public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { - if (mStartingWindow != null) { - mStartingWindow.addStartingWindow(info, appToken); + if (mStartingSurface != null) { + mStartingSurface.addStartingWindow(info, appToken); } } @Override public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, boolean playRevealAnimation) { - if (mStartingWindow != null) { - mStartingWindow.removeStartingWindow(taskId, leash, frame, playRevealAnimation); + if (mStartingSurface != null) { + mStartingSurface.removeStartingWindow(taskId, leash, frame, playRevealAnimation); } } @Override public void copySplashScreenView(int taskId) { - if (mStartingWindow != null) { - mStartingWindow.copySplashScreenView(taskId); + if (mStartingSurface != null) { + mStartingSurface.copySplashScreenView(taskId); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java deleted file mode 100644 index b29058b1f204..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2021 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 android.Manifest; -import android.util.Slog; - -import java.util.function.Consumer; - -/** - * Helpers for working with executors - */ -public class ExecutorUtils { - - /** - * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given - * callback. - */ - public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance, - String log, Consumer<T> callback) { - executeRemoteCallWithTaskPermission(controllerInstance, log, callback, - false /* blocking */); - } - - /** - * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given - * callback. - */ - public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance, - String log, Consumer<T> callback, boolean blocking) { - if (controllerInstance == null) return; - - final RemoteCallable<T> controller = controllerInstance; - controllerInstance.getContext().enforceCallingPermission( - Manifest.permission.MANAGE_ACTIVITY_TASKS, log); - if (blocking) { - try { - controllerInstance.getRemoteCallExecutor().executeBlocking(() -> { - callback.accept((T) controller); - }); - } catch (InterruptedException e) { - Slog.e("ExecutorUtils", "Remote call failed", e); - } - } else { - controllerInstance.getRemoteCallExecutor().execute(() -> { - callback.accept((T) controller); - }); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java deleted file mode 100644 index 30f535ba940c..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2021 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 android.content.Context; - -/** - * An interface for controllers that can receive remote calls. - */ -public interface RemoteCallable<T> { - /** - * Returns a context used for permission checking. - */ - Context getContext(); - - /** - * Returns the executor to post the handler callback to. - */ - ShellExecutor getRemoteCallExecutor(); -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index 9a09ca43d1d7..aab2334f8255 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -235,7 +235,7 @@ public class DragAndDropPolicy { mStarter.startShortcut(packageName, id, stage, position, opts, user); } else { mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), - null, stage, position, opts); + mContext, null, stage, position, opts); } } @@ -295,7 +295,7 @@ public class DragAndDropPolicy { @Nullable Bundle options); void startShortcut(String packageName, String shortcutId, @StageType int stage, @StagePosition int position, @Nullable Bundle options, UserHandle user); - void startIntent(PendingIntent intent, Intent fillInIntent, + void startIntent(PendingIntent intent, Context context, Intent fillInIntent, @StageType int stage, @StagePosition int position, @Nullable Bundle options); void enterSplitScreen(int taskId, boolean leftOrTop); @@ -337,8 +337,9 @@ public class DragAndDropPolicy { } @Override - public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent, int stage, - int position, @Nullable Bundle options) { + public void startIntent(PendingIntent intent, Context context, + @Nullable Intent fillInIntent, int stage, int position, + @Nullable Bundle options) { try { intent.send(mContext, 0, fillInIntent, null, null, null, options); } catch (PendingIntent.CanceledException e) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/IOneHanded.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/IOneHanded.aidl deleted file mode 100644 index 008b5087d7da..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/IOneHanded.aidl +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2021 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.onehanded; - -/** - * Interface that is exposed to remote callers to manipulate the OneHanded feature. - */ -interface IOneHanded { - - /** - * Enters one handed mode. - */ - oneway void startOneHanded() = 1; - - /** - * Exits one handed mode. - */ - oneway void stopOneHanded() = 2; -} 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 a7e9a0135de0..4f31c370108d 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 @@ -26,14 +26,6 @@ import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEv */ @ExternalThread public interface OneHanded { - - /** - * Returns a binder that can be passed to an external process to manipulate OneHanded. - */ - default IOneHanded createExternalInterface() { - return null; - } - /** * Return one handed settings enabled or not. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index 8022c1b6e047..5fc7c987899f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -18,10 +18,6 @@ package com.android.wm.shell.onehanded; import static android.os.UserHandle.USER_CURRENT; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; - -import android.Manifest; -import android.annotation.BinderThread; import android.content.ComponentName; import android.content.Context; import android.content.om.IOverlayManager; @@ -47,8 +43,6 @@ import com.android.internal.logging.UiEventLogger; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.ExecutorUtils; -import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; @@ -60,7 +54,7 @@ import java.io.PrintWriter; /** * Manages and manipulates the one handed states, transitions, and gesture for phones. */ -public class OneHandedController implements RemoteCallable<OneHandedController> { +public class OneHandedController { private static final String TAG = "OneHandedController"; private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE = @@ -245,16 +239,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController> return mImpl; } - @Override - public Context getContext() { - return mContext; - } - - @Override - public ShellExecutor getRemoteCallExecutor() { - return mMainExecutor; - } - /** * Set one handed enabled or disabled when user update settings */ @@ -583,22 +567,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController> } } - /** - * The interface for calls from outside the Shell, within the host process. - */ @ExternalThread private class OneHandedImpl implements OneHanded { - private IOneHandedImpl mIOneHanded; - - @Override - public IOneHanded createExternalInterface() { - if (mIOneHanded != null) { - mIOneHanded.invalidate(); - } - mIOneHanded = new IOneHandedImpl(OneHandedController.this); - return mIOneHanded; - } - @Override public boolean isOneHandedEnabled() { // This is volatile so return directly @@ -667,39 +637,4 @@ public class OneHandedController implements RemoteCallable<OneHandedController> }); } } - - /** - * The interface for calls from outside the host process. - */ - @BinderThread - private static class IOneHandedImpl extends IOneHanded.Stub { - private OneHandedController mController; - - IOneHandedImpl(OneHandedController controller) { - mController = controller; - } - - /** - * Invalidates this instance, preventing future calls from updating the controller. - */ - void invalidate() { - mController = null; - } - - @Override - public void startOneHanded() { - executeRemoteCallWithTaskPermission(mController, "startOneHanded", - (controller) -> { - controller.startOneHanded(); - }); - } - - @Override - public void stopOneHanded() { - executeRemoteCallWithTaskPermission(mController, "stopOneHanded", - (controller) -> { - controller.stopOneHanded(); - }); - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl deleted file mode 100644 index a6ffa6e44584..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2021 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.pip; - -import android.app.PictureInPictureParams; -import android.content.ComponentName; -import android.content.pm.ActivityInfo; -import android.graphics.Rect; - -import com.android.wm.shell.pip.IPipAnimationListener; - -/** - * Interface that is exposed to remote callers to manipulate the Pip feature. - */ -interface IPip { - - /** - * Notifies that Activity is about to be swiped to home with entering PiP transition and - * queries the destination bounds for PiP depends on Launcher's rotation and shelf height. - * - * @param componentName ComponentName represents the Activity - * @param activityInfo ActivityInfo tied to the Activity - * @param pictureInPictureParams PictureInPictureParams tied to the Activity - * @param launcherRotation Launcher rotation to calculate the PiP destination bounds - * @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds - * @return destination bounds the PiP window should land into - */ - Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo, - in PictureInPictureParams pictureInPictureParams, - int launcherRotation, int shelfHeight) = 1; - - /** - * Notifies the swiping Activity to PiP onto home transition is finished - * - * @param componentName ComponentName represents the Activity - * @param destinationBounds the destination bounds the PiP window lands into - */ - oneway void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 2; - - /** - * Sets listener to get pinned stack animation callbacks. - */ - oneway void setPinnedStackAnimationListener(IPipAnimationListener listener) = 3; - - /** - * Sets the shelf height and visibility. - */ - oneway void setShelfHeight(boolean visible, int shelfHeight) = 4; -} 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 6d4773bdeb1f..d14c3e3c0dd4 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 @@ -16,10 +16,15 @@ package com.android.wm.shell.pip; +import android.annotation.Nullable; +import android.app.PictureInPictureParams; +import android.content.ComponentName; +import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import com.android.wm.shell.common.annotations.ExternalThread; +import com.android.wm.shell.pip.phone.PipTouchHandler; import java.io.PrintWriter; import java.util.function.Consumer; @@ -29,14 +34,6 @@ import java.util.function.Consumer; */ @ExternalThread public interface Pip { - - /** - * Returns a binder that can be passed to an external process to manipulate PIP. - */ - default IPip createExternalInterface() { - return null; - } - /** * Expand PIP, it's possible that specific request to activate the window via Alt-tab. */ @@ -112,6 +109,30 @@ public interface Pip { default void showPictureInPictureMenu() {} /** + * Called by Launcher when swiping an auto-pip enabled Activity to home starts + * @param componentName {@link ComponentName} represents the Activity entering PiP + * @param activityInfo {@link ActivityInfo} tied to the Activity + * @param pictureInPictureParams {@link PictureInPictureParams} tied to the Activity + * @param launcherRotation Rotation Launcher is in + * @param shelfHeight Shelf height when landing PiP window onto Launcher + * @return Destination bounds of PiP window based on the parameters passed in + */ + default Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) { + return null; + } + + /** + * Called by Launcher when swiping an auto-pip enable Activity to home finishes + * @param componentName {@link ComponentName} represents the Activity entering PiP + * @param destinationBounds Destination bounds of PiP window + */ + default void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + return; + } + + /** * Called by NavigationBar in order to listen in for PiP bounds change. This is mostly used * for times where the PiP bounds could conflict with SystemUI elements, such as a stashed * PiP and the Back-from-Edge gesture. 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 501b90ea6828..9a584c67f97c 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 @@ -21,10 +21,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static android.view.WindowManager.INPUT_CONSUMER_PIP; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; -import android.Manifest; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.PictureInPictureParams; @@ -35,7 +33,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ParceledListSlice; import android.content.res.Configuration; import android.graphics.Rect; -import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -47,7 +44,6 @@ import android.view.DisplayInfo; import android.view.WindowManagerGlobal; import android.window.WindowContainerTransaction; -import androidx.annotation.BinderThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -55,13 +51,9 @@ 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.ExecutorUtils; -import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; -import com.android.wm.shell.pip.IPip; -import com.android.wm.shell.pip.IPipAnimationListener; import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsAlgorithm; @@ -79,8 +71,7 @@ import java.util.function.Consumer; /** * Manages the picture-in-picture (PIP) UI and states for Phones. */ -public class PipController implements PipTransitionController.PipTransitionCallback, - RemoteCallable<PipController> { +public class PipController implements PipTransitionController.PipTransitionCallback { private static final String TAG = "PipController"; private Context mContext; @@ -94,12 +85,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb private PipBoundsState mPipBoundsState; private PipTouchHandler mTouchHandler; private PipTransitionController mPipTransitionController; - protected final PipImpl mImpl; + protected final PipImpl mImpl = new PipImpl(); private final Rect mTmpInsetBounds = new Rect(); private boolean mIsInFixedRotation; - private IPipAnimationListener mPinnedStackAnimationRecentsCallback; + private Consumer<Boolean> mPinnedStackAnimationRecentsCallback; protected PhonePipMenuController mMenuController; protected PipTaskOrganizer mPipTaskOrganizer; @@ -273,7 +264,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb } mContext = context; - mImpl = new PipImpl(); mWindowManagerShellWrapper = windowManagerShellWrapper; mDisplayController = displayController; mPipBoundsAlgorithm = pipBoundsAlgorithm; @@ -376,16 +366,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb }); } - @Override - public Context getContext() { - return mContext; - } - - @Override - public ShellExecutor getRemoteCallExecutor() { - return mMainExecutor; - } - private void onConfigurationChanged(Configuration newConfig) { mPipBoundsAlgorithm.onConfigurationChanged(mContext); mTouchHandler.onConfigurationChanged(); @@ -494,7 +474,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipTaskOrganizer.setOneShotAnimationType(animationType); } - private void setPinnedStackAnimationListener(IPipAnimationListener callback) { + private void setPinnedStackAnimationListener(Consumer<Boolean> callback) { mPinnedStackAnimationRecentsCallback = callback; } @@ -532,11 +512,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb // Disable touches while the animation is running mTouchHandler.setTouchEnabled(false); if (mPinnedStackAnimationRecentsCallback != null) { - try { - mPinnedStackAnimationRecentsCallback.onPipAnimationStarted(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to call onPinnedStackAnimationStarted()", e); - } + mPinnedStackAnimationRecentsCallback.accept(true); } } @@ -662,21 +638,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipInputConsumer.dump(pw, innerPrefix); } - /** - * The interface for calls from outside the Shell, within the host process. - */ private class PipImpl implements Pip { - private IPipImpl mIPip; - - @Override - public IPip createExternalInterface() { - if (mIPip != null) { - mIPip.invalidate(); - } - mIPip = new IPipImpl(PipController.this); - return mIPip; - } - @Override public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) { mMainExecutor.execute(() -> { @@ -734,6 +696,13 @@ public class PipController implements PipTransitionController.PipTransitionCallb } @Override + public void setPinnedStackAnimationListener(Consumer<Boolean> callback) { + mMainExecutor.execute(() -> { + PipController.this.setPinnedStackAnimationListener(callback); + }); + } + + @Override public void setPinnedStackAnimationType(int animationType) { mMainExecutor.execute(() -> { PipController.this.setPinnedStackAnimationType(animationType); @@ -755,99 +724,37 @@ public class PipController implements PipTransitionController.PipTransitionCallb } @Override - public void dump(PrintWriter pw) { + public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, int launcherRotation, + int shelfHeight) { + Rect[] result = new Rect[1]; try { mMainExecutor.executeBlocking(() -> { - PipController.this.dump(pw); + result[0] = PipController.this.startSwipePipToHome(componentName, activityInfo, + pictureInPictureParams, launcherRotation, shelfHeight); }); } catch (InterruptedException e) { - Slog.e(TAG, "Failed to dump PipController in 2s"); + Slog.e(TAG, "Failed to start swipe pip to home"); } - } - } - - /** - * The interface for calls from outside the host process. - */ - @BinderThread - private static class IPipImpl extends IPip.Stub { - private PipController mController; - private IPipAnimationListener mListener; - private final IBinder.DeathRecipient mListenerDeathRecipient = - new IBinder.DeathRecipient() { - @Override - @BinderThread - public void binderDied() { - final PipController controller = mController; - controller.getRemoteCallExecutor().execute(() -> { - mListener = null; - controller.setPinnedStackAnimationListener(null); - }); - } - }; - - IPipImpl(PipController controller) { - mController = controller; - } - - /** - * Invalidates this instance, preventing future calls from updating the controller. - */ - void invalidate() { - mController = null; - } - - @Override - public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, - PictureInPictureParams pictureInPictureParams, int launcherRotation, - int shelfHeight) { - Rect[] result = new Rect[1]; - executeRemoteCallWithTaskPermission(mController, "startSwipePipToHome", - (controller) -> { - result[0] = controller.startSwipePipToHome(componentName, activityInfo, - pictureInPictureParams, launcherRotation, shelfHeight); - }, true /* blocking */); return result[0]; } @Override public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { - executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome", - (controller) -> { - controller.stopSwipePipToHome(componentName, destinationBounds); - }); - } - - @Override - public void setShelfHeight(boolean visible, int height) { - executeRemoteCallWithTaskPermission(mController, "setShelfHeight", - (controller) -> { - controller.setShelfHeight(visible, height); - }); + mMainExecutor.execute(() -> { + PipController.this.stopSwipePipToHome(componentName, destinationBounds); + }); } @Override - public void setPinnedStackAnimationListener(IPipAnimationListener listener) { - executeRemoteCallWithTaskPermission(mController, "setPinnedStackAnimationListener", - (controller) -> { - if (mListener != null) { - // Reset the old death recipient - mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } - if (listener != null) { - // Register the death recipient for the new listener to clear the listener - try { - listener.asBinder().linkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to link to death"); - return; - } - } - mListener = listener; - controller.setPinnedStackAnimationListener(listener); - }); + public void dump(PrintWriter pw) { + try { + mMainExecutor.executeBlocking(() -> { + PipController.this.dump(pw); + }); + } catch (InterruptedException e) { + Slog.e(TAG, "Failed to dump PipController in 2s"); + } } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl deleted file mode 100644 index 0c46eaba18ae..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2021 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.splitscreen; - -import android.app.PendingIntent; -import android.content.Intent; -import android.os.Bundle; -import android.os.UserHandle; - -import com.android.wm.shell.splitscreen.ISplitScreenListener; - -/** - * Interface that is exposed to remote callers to manipulate the splitscreen feature. - */ -interface ISplitScreen { - - /** - * Registers a split screen listener. - */ - oneway void registerSplitScreenListener(in ISplitScreenListener listener) = 1; - - /** - * Unregisters a split screen listener. - */ - oneway void unregisterSplitScreenListener(in ISplitScreenListener listener) = 2; - - /** - * Hides the side-stage if it is currently visible. - */ - oneway void setSideStageVisibility(boolean visible) = 3; - - /** - * Removes a task from the side stage. - */ - oneway void removeFromSideStage(int taskId) = 4; - - /** - * Removes the split-screen stages. - */ - oneway void exitSplitScreen() = 5; - - /** - * @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. - */ - oneway void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 6; - - /** - * Starts a task in a stage. - */ - oneway void startTask(int taskId, int stage, int position, in Bundle options) = 7; - - /** - * Starts a shortcut in a stage. - */ - oneway void startShortcut(String packageName, String shortcutId, int stage, int position, - in Bundle options, in UserHandle user) = 8; - - /** - * Starts an activity in a stage. - */ - oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int stage, - int position, in Bundle options) = 9; -} 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 340b55d7f446..25a84bd46484 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 @@ -35,7 +35,7 @@ import com.android.wm.shell.draganddrop.DragAndDropPolicy; * TODO: Figure out which of these are actually needed outside of the Shell */ @ExternalThread -public interface SplitScreen { +public interface SplitScreen extends DragAndDropPolicy.Starter { /** * Stage position isn't specified normally meaning to use what ever it is currently set to. */ @@ -89,10 +89,35 @@ public interface SplitScreen { void onTaskStageChanged(int taskId, @StageType int stage, boolean visible); } - /** - * Returns a binder that can be passed to an external process to manipulate SplitScreen. - */ - default ISplitScreen createExternalInterface() { - return null; - } + /** @return {@code true} if split-screen is currently visible. */ + boolean isSplitScreenVisible(); + /** Moves a task in the side-stage of split-screen. */ + boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition); + /** Moves a task in the side-stage of split-screen. */ + boolean moveToSideStage(ActivityManager.RunningTaskInfo task, + @StagePosition int sideStagePosition); + /** Removes a task from the side-stage of split-screen. */ + boolean removeFromSideStage(int taskId); + /** Sets the position of the side-stage. */ + void setSideStagePosition(@StagePosition int sideStagePosition); + /** Hides the side-stage if it is currently visible. */ + void setSideStageVisibility(boolean visible); + + /** Removes the split-screen stages. */ + void exitSplitScreen(); + /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */ + void exitSplitScreenOnHide(boolean exitSplitScreenOnHide); + /** Gets the stage bounds. */ + void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds); + + void registerSplitScreenListener(SplitScreenListener listener); + void unregisterSplitScreenListener(SplitScreenListener listener); + + void startTask(int taskId, + @StageType int stage, @StagePosition int position, @Nullable Bundle options); + void startShortcut(String packageName, String shortcutId, @StageType int stage, + @StagePosition int position, @Nullable Bundle options, UserHandle user); + void startIntent(PendingIntent intent, Context context, + @Nullable Intent fillInIntent, @StageType int stage, + @StagePosition int position, @Nullable Bundle options); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index d4362efe462d..bb6f6f259a1e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -18,7 +18,6 @@ package com.android.wm.shell.splitscreen; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED; @@ -35,24 +34,19 @@ import android.content.Intent; import android.content.pm.LauncherApps; import android.graphics.Rect; import android.os.Bundle; -import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; -import androidx.annotation.BinderThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.draganddrop.DragAndDropPolicy; -import com.android.wm.shell.splitscreen.ISplitScreenListener; import java.io.PrintWriter; @@ -61,8 +55,7 @@ import java.io.PrintWriter; * {@link SplitScreen}. * @see StageCoordinator */ -public class SplitScreenController implements DragAndDropPolicy.Starter, - RemoteCallable<SplitScreenController> { +public class SplitScreenController implements DragAndDropPolicy.Starter { private static final String TAG = SplitScreenController.class.getSimpleName(); private final ShellTaskOrganizer mTaskOrganizer; @@ -91,16 +84,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mImpl; } - @Override - public Context getContext() { - return mContext; - } - - @Override - public ShellExecutor getRemoteCallExecutor() { - return mMainExecutor; - } - public void onOrganizerRegistered() { if (mStageCoordinator == null) { // TODO: Multi-display @@ -189,13 +172,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } } - public void startIntent(PendingIntent intent, Intent fillInIntent, - @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position, - @Nullable Bundle options) { + public void startIntent(PendingIntent intent, Context context, + Intent fillInIntent, @SplitScreen.StageType int stage, + @SplitScreen.StagePosition int position, @Nullable Bundle options) { options = resolveStartStage(stage, position, options); try { - intent.send(mContext, 0, fillInIntent, null, null, null, options); + intent.send(context, 0, fillInIntent, null, null, null, options); } catch (PendingIntent.CanceledException e) { Slog.e(TAG, "Failed to launch activity", e); } @@ -259,170 +242,121 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } } - /** - * The interface for calls from outside the Shell, within the host process. - */ - @ExternalThread private class SplitScreenImpl implements SplitScreen { - private ISplitScreenImpl mISplitScreen; - @Override - public ISplitScreen createExternalInterface() { - if (mISplitScreen != null) { - mISplitScreen.invalidate(); - } - mISplitScreen = new ISplitScreenImpl(SplitScreenController.this); - return mISplitScreen; + public boolean isSplitScreenVisible() { + return mMainExecutor.executeBlockingForResult(() -> { + return SplitScreenController.this.isSplitScreenVisible(); + }, Boolean.class); } - } - /** - * The interface for calls from outside the host process. - */ - @BinderThread - private static class ISplitScreenImpl extends ISplitScreen.Stub { - private SplitScreenController mController; - private ISplitScreenListener mListener; - private final SplitScreen.SplitScreenListener mSplitScreenListener = - new SplitScreen.SplitScreenListener() { - @Override - public void onStagePositionChanged(int stage, int position) { - try { - if (mListener != null) { - mListener.onStagePositionChanged(stage, position); - } - } catch (RemoteException e) { - Slog.e(TAG, "onStagePositionChanged", e); - } - } + @Override + public boolean moveToSideStage(int taskId, int sideStagePosition) { + return mMainExecutor.executeBlockingForResult(() -> { + return SplitScreenController.this.moveToSideStage(taskId, sideStagePosition); + }, Boolean.class); + } - @Override - public void onTaskStageChanged(int taskId, int stage, boolean visible) { - try { - if (mListener != null) { - mListener.onTaskStageChanged(taskId, stage, visible); - } - } catch (RemoteException e) { - Slog.e(TAG, "onTaskStageChanged", e); - } - } - }; - private final IBinder.DeathRecipient mListenerDeathRecipient = - new IBinder.DeathRecipient() { - @Override - @BinderThread - public void binderDied() { - final SplitScreenController controller = mController; - controller.getRemoteCallExecutor().execute(() -> { - mListener = null; - controller.unregisterSplitScreenListener(mSplitScreenListener); - }); - } - }; + @Override + public boolean moveToSideStage(ActivityManager.RunningTaskInfo task, + int sideStagePosition) { + return mMainExecutor.executeBlockingForResult(() -> { + return SplitScreenController.this.moveToSideStage(task, sideStagePosition); + }, Boolean.class); + } - public ISplitScreenImpl(SplitScreenController controller) { - mController = controller; + @Override + public boolean removeFromSideStage(int taskId) { + return mMainExecutor.executeBlockingForResult(() -> { + return SplitScreenController.this.removeFromSideStage(taskId); + }, Boolean.class); } - /** - * Invalidates this instance, preventing future calls from updating the controller. - */ - void invalidate() { - mController = null; + @Override + public void setSideStagePosition(int sideStagePosition) { + mMainExecutor.execute(() -> { + SplitScreenController.this.setSideStagePosition(sideStagePosition); + }); } @Override - public void registerSplitScreenListener(ISplitScreenListener listener) { - executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener", - (controller) -> { - if (mListener != null) { - mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } - if (listener != null) { - try { - listener.asBinder().linkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to link to death"); - return; - } - } - mListener = listener; - controller.registerSplitScreenListener(mSplitScreenListener); - }); + public void setSideStageVisibility(boolean visible) { + mMainExecutor.execute(() -> { + SplitScreenController.this.setSideStageVisibility(visible); + }); } @Override - public void unregisterSplitScreenListener(ISplitScreenListener listener) { - executeRemoteCallWithTaskPermission(mController, "unregisterSplitScreenListener", - (controller) -> { - if (mListener != null) { - mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } - mListener = null; - controller.unregisterSplitScreenListener(mSplitScreenListener); - }); + public void enterSplitScreen(int taskId, boolean leftOrTop) { + mMainExecutor.execute(() -> { + SplitScreenController.this.enterSplitScreen(taskId, leftOrTop); + }); } @Override public void exitSplitScreen() { - executeRemoteCallWithTaskPermission(mController, "exitSplitScreen", - (controller) -> { - controller.exitSplitScreen(); - }); + mMainExecutor.execute(() -> { + SplitScreenController.this.exitSplitScreen(); + }); } @Override public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { - executeRemoteCallWithTaskPermission(mController, "exitSplitScreenOnHide", - (controller) -> { - controller.exitSplitScreenOnHide(exitSplitScreenOnHide); - }); + mMainExecutor.execute(() -> { + SplitScreenController.this.exitSplitScreenOnHide(exitSplitScreenOnHide); + }); } @Override - public void setSideStageVisibility(boolean visible) { - executeRemoteCallWithTaskPermission(mController, "setSideStageVisibility", - (controller) -> { - controller.setSideStageVisibility(visible); - }); + public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) { + try { + mMainExecutor.executeBlocking(() -> { + SplitScreenController.this.getStageBounds(outTopOrLeftBounds, + outBottomOrRightBounds); + }); + } catch (InterruptedException e) { + Slog.e(TAG, "Failed to get stage bounds in 2s"); + } + } + + @Override + public void registerSplitScreenListener(SplitScreenListener listener) { + mMainExecutor.execute(() -> { + SplitScreenController.this.registerSplitScreenListener(listener); + }); } @Override - public void removeFromSideStage(int taskId) { - executeRemoteCallWithTaskPermission(mController, "removeFromSideStage", - (controller) -> { - controller.removeFromSideStage(taskId); - }); + public void unregisterSplitScreenListener(SplitScreenListener listener) { + mMainExecutor.execute(() -> { + SplitScreenController.this.unregisterSplitScreenListener(listener); + }); } @Override public void startTask(int taskId, int stage, int position, @Nullable Bundle options) { - executeRemoteCallWithTaskPermission(mController, "startTask", - (controller) -> { - controller.startTask(taskId, stage, position, options); - }); + mMainExecutor.execute(() -> { + SplitScreenController.this.startTask(taskId, stage, position, options); + }); } @Override public void startShortcut(String packageName, String shortcutId, int stage, int position, @Nullable Bundle options, UserHandle user) { - executeRemoteCallWithTaskPermission(mController, "startShortcut", - (controller) -> { - controller.startShortcut(packageName, shortcutId, stage, position, - options, user); - }); + mMainExecutor.execute(() -> { + SplitScreenController.this.startShortcut(packageName, shortcutId, stage, position, + options, user); + }); } @Override - public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position, - @Nullable Bundle options) { - executeRemoteCallWithTaskPermission(mController, "startIntent", - (controller) -> { - controller.startIntent(intent, fillInIntent, stage, position, options); - }); + public void startIntent(PendingIntent intent, Context context, Intent fillInIntent, + int stage, int position, @Nullable Bundle options) { + mMainExecutor.execute(() -> { + SplitScreenController.this.startIntent(intent, context, fillInIntent, stage, + position, options); + }); } } + } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindow.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindow.aidl deleted file mode 100644 index 546c406ef453..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindow.aidl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2021 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.startingsurface; - -import com.android.wm.shell.startingsurface.IStartingWindowListener; - -/** - * Interface that is exposed to remote callers to manipulate starting windows. - */ -interface IStartingWindow { - /** - * Sets listener to get task launching callbacks. - */ - oneway void setStartingWindowListener(IStartingWindowListener listener) = 43; -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java index 079d68973852..f258286f2d17 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java @@ -16,15 +16,35 @@ package com.android.wm.shell.startingsurface; +import android.graphics.Rect; +import android.os.IBinder; +import android.view.SurfaceControl; +import android.window.StartingWindowInfo; + +import java.util.function.BiConsumer; /** * Interface to engage starting window feature. */ public interface StartingSurface { + /** + * Called when a task need a starting window. + */ + void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken); + /** + * Called when the content of a task is ready to show, starting window can be removed. + */ + void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation); + /** + * Called when the Task wants to copy the splash screen. + * @param taskId + */ + void copySplashScreenView(int taskId); /** - * Returns a binder that can be passed to an external process to manipulate starting windows. + * Registers the starting window listener. + * + * @param listener The callback when need a starting window. */ - default IStartingWindow createExternalInterface() { - return null; - } + void setStartingWindowListener(BiConsumer<Integer, Integer> listener); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java index b6ca86989690..a694e525a761 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java @@ -25,8 +25,6 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK; import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING; import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; - import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.content.Context; @@ -39,9 +37,6 @@ import android.window.StartingWindowInfo; import android.window.TaskOrganizer; import android.window.TaskSnapshot; -import androidx.annotation.BinderThread; - -import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; @@ -63,7 +58,7 @@ import java.util.function.BiConsumer; * constructor to keep everything synchronized. * @hide */ -public class StartingWindowController implements RemoteCallable<StartingWindowController> { +public class StartingWindowController { private static final String TAG = StartingWindowController.class.getSimpleName(); static final boolean DEBUG_SPLASH_SCREEN = false; static final boolean DEBUG_TASK_SNAPSHOT = false; @@ -73,17 +68,17 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo private BiConsumer<Integer, Integer> mTaskLaunchingCallback; private final StartingSurfaceImpl mImpl = new StartingSurfaceImpl(); - private final Context mContext; private final ShellExecutor mSplashScreenExecutor; // For Car Launcher public StartingWindowController(Context context, ShellExecutor splashScreenExecutor) { - this(context, splashScreenExecutor, new TransactionPool()); + mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor, + new TransactionPool()); + mSplashScreenExecutor = splashScreenExecutor; } public StartingWindowController(Context context, ShellExecutor splashScreenExecutor, TransactionPool pool) { - mContext = context; mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor, pool); mSplashScreenExecutor = splashScreenExecutor; } @@ -95,16 +90,6 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo return mImpl; } - @Override - public Context getContext() { - return mContext; - } - - @Override - public ShellExecutor getRemoteCallExecutor() { - return mSplashScreenExecutor; - } - private static class StartingTypeChecker { TaskSnapshot mSnapshot; @@ -203,121 +188,59 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo /** * Called when a task need a starting window. */ - public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { - mSplashScreenExecutor.execute(() -> { - final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(windowInfo); - final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo; - if (mTaskLaunchingCallback != null) { - mTaskLaunchingCallback.accept(runningTaskInfo.taskId, suggestionType); - } - if (suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN) { - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken); - } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) { - final TaskSnapshot snapshot = mStartingTypeChecker.mSnapshot; - mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot); - } - // If prefer don't show, then don't show! - }); + void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { + final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(windowInfo); + final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo; + if (mTaskLaunchingCallback != null) { + mTaskLaunchingCallback.accept(runningTaskInfo.taskId, suggestionType); + } + if (suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN) { + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken); + } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) { + final TaskSnapshot snapshot = mStartingTypeChecker.mSnapshot; + mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot); + } + // If prefer don't show, then don't show! } - public void copySplashScreenView(int taskId) { - mSplashScreenExecutor.execute(() -> { - mStartingSurfaceDrawer.copySplashScreenView(taskId); - }); + void copySplashScreenView(int taskId) { + mStartingSurfaceDrawer.copySplashScreenView(taskId); } /** * Called when the content of a task is ready to show, starting window can be removed. */ - public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, boolean playRevealAnimation) { - mSplashScreenExecutor.execute(() -> { - mStartingSurfaceDrawer.removeStartingWindow(taskId, leash, frame, playRevealAnimation); - }); + mStartingSurfaceDrawer.removeStartingWindow(taskId, leash, frame, playRevealAnimation); } - /** - * The interface for calls from outside the Shell, within the host process. - */ private class StartingSurfaceImpl implements StartingSurface { - private IStartingWindowImpl mIStartingWindow; @Override - public IStartingWindowImpl createExternalInterface() { - if (mIStartingWindow != null) { - mIStartingWindow.invalidate(); - } - mIStartingWindow = new IStartingWindowImpl(StartingWindowController.this); - return mIStartingWindow; + public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { + mSplashScreenExecutor.execute(() -> + StartingWindowController.this.addStartingWindow(windowInfo, appToken)); } - } - /** - * The interface for calls from outside the host process. - */ - @BinderThread - private static class IStartingWindowImpl extends IStartingWindow.Stub { - private StartingWindowController mController; - private IStartingWindowListener mListener; - private final BiConsumer<Integer, Integer> mStartingWindowListener = - this::notifyIStartingWindowListener; - private final IBinder.DeathRecipient mListenerDeathRecipient = - new IBinder.DeathRecipient() { - @Override - @BinderThread - public void binderDied() { - final StartingWindowController controller = mController; - controller.getRemoteCallExecutor().execute(() -> { - mListener = null; - controller.setStartingWindowListener(null); - }); - } - }; - - public IStartingWindowImpl(StartingWindowController controller) { - mController = controller; - } - - /** - * Invalidates this instance, preventing future calls from updating the controller. - */ - void invalidate() { - mController = null; + @Override + public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { + mSplashScreenExecutor.execute(() -> + StartingWindowController.this.removeStartingWindow(taskId, leash, frame, + playRevealAnimation)); } @Override - public void setStartingWindowListener(IStartingWindowListener listener) { - executeRemoteCallWithTaskPermission(mController, "setStartingWindowListener", - (controller) -> { - if (mListener != null) { - // Reset the old death recipient - mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } - if (listener != null) { - try { - listener.asBinder().linkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to link to death"); - return; - } - } - mListener = listener; - controller.setStartingWindowListener(mStartingWindowListener); - }); + public void copySplashScreenView(int taskId) { + mSplashScreenExecutor.execute(() -> + StartingWindowController.this.copySplashScreenView(taskId)); } - private void notifyIStartingWindowListener(int taskId, int supportedType) { - if (mListener == null) { - return; - } - - try { - mListener.onTaskLaunching(taskId, supportedType); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to notify task launching", e); - } + @Override + public void setStartingWindowListener(BiConsumer<Integer, Integer> listener) { + mSplashScreenExecutor.execute(() -> + StartingWindowController.this.setStartingWindowListener(listener)); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl deleted file mode 100644 index dffc700a3690..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2021 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.transition; - -import android.window.IRemoteTransition; -import android.window.TransitionFilter; - -/** - * Interface that is exposed to remote callers to manipulate the transitions feature. - */ -interface IShellTransitions { - - /** - * Registers a remote transition handler. - */ - oneway void registerRemote(in TransitionFilter filter, - in IRemoteTransition remoteTransition) = 1; - - /** - * Unregisters a remote transition handler. - */ - oneway void unregisterRemote(in IRemoteTransition remoteTransition) = 2; -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java index 9667f4b6a8c5..ac93a17b4014 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java @@ -16,8 +16,6 @@ package com.android.wm.shell.transition; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; - import android.annotation.NonNull; import android.annotation.Nullable; import android.os.IBinder; @@ -25,7 +23,6 @@ import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; -import android.util.Slog; import android.view.SurfaceControl; import android.window.IRemoteTransition; import android.window.IRemoteTransitionFinishedCallback; @@ -34,8 +31,6 @@ import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; -import androidx.annotation.BinderThread; - import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -47,8 +42,6 @@ import java.util.ArrayList; * if the request includes a specific remote. */ public class RemoteTransitionHandler implements Transitions.TransitionHandler { - private static final String TAG = "RemoteTransitionHandler"; - private final ShellExecutor mMainExecutor; /** Includes remotes explicitly requested by, eg, ActivityOptions */ @@ -58,33 +51,15 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters = new ArrayList<>(); - private final IBinder.DeathRecipient mTransitionDeathRecipient = - new IBinder.DeathRecipient() { - @Override - @BinderThread - public void binderDied() { - mMainExecutor.execute(() -> { - mFilters.clear(); - }); - } - }; - RemoteTransitionHandler(@NonNull ShellExecutor mainExecutor) { mMainExecutor = mainExecutor; } void addFiltered(TransitionFilter filter, IRemoteTransition remote) { - try { - remote.asBinder().linkToDeath(mTransitionDeathRecipient, 0 /* flags */); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to link to death"); - return; - } mFilters.add(new Pair<>(filter, remote)); } void removeFiltered(IRemoteTransition remote) { - remote.asBinder().unlinkToDeath(mTransitionDeathRecipient, 0 /* flags */); for (int i = mFilters.size() - 1; i >= 0; --i) { if (mFilters.get(i).second == remote) { mFilters.remove(i); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java index bc42c6e2f12c..85bbf74d56b9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ShellTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java @@ -26,15 +26,7 @@ import com.android.wm.shell.common.annotations.ExternalThread; * Interface to manage remote transitions. */ @ExternalThread -public interface ShellTransitions { - - /** - * Returns a binder that can be passed to an external process to manipulate remote transitions. - */ - default IShellTransitions createExternalInterface() { - return null; - } - +public interface RemoteTransitions { /** * Registers a remote transition. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index ca1b53d4d46b..677db10d07a7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -23,8 +23,6 @@ import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; - import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; @@ -53,7 +51,6 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ExternalThread; @@ -63,7 +60,7 @@ import java.util.ArrayList; import java.util.Arrays; /** Plays transition animations */ -public class Transitions implements RemoteCallable<Transitions> { +public class Transitions { static final String TAG = "ShellTransitions"; /** Set to {@code true} to enable shell transitions. */ @@ -76,7 +73,7 @@ public class Transitions implements RemoteCallable<Transitions> { private final ShellExecutor mAnimExecutor; private final TransitionPlayerImpl mPlayerImpl; private final RemoteTransitionHandler mRemoteTransitionHandler; - private final ShellTransitionImpl mImpl = new ShellTransitionImpl(); + private final RemoteTransitionImpl mImpl = new RemoteTransitionImpl(); /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */ private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>(); @@ -90,6 +87,10 @@ public class Transitions implements RemoteCallable<Transitions> { /** Keeps track of currently tracked transitions and all the animations associated with each */ private final ArrayMap<IBinder, ActiveTransition> mActiveTransitions = new ArrayMap<>(); + public static RemoteTransitions asRemoteTransitions(Transitions transitions) { + return transitions.mImpl; + } + public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, @NonNull Context context, @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { @@ -125,20 +126,6 @@ public class Transitions implements RemoteCallable<Transitions> { mRemoteTransitionHandler = null; } - public ShellTransitions asRemoteTransitions() { - return mImpl; - } - - @Override - public Context getContext() { - return mContext; - } - - @Override - public ShellExecutor getRemoteCallExecutor() { - return mMainExecutor; - } - private void dispatchAnimScaleSetting(float scale) { for (int i = mHandlers.size() - 1; i >= 0; --i) { mHandlers.get(i).setAnimScaleSetting(scale); @@ -147,8 +134,8 @@ public class Transitions implements RemoteCallable<Transitions> { /** Create an empty/non-registering transitions object for system-ui tests. */ @VisibleForTesting - public static ShellTransitions createEmptyForTesting() { - return new ShellTransitions() { + public static RemoteTransitions createEmptyForTesting() { + return new RemoteTransitions() { @Override public void registerRemote(@androidx.annotation.NonNull TransitionFilter filter, @androidx.annotation.NonNull IRemoteTransition remoteTransition) { @@ -439,74 +426,24 @@ public class Transitions implements RemoteCallable<Transitions> { } } - /** - * The interface for calls from outside the Shell, within the host process. - */ @ExternalThread - private class ShellTransitionImpl implements ShellTransitions { - private IShellTransitionsImpl mIShellTransitions; - - @Override - public IShellTransitions createExternalInterface() { - if (mIShellTransitions != null) { - mIShellTransitions.invalidate(); - } - mIShellTransitions = new IShellTransitionsImpl(Transitions.this); - return mIShellTransitions; - } - + private class RemoteTransitionImpl implements RemoteTransitions { @Override public void registerRemote(@NonNull TransitionFilter filter, @NonNull IRemoteTransition remoteTransition) { mMainExecutor.execute(() -> { - mRemoteTransitionHandler.addFiltered(filter, remoteTransition); + Transitions.this.registerRemote(filter, remoteTransition); }); } @Override public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) { mMainExecutor.execute(() -> { - mRemoteTransitionHandler.removeFiltered(remoteTransition); + Transitions.this.unregisterRemote(remoteTransition); }); } } - /** - * The interface for calls from outside the host process. - */ - @BinderThread - private static class IShellTransitionsImpl extends IShellTransitions.Stub { - private Transitions mTransitions; - - IShellTransitionsImpl(Transitions transitions) { - mTransitions = transitions; - } - - /** - * Invalidates this instance, preventing future calls from updating the controller. - */ - void invalidate() { - mTransitions = null; - } - - @Override - public void registerRemote(@NonNull TransitionFilter filter, - @NonNull IRemoteTransition remoteTransition) { - executeRemoteCallWithTaskPermission(mTransitions, "registerRemote", - (transitions) -> { - transitions.mRemoteTransitionHandler.addFiltered(filter, remoteTransition); - }); - } - - @Override - public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) { - executeRemoteCallWithTaskPermission(mTransitions, "unregisterRemote", - (transitions) -> { - transitions.mRemoteTransitionHandler.removeFiltered(remoteTransition); - }); - } - } - private class SettingsObserver extends ContentObserver { SettingsObserver() { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java index 2f2bbba11646..c1c4c6dd08d7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java @@ -205,7 +205,7 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); } @@ -217,12 +217,12 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); } @@ -234,12 +234,12 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); } @@ -251,7 +251,7 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); } @@ -263,7 +263,7 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); } @@ -276,13 +276,13 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); } @@ -295,13 +295,13 @@ public class DragAndDropPolicyTest { mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM); mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData); - verify(mSplitScreenStarter).startIntent(any(), any(), + verify(mSplitScreenStarter).startIntent(any(), any(), any(), eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index b4db3055e58c..5f44b62da6f7 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -21,6 +21,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.annotation.TestApi; import android.content.Context; import android.content.pm.PackageManager; @@ -89,6 +90,7 @@ public final class MediaRouter2 { private final CopyOnWriteArrayList<ControllerCreationRequest> mControllerCreationRequests = new CopyOnWriteArrayList<>(); + // TODO: Specify the fields that are only used (or not used) by system media router. private final String mClientPackageName; private final ManagerCallback mManagerCallback; @@ -132,18 +134,34 @@ public final class MediaRouter2 { } /** - * Gets an instance of the media router which controls the app's media routing. + * Gets an instance of the system media router which controls the app's media routing. * Returns {@code null} if the given package name is invalid. + * There are several things to note when using the media routers created with this method. * <p> - * Note: For media routers created with this method, the discovery preference passed to - * {@link #registerRouteCallback} will have no effect. The callback will be called accordingly - * with the client app's discovery preference. Therefore, it is recommended to pass + * First of all, the discovery preference passed to {@link #registerRouteCallback} + * will have no effect. The callback will be called accordingly with the client app's + * discovery preference. Therefore, it is recommended to pass * {@link RouteDiscoveryPreference#EMPTY} there. + * <p> + * Also, do not keep/compare the instances of the {@link RoutingController}, since they are + * always newly created with the latest session information whenever below methods are called: + * <ul> + * <li> {@link #getControllers()} </li> + * <li> {@link #getController(String)}} </li> + * <li> {@link TransferCallback#onTransfer(RoutingController, RoutingController)} </li> + * <li> {@link TransferCallback#onStop(RoutingController)} </li> + * <li> {@link ControllerCallback#onControllerUpdated(RoutingController)} </li> + * </ul> + * Therefore, in order to track the current routing status, keep the controller's ID instead, + * and use {@link #getController(String)} and {@link #getSystemController()} for + * getting controllers. + * <p> + * Finally, it will have no effect to call {@link #setOnGetControllerHintsListener}. * * @param clientPackageName the package name of the app to control * @hide */ - //@SystemApi + @SystemApi @Nullable public static MediaRouter2 getInstance(@NonNull Context context, @NonNull String clientPackageName) { @@ -168,12 +186,40 @@ public final class MediaRouter2 { instance = new MediaRouter2(context, clientPackageName); sSystemMediaRouter2Map.put(clientPackageName, instance); // TODO: Remove router instance once it is not needed. - instance.registerManagerCallback(); + instance.registerManagerCallbackForSystemRouter(); } return instance; } } + /** + * Starts scanning remote routes. + * Note that calling start/stopScan is applied to all system routers in the same process. + * + * @see #stopScan() + * @hide + */ + @SystemApi + public void startScan() { + if (isSystemRouter()) { + sManager.startScan(); + } + } + + /** + * Stops scanning remote routes to reduce resource consumption. + * Note that calling start/stopScan is applied to all system routers in the same process. + * + * @see #startScan() + * @hide + */ + @SystemApi + public void stopScan() { + if (isSystemRouter()) { + sManager.stopScan(); + } + } + private MediaRouter2(Context appContext) { mContext = appContext; mMediaRouterService = IMediaRouterService.Stub.asInterface( @@ -209,13 +255,15 @@ public final class MediaRouter2 { } private MediaRouter2(Context context, String clientPackageName) { + mContext = context; mClientPackageName = clientPackageName; mManagerCallback = new ManagerCallback(); - mContext = context; - mMediaRouterService = null; - mPackageName = null; mHandler = new Handler(Looper.getMainLooper()); - mSystemController = null; + mSystemController = new SystemRoutingController(sManager.getSystemRoutingSession()); + mMediaRouterService = null; // TODO: Make this non-null and check permission. + + // Only used by non-system MediaRouter2. + mPackageName = null; } /** @@ -240,7 +288,7 @@ public final class MediaRouter2 { * @see #getInstance(Context, String) * @hide */ - //@SystemApi + @SystemApi @Nullable public String getClientPackageName() { return mClientPackageName; @@ -358,7 +406,8 @@ public final class MediaRouter2 { * * @hide */ - //@SystemApi + @SystemApi + @NonNull public List<MediaRoute2Info> getAllRoutes() { if (isSystemRouter()) { return sManager.getAllRoutes(); @@ -377,10 +426,6 @@ public final class MediaRouter2 { */ @NonNull public List<MediaRoute2Info> getRoutes() { - if (isSystemRouter()) { - return sManager.getAvailableRoutes(mClientPackageName); - } - synchronized (mLock) { if (mShouldUpdateRoutes) { mShouldUpdateRoutes = false; @@ -474,6 +519,9 @@ public final class MediaRouter2 { * {@code null} for unset. */ public void setOnGetControllerHintsListener(@Nullable OnGetControllerHintsListener listener) { + if (isSystemRouter()) { + return; + } mOnGetControllerHintsListener = listener; } @@ -519,7 +567,7 @@ public final class MediaRouter2 { * @param route the route you want to transfer the media to. * @hide */ - //@SystemApi + @SystemApi public void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) { if (isSystemRouter()) { sManager.transfer(controller.getRoutingSessionInfo(), route); @@ -606,6 +654,23 @@ public final class MediaRouter2 { } /** + * Gets a {@link RoutingController} whose ID is equal to the given ID. + * Returns {@code null} if there is no matching controller. + * @hide + */ + @SystemApi + @Nullable + public RoutingController getController(@NonNull String id) { + Objects.requireNonNull(id, "id must not be null"); + for (RoutingController controller : getControllers()) { + if (TextUtils.equals(id, controller.getId())) { + return controller; + } + } + return null; + } + + /** * Gets the list of currently active {@link RoutingController routing controllers} on which * media can be played. * <p> @@ -614,15 +679,25 @@ public final class MediaRouter2 { */ @NonNull public List<RoutingController> getControllers() { - // TODO: Do not create the controller instances every time, - // Instead, update the list using the sessions' ID and session related callbacks. + List<RoutingController> result = new ArrayList<>(); + if (isSystemRouter()) { - return sManager.getRoutingSessions(mClientPackageName).stream() - .map(info -> new RoutingController(info)) - .collect(Collectors.toList()); + // Unlike non-system MediaRouter2, controller instances cannot be kept, + // since the transfer events initiated from other apps will not come through manager. + List<RoutingSessionInfo> sessions = sManager.getRoutingSessions(mClientPackageName); + for (RoutingSessionInfo session : sessions) { + RoutingController controller; + if (session.isSystemSession()) { + mSystemController.setRoutingSessionInfo(session); + controller = mSystemController; + } else { + controller = new RoutingController(session); + } + result.add(controller); + } + return result; } - List<RoutingController> result = new ArrayList<>(); result.add(0, mSystemController); synchronized (mLock) { result.addAll(mNonSystemRoutingControllers.values()); @@ -639,9 +714,15 @@ public final class MediaRouter2 { * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}. * @hide */ + @SystemApi public void setRouteVolume(@NonNull MediaRoute2Info route, int volume) { Objects.requireNonNull(route, "route must not be null"); + if (isSystemRouter()) { + sManager.setRouteVolume(route, volume); + return; + } + MediaRouter2Stub stub; synchronized (mLock) { stub = mStub; @@ -928,8 +1009,9 @@ public final class MediaRouter2 { /** * Registers {@link MediaRouter2Manager.Callback} for getting events. + * Should only used for system media routers. */ - private void registerManagerCallback() { + private void registerManagerCallbackForSystemRouter() { // Using direct executor here, since MediaRouter2Manager also posts to the main handler. sManager.registerCallback(Runnable::run, mManagerCallback); } @@ -941,6 +1023,16 @@ public final class MediaRouter2 { .collect(Collectors.toList()); } + private void updateAllRoutesFromManager() { + synchronized (mLock) { + mRoutes.clear(); + for (MediaRoute2Info route : sManager.getAllRoutes()) { + mRoutes.put(route.getId(), route); + } + mShouldUpdateRoutes = true; + } + } + private void notifyRoutesAdded(List<MediaRoute2Info> routes) { for (RouteCallbackRecord record: mRouteCallbackRecords) { List<MediaRoute2Info> filteredRoutes = filterRoutes(routes, record.mPreference); @@ -971,6 +1063,13 @@ public final class MediaRouter2 { } } + private void notifyPreferredFeaturesChanged(List<String> features) { + for (RouteCallbackRecord record: mRouteCallbackRecords) { + record.mExecutor.execute( + () -> record.mRouteCallback.onPreferredFeaturesChanged(features)); + } + } + private void notifyTransfer(RoutingController oldController, RoutingController newController) { for (TransferCallbackRecord record: mTransferCallbackRecords) { record.mExecutor.execute( @@ -1024,6 +1123,17 @@ public final class MediaRouter2 { * @param routes the list of routes that have been changed. It's never empty. */ public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {} + + /** + * Called when the client app's preferred features are changed. + * When this is called, it is recommended to {@link #getRoutes()} to get the routes + * that are currently available to the app. + * + * @param preferredFeatures the new preferred features set by the application + * @hide + */ + @SystemApi + public void onPreferredFeaturesChanged(@NonNull List<String> preferredFeatures) {} } /** @@ -1131,6 +1241,11 @@ public final class MediaRouter2 { mState = CONTROLLER_STATE_ACTIVE; } + RoutingController(@NonNull RoutingSessionInfo sessionInfo, int state) { + mSessionInfo = sessionInfo; + mState = state; + } + /** * @return the ID of the controller. It is globally unique. */ @@ -1291,6 +1406,11 @@ public final class MediaRouter2 { return; } + if (isSystemRouter()) { + sManager.selectRoute(getRoutingSessionInfo(), route); + return; + } + MediaRouter2Stub stub; synchronized (mLock) { stub = mStub; @@ -1338,6 +1458,11 @@ public final class MediaRouter2 { return; } + if (isSystemRouter()) { + sManager.deselectRoute(getRoutingSessionInfo(), route); + return; + } + MediaRouter2Stub stub; synchronized (mLock) { stub = mStub; @@ -1407,6 +1532,12 @@ public final class MediaRouter2 { Log.w(TAG, "setVolume: Called on released controller. Ignoring."); return; } + + if (isSystemRouter()) { + sManager.setSessionVolume(getRoutingSessionInfo(), volume); + return; + } + MediaRouter2Stub stub; synchronized (mLock) { stub = mStub; @@ -1471,6 +1602,11 @@ public final class MediaRouter2 { mState = CONTROLLER_STATE_RELEASED; } + if (isSystemRouter()) { + sManager.releaseSession(getRoutingSessionInfo()); + return; + } + synchronized (mLock) { mNonSystemRoutingControllers.remove(getId(), this); @@ -1539,6 +1675,12 @@ public final class MediaRouter2 { } private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) { + if (isSystemRouter()) { + return getRoutes().stream() + .filter(r -> routeIds.contains(r.getId())) + .collect(Collectors.toList()); + } + synchronized (mLock) { return routeIds.stream().map(mRoutes::get) .filter(Objects::nonNull) @@ -1722,12 +1864,17 @@ public final class MediaRouter2 { } } + // Note: All methods are run on main thread. class ManagerCallback implements MediaRouter2Manager.Callback { @Override public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) { - List<MediaRoute2Info> filteredRoutes = - sManager.filterRoutesForPackage(routes, mClientPackageName); + updateAllRoutesFromManager(); + + List<MediaRoute2Info> filteredRoutes; + synchronized (mLock) { + filteredRoutes = filterRoutes(routes, mDiscoveryPreference); + } if (filteredRoutes.isEmpty()) { return; } @@ -1739,8 +1886,12 @@ public final class MediaRouter2 { @Override public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) { - List<MediaRoute2Info> filteredRoutes = - sManager.filterRoutesForPackage(routes, mClientPackageName); + updateAllRoutesFromManager(); + + List<MediaRoute2Info> filteredRoutes; + synchronized (mLock) { + filteredRoutes = filterRoutes(routes, mDiscoveryPreference); + } if (filteredRoutes.isEmpty()) { return; } @@ -1752,8 +1903,12 @@ public final class MediaRouter2 { @Override public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) { - List<MediaRoute2Info> filteredRoutes = - sManager.filterRoutesForPackage(routes, mClientPackageName); + updateAllRoutesFromManager(); + + List<MediaRoute2Info> filteredRoutes; + synchronized (mLock) { + filteredRoutes = filterRoutes(routes, mDiscoveryPreference); + } if (filteredRoutes.isEmpty()) { return; } @@ -1764,31 +1919,98 @@ public final class MediaRouter2 { } @Override - public void onSessionUpdated(@NonNull RoutingSessionInfo session) { - // TODO: Call ControllerCallback.onControllerUpdated - } - - @Override public void onTransferred(@NonNull RoutingSessionInfo oldSession, - @Nullable RoutingSessionInfo newSession) { - // TODO: Call TransferCallback.onTransfer + @NonNull RoutingSessionInfo newSession) { + if (!oldSession.isSystemSession() + && !TextUtils.equals(mClientPackageName, oldSession.getClientPackageName())) { + return; + } + + if (!newSession.isSystemSession() + && !TextUtils.equals(mClientPackageName, newSession.getClientPackageName())) { + return; + } + + // For successful in-session transfer, onControllerUpdated() handles it. + if (TextUtils.equals(oldSession.getId(), newSession.getId())) { + return; + } + + + RoutingController oldController; + if (oldSession.isSystemSession()) { + mSystemController.setRoutingSessionInfo(oldSession); + oldController = mSystemController; + } else { + oldController = new RoutingController(oldSession); + } + + RoutingController newController; + if (oldSession.isSystemSession()) { + mSystemController.setRoutingSessionInfo(newSession); + newController = mSystemController; + } else { + newController = new RoutingController(newSession); + } + + notifyTransfer(oldController, newController); } @Override public void onTransferFailed(@NonNull RoutingSessionInfo session, @NonNull MediaRoute2Info route) { - // TODO: Call TransferCallback.onTransferFailure + if (!session.isSystemSession() + && !TextUtils.equals(mClientPackageName, session.getClientPackageName())) { + return; + } + notifyTransferFailure(route); + } + + @Override + public void onSessionUpdated(@NonNull RoutingSessionInfo session) { + if (!session.isSystemSession() + && !TextUtils.equals(mClientPackageName, session.getClientPackageName())) { + return; + } + + RoutingController controller; + if (session.isSystemSession()) { + mSystemController.setRoutingSessionInfo(session); + controller = mSystemController; + } else { + controller = new RoutingController(session); + } + notifyControllerUpdated(controller); } @Override public void onSessionReleased(@NonNull RoutingSessionInfo session) { - // TODO: Call TransferCallback.onStop() + if (session.isSystemSession()) { + Log.e(TAG, "onSessionReleased: Called on system session. Ignoring."); + return; + } + + if (!TextUtils.equals(mClientPackageName, session.getClientPackageName())) { + return; + } + + notifyStop(new RoutingController(session, RoutingController.CONTROLLER_STATE_RELEASED)); } @Override public void onPreferredFeaturesChanged(@NonNull String packageName, @NonNull List<String> preferredFeatures) { - // Does nothing. + if (!TextUtils.equals(mClientPackageName, packageName)) { + return; + } + + synchronized (mLock) { + mDiscoveryPreference = new RouteDiscoveryPreference.Builder( + preferredFeatures, true).build(); + } + + updateAllRoutesFromManager(); + notifyPreferredFeaturesChanged(preferredFeatures); } @Override diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index ca619d4072c3..20e3573cfbe4 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -148,7 +148,7 @@ public final class MediaRouter2Manager { /** * Starts scanning remote routes. - * @see #stopScan(String) + * @see #stopScan() */ public void startScan() { Client client = getOrCreateClient(); @@ -163,7 +163,7 @@ public final class MediaRouter2Manager { /** * Stops scanning remote routes to reduce resource consumption. - * @see #startScan(String) + * @see #startScan() */ public void stopScan() { Client client = getOrCreateClient(); @@ -788,8 +788,8 @@ public final class MediaRouter2Manager { * Requests releasing a session. * <p> * If a session is released, any operation on the session will be ignored. - * {@link Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo)} with {@code null} - * session will be called when the session is released. + * {@link Callback#onSessionReleased(RoutingSessionInfo)} will be called + * when the session is released. * </p> * * @see Callback#onTransferred(RoutingSessionInfo, RoutingSessionInfo) @@ -945,10 +945,10 @@ public final class MediaRouter2Manager { * Called when media is transferred. * * @param oldSession the previous session - * @param newSession the new session or {@code null} if the session is released. + * @param newSession the new session */ default void onTransferred(@NonNull RoutingSessionInfo oldSession, - @Nullable RoutingSessionInfo newSession) { } + @NonNull RoutingSessionInfo newSession) { } /** * Called when {@link #transfer(RoutingSessionInfo, MediaRoute2Info)} fails. diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index b6fd286d3ad1..657435326d15 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -54,7 +54,7 @@ java_library { name: "SystemUI-sensors", srcs: [ "src/com/android/systemui/util/sensors/ThresholdSensor.java", - ] + ], } android_library { @@ -99,7 +99,7 @@ android_library { "SystemUI-tags", "SystemUI-proto", "dagger2", - "jsr330" + "jsr330", ], manifest: "AndroidManifest.xml", diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp index 9e67e4becc8e..d6204db97872 100644 --- a/packages/SystemUI/plugin/Android.bp +++ b/packages/SystemUI/plugin/Android.bp @@ -25,7 +25,10 @@ java_library { name: "SystemUIPluginLib", - srcs: ["src/**/*.java"], + srcs: [ + "src/**/*.java", + "bcsmartspace/src/**/*.java", + ], static_libs: [ "PluginCoreLib", diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java new file mode 100644 index 000000000000..f8a9a0459673 --- /dev/null +++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 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.systemui.plugins; + +import android.os.Parcelable; + +import com.android.systemui.plugins.annotations.ProvidesInterface; + +import java.util.List; + +/** + * Interface to provide SmartspaceTargets to BcSmartspace. + */ +@ProvidesInterface(action = BcSmartspaceDataPlugin.ACTION, version = BcSmartspaceDataPlugin.VERSION) +public interface BcSmartspaceDataPlugin extends Plugin { + String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA"; + int VERSION = 1; + + /** Register a listener to get Smartspace data. */ + void registerListener(SmartspaceTargetListener listener); + + /** Unregister a listener. */ + void unregisterListener(SmartspaceTargetListener listener); + + /** Provides Smartspace data to registered listeners. */ + interface SmartspaceTargetListener { + /** Each Parcelable is a SmartspaceTarget that represents a card. */ + void onSmartspaceTargetsUpdated(List<? extends Parcelable> targets); + } +} diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 70be7c67ad0d..a8083f164576 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -851,8 +851,6 @@ <string name="quick_settings_wifi_label">Wi-Fi</string> <!-- QuickSettings: Internet [CHAR LIMIT=NONE] --> <string name="quick_settings_internet_label">Internet</string> - <!-- QuickSettings: Airplane-safe [CHAR LIMIT=NONE] --> - <string name="quick_settings_airplane_safe_label">Airplane-safe</string> <!-- QuickSettings: networks available [CHAR LIMIT=NONE] --> <string name="quick_settings_networks_available">Networks available</string> <!-- QuickSettings: networks unavailable [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index f98a959346d3..09e9675a3277 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -41,7 +41,6 @@ android_library { srcs: [ "src/**/*.java", "src/**/I*.aidl", - ":wm_shell-aidls", ], static_libs: [ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl index 2569b780c1bb..97aa512ea7df 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl @@ -14,14 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.pip; +package com.android.systemui.shared.recents; /** - * Listener interface that Launcher attaches to SystemUI to get Pip animation callbacks. + * Listener interface that Launcher attaches to SystemUI to get + * pinned stack animation callbacks. */ -oneway interface IPipAnimationListener { +oneway interface IPinnedStackAnimationListener { /** - * Notifies the listener that the Pip animation is started. + * Notifies the pinned stack animation is started. */ - void onPipAnimationStarted(); + void onPinnedStackAnimationStarted(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreenListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl index faab4c2009cf..54242bece2b6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreenListener.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISplitScreenListener.aidl @@ -14,20 +14,12 @@ * limitations under the License. */ -package com.android.wm.shell.splitscreen; +package com.android.systemui.shared.recents; /** * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks. */ oneway interface ISplitScreenListener { - - /** - * Called when the stage position changes. - */ void onStagePositionChanged(int stage, int position); - - /** - * Called when a task changes stages. - */ void onTaskStageChanged(int taskId, int stage, boolean visible); -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindowListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IStartingWindowListener.aidl index f562c8fc4f85..eb3e60cec5c5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/IStartingWindowListener.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IStartingWindowListener.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.startingsurface; +package com.android.systemui.shared.recents; /** * Listener interface that Launcher attaches to SystemUI to get diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index 3da30856ff64..512628447530 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -16,6 +16,11 @@ package com.android.systemui.shared.recents; +import android.app.PendingIntent; +import android.app.PictureInPictureParams; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; @@ -23,15 +28,26 @@ import android.os.Bundle; import android.os.UserHandle; import android.view.MotionEvent; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; +import com.android.systemui.shared.recents.ISplitScreenListener; +import com.android.systemui.shared.recents.IStartingWindowListener; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.RemoteTransitionCompat; /** * Temporary callbacks into SystemUI. + * Next id = 44 */ interface ISystemUiProxy { /** + * Proxies SurfaceControl.screenshotToBuffer(). + * @Removed + * GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer, + * int maxLayer, boolean useIdentityTransform, int rotation) = 0; + */ + + /** * Begins screen pinning on the provided {@param taskId}. */ void startScreenPinning(int taskId) = 1; @@ -99,6 +115,11 @@ interface ISystemUiProxy { void stopScreenPinning() = 17; /** + * Sets the shelf height and visibility. + */ + void setShelfHeight(boolean visible, int shelfHeight) = 20; + + /** * Handle the provided image as if it was a screenshot. * * Deprecated, use handleImageBundleAsScreenshot with image bundle and UserTask @@ -118,12 +139,27 @@ interface ISystemUiProxy { void notifySwipeToHomeFinished() = 23; /** + * Sets listener to get pinned stack animation callbacks. + */ + void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) = 24; + + /** * Notifies that quickstep will switch to a new task * @param rotation indicates which Surface.Rotation the gesture was started in */ void onQuickSwitchToNewTask(int rotation) = 25; /** + * Start the one-handed mode. + */ + void startOneHandedMode() = 26; + + /** + * Stop the one-handed mode. + */ + void stopOneHandedMode() = 27; + + /** * Handle the provided image as if it was a screenshot. */ void handleImageBundleAsScreenshot(in Bundle screenImageBundle, in Rect locationInScreen, @@ -134,5 +170,88 @@ interface ISystemUiProxy { */ void expandNotificationPanel() = 29; - // Next id = 44 + /** + * Notifies that Activity is about to be swiped to home with entering PiP transition and + * queries the destination bounds for PiP depends on Launcher's rotation and shelf height. + * + * @param componentName ComponentName represents the Activity + * @param activityInfo ActivityInfo tied to the Activity + * @param pictureInPictureParams PictureInPictureParams tied to the Activity + * @param launcherRotation Launcher rotation to calculate the PiP destination bounds + * @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds + * @return destination bounds the PiP window should land into + */ + Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo, + in PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) = 30; + + /** + * Notifies the swiping Activity to PiP onto home transition is finished + * + * @param componentName ComponentName represents the Activity + * @param destinationBounds the destination bounds the PiP window lands into + */ + void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 31; + + /** + * Registers a RemoteTransitionCompat that will handle transitions. This parameter bundles an + * IRemoteTransition and a filter that must pass for it. + */ + void registerRemoteTransition(in RemoteTransitionCompat remoteTransition) = 32; + + /** Unegisters a RemoteTransitionCompat that will handle transitions. */ + void unregisterRemoteTransition(in RemoteTransitionCompat remoteTransition) = 33; + +// SplitScreen APIs...copied from SplitScreen.java + /** + * Stage position isn't specified normally meaning to use what ever it is currently set to. + */ + //int STAGE_POSITION_UNDEFINED = -1; + /** + * Specifies that a stage is positioned at the top half of the screen if + * in portrait mode or at the left half of the screen if in landscape mode. + */ + //int STAGE_POSITION_TOP_OR_LEFT = 0; + /** + * Specifies that a stage is positioned at the bottom half of the screen if + * in portrait mode or at the right half of the screen if in landscape mode. + */ + //int STAGE_POSITION_BOTTOM_OR_RIGHT = 1; + + /** + * Stage type isn't specified normally meaning to use what ever the default is. + * E.g. exit split-screen and launch the app in fullscreen. + */ + //int STAGE_TYPE_UNDEFINED = -1; + /** + * The main stage type. + * @see MainStage + */ + //int STAGE_TYPE_MAIN = 0; + /** + * The side stage type. + * @see SideStage + */ + //int STAGE_TYPE_SIDE = 1; + + void registerSplitScreenListener(in ISplitScreenListener listener) = 34; + void unregisterSplitScreenListener(in ISplitScreenListener listener) = 35; + + /** Hides the side-stage if it is currently visible. */ + void setSideStageVisibility(in boolean visible) = 36; + /** Removes the split-screen stages. */ + void exitSplitScreen() = 37; + /** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */ + void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 38; + void startTask(in int taskId, in int stage, in int position, in Bundle options) = 39; + void startShortcut(in String packageName, in String shortcutId, in int stage, in int position, + in Bundle options, in UserHandle user) = 40; + void startIntent( + in PendingIntent intent, in Intent fillInIntent, in int stage, in int position, + in Bundle options) = 41; + void removeFromSideStage(in int taskId) = 42; + /** + * Sets listener to get task launching callbacks. + */ + void setStartingWindowListener(IStartingWindowListener listener) = 43; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 41840afc4995..937c1df10315 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -41,18 +41,6 @@ public class QuickStepContract { public static final String KEY_EXTRA_INPUT_MONITOR = "extra_input_monitor"; public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius"; public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners"; - // See IPip.aidl - public static final String KEY_EXTRA_SHELL_PIP = "extra_shell_pip"; - // See ISplitScreen.aidl - public static final String KEY_EXTRA_SHELL_SPLIT_SCREEN = "extra_shell_split_screen"; - // See IOneHanded.aidl - public static final String KEY_EXTRA_SHELL_ONE_HANDED = "extra_shell_one_handed"; - // See IShellTransitions.aidl - public static final String KEY_EXTRA_SHELL_SHELL_TRANSITIONS = - "extra_shell_shell_transitions"; - // See IStartingWindow.aidl - public static final String KEY_EXTRA_SHELL_STARTING_WINDOW = - "extra_shell_starting_window"; public static final String NAV_BAR_MODE_2BUTTON_OVERLAY = WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index ed3d5ec33b41..8f79de518419 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -34,7 +34,7 @@ import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; -import com.android.wm.shell.transition.ShellTransitions; +import com.android.wm.shell.transition.RemoteTransitions; import java.util.Optional; @@ -87,7 +87,7 @@ public interface SysUIComponent { Builder setShellCommandHandler(Optional<ShellCommandHandler> shellDump); @BindsInstance - Builder setTransitions(ShellTransitions t); + Builder setTransitions(RemoteTransitions t); @BindsInstance Builder setStartingSurface(Optional<StartingSurface> s); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index bbd95b4d0c90..1b77d1c16639 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -33,7 +33,7 @@ import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; -import com.android.wm.shell.transition.ShellTransitions; +import com.android.wm.shell.transition.RemoteTransitions; import java.util.Optional; @@ -98,7 +98,7 @@ public interface WMComponent { Optional<TaskViewFactory> getTaskViewFactory(); @WMSingleton - ShellTransitions getTransitions(); + RemoteTransitions getTransitions(); @WMSingleton Optional<StartingSurface> getStartingSurface(); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index b4716598cc0c..c3a523f82d9d 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -35,6 +35,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE; import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; @@ -85,6 +86,7 @@ import android.text.TextUtils; import android.util.Log; import android.view.Display; import android.view.Gravity; +import android.view.HapticFeedbackConstants; import android.view.IWindowManager; import android.view.InsetsState.InternalInsetsType; import android.view.KeyEvent; @@ -213,6 +215,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private boolean mAllowForceNavBarHandleOpaque; private boolean mForceNavBarHandleOpaque; + private Optional<Long> mHomeButtonLongPressDurationMs; private boolean mIsCurrentUserSetup; /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */ @@ -384,6 +387,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true); + private final Runnable mOnVariableDurationHomeLongClick = () -> { + if (onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView())) { + mNavigationBarView.getHomeButton().getCurrentView().performHapticFeedback( + HapticFeedbackConstants.LONG_PRESS, + HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } + }; + private final ContentObserver mAssistContentObserver = new ContentObserver( new Handler(Looper.getMainLooper())) { @Override @@ -405,6 +416,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mForceNavBarHandleOpaque = properties.getBoolean( NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true); } + + if (properties.getKeyset().contains(HOME_BUTTON_LONG_PRESS_DURATION_MS)) { + mHomeButtonLongPressDurationMs = Optional.of( + properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0) + ).filter(duration -> duration != 0); + reconfigureHomeLongClick(); + } } }; @@ -523,6 +541,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener, DeviceConfig.NAMESPACE_SYSTEMUI, NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true); + mHomeButtonLongPressDurationMs = Optional.of(DeviceConfig.getLong( + DeviceConfig.NAMESPACE_SYSTEMUI, + HOME_BUTTON_LONG_PRESS_DURATION_MS, + /* defaultValue = */ 0 + )).filter(duration -> duration != 0); DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener); @@ -782,6 +805,22 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } } + private void reconfigureHomeLongClick() { + if (mNavigationBarView == null + || mNavigationBarView.getHomeButton().getCurrentView() == null) { + return; + } + if (mHomeButtonLongPressDurationMs.isPresent()) { + mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(false); + mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(false); + mNavigationBarView.getHomeButton().setOnLongClickListener(null); + } else { + mNavigationBarView.getHomeButton().getCurrentView().setLongClickable(true); + mNavigationBarView.getHomeButton().getCurrentView().setHapticFeedbackEnabled(true); + mNavigationBarView.getHomeButton().setOnLongClickListener(this::onHomeLongClick); + } + } + private int deltaRotation(int oldRotation, int newRotation) { int delta = newRotation - oldRotation; if (delta < 0) delta += 4; @@ -792,6 +831,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, pw.println("NavigationBar (displayId=" + mDisplayId + "):"); pw.println(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation); pw.println(" mCurrentRotation=" + mCurrentRotation); + pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs); if (mNavigationBarView != null) { pw.println(" mNavigationBarWindowState=" @@ -1121,7 +1161,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, ButtonDispatcher homeButton = mNavigationBarView.getHomeButton(); homeButton.setOnTouchListener(this::onHomeTouch); - homeButton.setOnLongClickListener(this::onHomeLongClick); + + reconfigureHomeLongClick(); ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton(); accessibilityButton.setOnClickListener(this::onAccessibilityClick); @@ -1131,7 +1172,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, updateScreenPinningGestures(); } - private boolean onHomeTouch(View v, MotionEvent event) { + @VisibleForTesting + boolean onHomeTouch(View v, MotionEvent event) { if (mHomeBlockedThisTouch && event.getActionMasked() != MotionEvent.ACTION_DOWN) { return true; } @@ -1151,9 +1193,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener, return true; } } + mHomeButtonLongPressDurationMs.ifPresent(longPressDuration -> { + mHandler.postDelayed(mOnVariableDurationHomeLongClick, longPressDuration); + }); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: + mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); mStatusBarLazy.get().awakenDreams(); break; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java index 14a3fc0bcd73..1a17e61111d9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java @@ -416,7 +416,6 @@ public class InternetTile extends QSTileImpl<SignalState> { } } else { state.icon = ResourceIcon.get(cb.mWifiSignalIconId); - state.label = r.getString(R.string.quick_settings_airplane_safe_label); } } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) { state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); @@ -480,9 +479,6 @@ public class InternetTile extends QSTileImpl<SignalState> { state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available); state.secondaryLabel = r.getString(R.string.quick_settings_networks_available); } else { - if (cb.mAirplaneModeEnabled) { - state.label = r.getString(R.string.quick_settings_airplane_safe_label); - } state.icon = new SignalIcon(cb.mMobileSignalIconId); state.secondaryLabel = appendMobileDataType(cb.mDataSubscriptionName, getMobileDataContentName(cb)); diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index b0a3f437b5ec..8951605846a1 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -25,11 +25,6 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS; @@ -40,12 +35,15 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_T import android.annotation.FloatRange; import android.app.ActivityTaskManager; +import android.app.PendingIntent; +import android.app.PictureInPictureParams; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; @@ -59,12 +57,14 @@ import android.os.Looper; import android.os.PatternMatcher; import android.os.RemoteException; import android.os.UserHandle; +import android.util.ArraySet; import android.util.Log; import android.view.InputMonitor; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; import android.view.accessibility.AccessibilityManager; +import android.window.IRemoteTransition; import androidx.annotation.NonNull; @@ -83,11 +83,15 @@ import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.shared.recents.IOverviewProxy; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; +import com.android.systemui.shared.recents.ISplitScreenListener; +import com.android.systemui.shared.recents.IStartingWindowListener; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.shared.system.RemoteTransitionCompat; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; @@ -99,7 +103,7 @@ import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; -import com.android.wm.shell.transition.ShellTransitions; +import com.android.wm.shell.transition.RemoteTransitions; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -107,6 +111,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.function.BiConsumer; +import java.util.function.Consumer; import javax.inject.Inject; @@ -146,11 +151,12 @@ public class OverviewProxyService extends CurrentUserTracker implements private final ScreenshotHelper mScreenshotHelper; private final Optional<OneHanded> mOneHandedOptional; private final CommandQueue mCommandQueue; - private final ShellTransitions mShellTransitions; + private final RemoteTransitions mShellTransitions; private final Optional<StartingSurface> mStartingSurface; private Region mActiveNavBarRegion; + private IPinnedStackAnimationListener mIPinnedStackAnimationListener; private IOverviewProxy mOverviewProxy; private int mConnectionBackoffAttempts; private boolean mBound; @@ -163,6 +169,8 @@ public class OverviewProxyService extends CurrentUserTracker implements private float mWindowCornerRadius; private boolean mSupportsRoundedCornersOnWindows; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; + private final ArraySet<IRemoteTransition> mRemoteTransitions = new ArraySet<>(); + private IStartingWindowListener mIStartingWindowListener; @VisibleForTesting public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() { @@ -375,6 +383,20 @@ public class OverviewProxyService extends CurrentUserTracker implements } @Override + public void setShelfHeight(boolean visible, int shelfHeight) { + if (!verifyCaller("setShelfHeight")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mPipOptional.ifPresent( + pip -> pip.setShelfHeight(visible, shelfHeight)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen, Insets visibleInsets, int taskId) { // Deprecated @@ -402,6 +424,36 @@ public class OverviewProxyService extends CurrentUserTracker implements } @Override + public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { + if (!verifyCaller("setPinnedStackAnimationListener")) { + return; + } + mIPinnedStackAnimationListener = listener; + final long token = Binder.clearCallingIdentity(); + try { + mPipOptional.ifPresent( + pip -> pip.setPinnedStackAnimationListener(mPinnedStackAnimationCallback)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void setStartingWindowListener(IStartingWindowListener listener) { + if (!verifyCaller("setStartingWindowListener")) { + return; + } + mIStartingWindowListener = listener; + final long token = Binder.clearCallingIdentity(); + try { + mStartingSurface.ifPresent(s -> + s.setStartingWindowListener(mStartingWindowListener)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void onQuickSwitchToNewTask(@Surface.Rotation int rotation) { if (!verifyCaller("onQuickSwitchToNewTask")) { return; @@ -415,6 +467,32 @@ public class OverviewProxyService extends CurrentUserTracker implements } @Override + public void startOneHandedMode() { + if (!verifyCaller("startOneHandedMode")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mOneHandedOptional.ifPresent(oneHanded -> oneHanded.startOneHanded()); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void stopOneHandedMode() { + if (!verifyCaller("stopOneHandedMode")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mOneHandedOptional.ifPresent(oneHanded -> oneHanded.stopOneHanded()); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen, Insets visibleInsets, Task.TaskKey task) { mScreenshotHelper.provideScreenshot( @@ -442,6 +520,190 @@ public class OverviewProxyService extends CurrentUserTracker implements } } + @Override + public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) { + if (!verifyCaller("startSwipePipToHome")) { + return null; + } + final long binderToken = Binder.clearCallingIdentity(); + try { + return mPipOptional.map(pip -> + pip.startSwipePipToHome(componentName, activityInfo, + pictureInPictureParams, launcherRotation, shelfHeight)) + .orElse(null); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + @Override + public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + if (!verifyCaller("stopSwipePipToHome")) { + return; + } + final long binderToken = Binder.clearCallingIdentity(); + try { + mPipOptional.ifPresent(pip -> pip.stopSwipePipToHome( + componentName, destinationBounds)); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + @Override + public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) { + if (!verifyCaller("registerRemoteTransition")) return; + final long binderToken = Binder.clearCallingIdentity(); + try { + mRemoteTransitions.add(remoteTransition.getTransition()); + mShellTransitions.registerRemote( + remoteTransition.getFilter(), remoteTransition.getTransition()); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + @Override + public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) { + if (!verifyCaller("registerRemoteTransition")) return; + final long binderToken = Binder.clearCallingIdentity(); + try { + mRemoteTransitions.remove(remoteTransition.getTransition()); + mShellTransitions.unregisterRemote(remoteTransition.getTransition()); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + @Override + public void registerSplitScreenListener(ISplitScreenListener listener) { + if (!verifyCaller("registerSplitScreenListener")) { + return; + } + mISplitScreenListener = listener; + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent( + s -> s.registerSplitScreenListener(mSplitScreenListener)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void unregisterSplitScreenListener(ISplitScreenListener listener) { + if (!verifyCaller("unregisterSplitScreenListener")) { + return; + } + mISplitScreenListener = null; + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent( + s -> s.unregisterSplitScreenListener(mSplitScreenListener)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void setSideStageVisibility(boolean visible) { + if (!verifyCaller("setSideStageVisibility")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent(s -> s.setSideStageVisibility(visible)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void exitSplitScreen() { + if (!verifyCaller("exitSplitScreen")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent(s -> s.exitSplitScreen()); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { + if (!verifyCaller("exitSplitScreenOnHide")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent(s -> s.exitSplitScreenOnHide(exitSplitScreenOnHide)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void startTask(int taskId, int stage, int position, Bundle options) { + if (!verifyCaller("startTask")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent( + s -> s.startTask(taskId, stage, position, options)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void startShortcut(String packageName, String shortcutId, int stage, int position, + Bundle options, UserHandle user) { + if (!verifyCaller("startShortcut")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent(s -> + s.startShortcut(packageName, shortcutId, stage, position, options, user)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void startIntent(PendingIntent intent, Intent fillInIntent, + int stage, int position, Bundle options) { + if (!verifyCaller("startIntent")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent(s -> + s.startIntent(intent, mContext, fillInIntent, stage, position, options)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void removeFromSideStage(int taskId) { + if (!verifyCaller("removeFromSideStage")) { + return; + } + final long token = Binder.clearCallingIdentity(); + try { + mSplitScreenOptional.ifPresent( + s -> s.removeFromSideStage(taskId)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private boolean verifyCaller(String reason) { final int callerId = Binder.getCallingUserHandle().getIdentifier(); if (callerId != mCurrentBoundedUserId) { @@ -495,22 +757,6 @@ public class OverviewProxyService extends CurrentUserTracker implements params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder()); params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius); params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows); - - mPipOptional.ifPresent((pip) -> params.putBinder( - KEY_EXTRA_SHELL_PIP, - pip.createExternalInterface().asBinder())); - mSplitScreenOptional.ifPresent((splitscreen) -> params.putBinder( - KEY_EXTRA_SHELL_SPLIT_SCREEN, - splitscreen.createExternalInterface().asBinder())); - mOneHandedOptional.ifPresent((onehanded) -> params.putBinder( - KEY_EXTRA_SHELL_ONE_HANDED, - onehanded.createExternalInterface().asBinder())); - params.putBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS, - mShellTransitions.createExternalInterface().asBinder()); - mStartingSurface.ifPresent((startingwindow) -> params.putBinder( - KEY_EXTRA_SHELL_STARTING_WINDOW, - startingwindow.createExternalInterface().asBinder())); - try { mOverviewProxy.onInitialize(params); } catch (RemoteException e) { @@ -550,11 +796,42 @@ public class OverviewProxyService extends CurrentUserTracker implements private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged; private final BiConsumer<Rect, Rect> mSplitScreenBoundsChangeListener = this::notifySplitScreenBoundsChanged; + private final Consumer<Boolean> mPinnedStackAnimationCallback = + this::notifyPinnedStackAnimationStarted; + + private final BiConsumer<Integer, Integer> mStartingWindowListener = + this::notifyTaskLaunching; // This is the death handler for the binder from the launcher service private final IBinder.DeathRecipient mOverviewServiceDeathRcpt = this::cleanupAfterDeath; + private ISplitScreenListener mISplitScreenListener; + private final SplitScreen.SplitScreenListener mSplitScreenListener = + new SplitScreen.SplitScreenListener() { + @Override + public void onStagePositionChanged(int stage, int position) { + try { + if (mISplitScreenListener != null) { + mISplitScreenListener.onStagePositionChanged(stage, position); + } + } catch (RemoteException e) { + Log.e(TAG_OPS, "onStagePositionChanged", e); + } + } + + @Override + public void onTaskStageChanged(int taskId, int stage, boolean visible) { + try { + if (mISplitScreenListener != null) { + mISplitScreenListener.onTaskStageChanged(taskId, stage, visible); + } + } catch (RemoteException e) { + Log.e(TAG_OPS, "onTaskStageChanged", e); + } + } + }; + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @Inject public OverviewProxyService(Context context, CommandQueue commandQueue, @@ -567,7 +844,7 @@ public class OverviewProxyService extends CurrentUserTracker implements Optional<Lazy<StatusBar>> statusBarOptionalLazy, Optional<OneHanded> oneHandedOptional, BroadcastDispatcher broadcastDispatcher, - ShellTransitions shellTransitions, + RemoteTransitions shellTransitions, Optional<StartingSurface> startingSurface) { super(broadcastDispatcher); mContext = context; @@ -684,6 +961,29 @@ public class OverviewProxyService extends CurrentUserTracker implements } } + private void notifyPinnedStackAnimationStarted(Boolean isAnimationStarted) { + if (mIPinnedStackAnimationListener == null) { + return; + } + try { + mIPinnedStackAnimationListener.onPinnedStackAnimationStarted(); + } catch (RemoteException e) { + Log.e(TAG_OPS, "Failed to call onPinnedStackAnimationStarted()", e); + } + } + + private void notifyTaskLaunching(int taskId, int supportedType) { + if (mIStartingWindowListener == null) { + return; + } + + try { + mIStartingWindowListener.onTaskLaunching(taskId, supportedType); + } catch (RemoteException e) { + Log.e(TAG_OPS, "Failed to call notifyTaskLaunching()", e); + } + } + private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing) { mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING, @@ -727,6 +1027,12 @@ public class OverviewProxyService extends CurrentUserTracker implements // Clean up the minimized state if launcher dies mLegacySplitScreenOptional.ifPresent( splitScreen -> splitScreen.setMinimized(false)); + + // Clean up any registered remote transitions + for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) { + mShellTransitions.unregisterRemote(mRemoteTransitions.valueAt(i)); + } + mRemoteTransitions.clear(); } public void startConnectionToCurrentUser() { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index ddfa63a33149..3f4ec8539d38 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -81,7 +81,7 @@ import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.startingsurface.StartingSurface; import com.android.wm.shell.startingsurface.StartingWindowController; -import com.android.wm.shell.transition.ShellTransitions; +import com.android.wm.shell.transition.RemoteTransitions; import com.android.wm.shell.transition.Transitions; import java.util.Optional; @@ -399,8 +399,8 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static ShellTransitions provideRemoteTransitions(Transitions transitions) { - return transitions.asRemoteTransitions(); + static RemoteTransitions provideRemoteTransitions(Transitions transitions) { + return Transitions.asRemoteTransitions(transitions); } @WMSingleton @@ -509,33 +509,27 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static ShellInit provideShellInit(ShellInitImpl impl) { - return impl.asShellInit(); - } - - @WMSingleton - @Provides - static ShellInitImpl provideShellInitImpl(DisplayImeController displayImeController, + static ShellInit provideShellInit(DisplayImeController displayImeController, DragAndDropController dragAndDropController, ShellTaskOrganizer shellTaskOrganizer, Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<AppPairsController> appPairsOptional, + Optional<StartingSurface> startingSurface, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, Transitions transitions, - StartingWindowController startingWindow, @ShellMainThread ShellExecutor mainExecutor) { - return new ShellInitImpl(displayImeController, + return ShellInitImpl.create(displayImeController, dragAndDropController, shellTaskOrganizer, legacySplitScreenOptional, splitScreenOptional, appPairsOptional, + startingSurface, pipTouchHandlerOptional, fullscreenTaskListener, transitions, - startingWindow, mainExecutor); } @@ -545,13 +539,7 @@ public abstract class WMShellBaseModule { */ @WMSingleton @Provides - static Optional<ShellCommandHandler> provideShellCommandHandler(ShellCommandHandlerImpl impl) { - return Optional.of(impl.asShellCommandHandler()); - } - - @WMSingleton - @Provides - static ShellCommandHandlerImpl provideShellCommandHandlerImpl( + static Optional<ShellCommandHandler> provideShellCommandHandler( ShellTaskOrganizer shellTaskOrganizer, Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, @@ -560,8 +548,8 @@ public abstract class WMShellBaseModule { Optional<HideDisplayCutoutController> hideDisplayCutout, Optional<AppPairsController> appPairsOptional, @ShellMainThread ShellExecutor mainExecutor) { - return new ShellCommandHandlerImpl(shellTaskOrganizer, + return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer, legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional, - hideDisplayCutout, appPairsOptional, mainExecutor); + hideDisplayCutout, appPairsOptional, mainExecutor)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index 2b76f1c2d76d..22c553b764b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -23,6 +23,7 @@ import static android.inputmethodservice.InputMethodService.IME_VISIBLE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS; import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS; import static org.junit.Assert.assertEquals; @@ -31,6 +32,7 @@ import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -44,12 +46,16 @@ import android.content.IntentFilter; import android.hardware.display.DisplayManagerGlobal; import android.os.Handler; import android.os.Looper; +import android.os.SystemClock; import android.os.UserHandle; +import android.provider.DeviceConfig; +import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.Display; import android.view.DisplayInfo; +import android.view.MotionEvent; import android.view.WindowManager; import android.view.WindowMetrics; import android.view.accessibility.AccessibilityManager; @@ -79,6 +85,7 @@ import com.android.systemui.utils.leaks.LeakCheckedTest; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.Pip; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -101,6 +108,7 @@ public class NavigationBarTest extends SysuiTestCase { private OverviewProxyService mOverviewProxyService; private CommandQueue mCommandQueue; private SysUiState mMockSysUiState; + @Mock private Handler mHandler; @Mock private BroadcastDispatcher mBroadcastDispatcher; @@ -124,12 +132,17 @@ public class NavigationBarTest extends SysuiTestCase { mDependency.injectMockDependency(NavigationBarController.class); mOverviewProxyService = mDependency.injectMockDependency(OverviewProxyService.class); TestableLooper.get(this).runWithLooper(() -> { - mHandler = new Handler(); mNavigationBar = createNavBar(mContext); mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal); }); } + @After + public void tearDown() throws Exception { + DeviceConfig.resetToDefaults( + Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI); + } + private void setupSysuiDependency() { Display display = new Display(DisplayManagerGlobal.getInstance(), EXTERNAL_DISPLAY_ID, new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS); @@ -163,6 +176,32 @@ public class NavigationBarTest extends SysuiTestCase { } @Test + public void testHomeLongPressWithCustomDuration() throws Exception { + DeviceConfig.setProperties( + new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_SYSTEMUI) + .setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100) + .build()); + mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null)); + + mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain( + /*downTime=*/SystemClock.uptimeMillis(), + /*eventTime=*/SystemClock.uptimeMillis(), + /*action=*/MotionEvent.ACTION_DOWN, + 0, 0, 0 + )); + verify(mHandler, times(1)).postDelayed(any(), eq(100L)); + + mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain( + /*downTime=*/SystemClock.uptimeMillis(), + /*eventTime=*/SystemClock.uptimeMillis(), + /*action=*/MotionEvent.ACTION_UP, + 0, 0, 0 + )); + + verify(mHandler, times(1)).removeCallbacks(any()); + } + + @Test public void testRegisteredWithDispatcher() { mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null)); verify(mBroadcastDispatcher).registerReceiverWithHandler( diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java new file mode 100644 index 000000000000..25104b8b1d20 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java @@ -0,0 +1,123 @@ +/* + * 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.systemui.recents; + +import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.pm.PackageManager; +import android.os.RemoteException; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableContext; +import android.testing.TestableLooper; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.NavigationBarController; +import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; +import com.android.wm.shell.pip.Pip; +import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.startingsurface.StartingSurface; +import com.android.wm.shell.transition.RemoteTransitions; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Optional; + +import dagger.Lazy; + +/** + * Unit tests for {@link com.android.systemui.recents.OverviewProxyService} + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class OverviewProxyServiceTest extends SysuiTestCase { + private OverviewProxyService mSpiedOverviewProxyService; + private TestableContext mSpiedContext; + + @Mock private BroadcastDispatcher mMockBroadcastDispatcher; + @Mock private CommandQueue mMockCommandQueue; + @Mock private Lazy<NavigationBarController> mMockNavBarControllerLazy; + @Mock private IPinnedStackAnimationListener mMockPinnedStackAnimationListener; + @Mock private NavigationModeController mMockNavModeController; + @Mock private NotificationShadeWindowController mMockStatusBarWinController; + @Mock private Optional<Pip> mMockPipOptional; + @Mock private Optional<LegacySplitScreen> mMockLegacySplitScreenOptional; + @Mock private Optional<SplitScreen> mMockSplitScreenOptional; + @Mock private Optional<Lazy<StatusBar>> mMockStatusBarOptionalLazy; + @Mock private Optional<com.android.wm.shell.onehanded.OneHanded> mMockOneHandedOptional; + @Mock private PackageManager mPackageManager; + @Mock private SysUiState mMockSysUiState; + @Mock private RemoteTransitions mMockTransitions; + @Mock private Optional<StartingSurface> mStartingSurface; + + @Before + public void setUp() throws RemoteException { + MockitoAnnotations.initMocks(this); + + mSpiedContext = spy(mContext); + + when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false); + when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager); + + mSpiedOverviewProxyService = spy(new OverviewProxyService(mSpiedContext, mMockCommandQueue, + mMockNavBarControllerLazy, mMockNavModeController, mMockStatusBarWinController, + mMockSysUiState, mMockPipOptional, mMockLegacySplitScreenOptional, + mMockSplitScreenOptional, mMockStatusBarOptionalLazy, mMockOneHandedOptional, + mMockBroadcastDispatcher, mMockTransitions, mStartingSurface)); + } + + @Test + public void testNonPipDevice_shouldNotNotifySwipeToHomeFinished() throws RemoteException { + mSpiedOverviewProxyService.mSysUiProxy.notifySwipeToHomeFinished(); + + verify(mMockPipOptional, never()).ifPresent(any()); + } + + @Test + public void testNonPipDevice_shouldNotSetPinnedStackAnimationListener() throws RemoteException { + mSpiedOverviewProxyService.mSysUiProxy.setPinnedStackAnimationListener( + mMockPinnedStackAnimationListener); + + verify(mMockPipOptional, never()).ifPresent(any()); + } + + @Test + public void testNonPipDevice_shouldNotSetShelfHeight() throws RemoteException { + mSpiedOverviewProxyService.mSysUiProxy.setShelfHeight(true /* visible */, + 100 /* shelfHeight */); + + verify(mMockPipOptional, never()).ifPresent(any()); + } +} diff --git a/services/core/Android.bp b/services/core/Android.bp index b00689be3656..0fe874c0840a 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -81,10 +81,19 @@ genrule { out: ["services.core.protolog.json"], } +genrule { + name: "statslog-art-java-gen", + tools: ["stats-log-api-gen"], + cmd: "$(location stats-log-api-gen) --java $(out) --module art" + + " --javaPackage com.android.internal.art --javaClass ArtStatsLog --worksource", + out: ["com/android/internal/art/ArtStatsLog.java"], +} + java_library_static { name: "services.core.unboosted", defaults: ["platform_service_defaults"], srcs: [ + ":statslog-art-java-gen", ":services.core-sources", ":services.core.protologsrc", ":dumpstate_aidl", diff --git a/services/core/java/com/android/server/ContextHubSystemService.java b/services/core/java/com/android/server/ContextHubSystemService.java index a35351958f43..96ff9006acce 100644 --- a/services/core/java/com/android/server/ContextHubSystemService.java +++ b/services/core/java/com/android/server/ContextHubSystemService.java @@ -16,6 +16,8 @@ package com.android.server; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.util.Log; @@ -51,4 +53,9 @@ class ContextHubSystemService extends SystemService { publishBinderService(Context.CONTEXTHUB_SERVICE, mContextHubService); } } + + @Override + public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { + mContextHubService.onUserChanged(); + } } diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index dde45c4ab52a..c44089b7817f 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -17,13 +17,16 @@ package com.android.server.location.contexthub; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.UserInfo; import android.database.ContentObserver; import android.hardware.SensorPrivacyManager; +import android.hardware.SensorPrivacyManagerInternal; import android.hardware.contexthub.V1_0.AsyncEventType; import android.hardware.contexthub.V1_0.ContextHub; import android.hardware.contexthub.V1_0.ContextHubMsg; @@ -59,6 +62,7 @@ import android.util.Pair; import android.util.proto.ProtoOutputStream; import com.android.internal.util.DumpUtils; +import com.android.server.LocalServices; import com.android.server.location.ContextHubServiceProto; import java.io.FileDescriptor; @@ -127,6 +131,8 @@ public class ContextHubService extends IContextHubService.Stub { // Lock object for sendWifiSettingUpdate() private final Object mSendWifiSettingUpdateLock = new Object(); + private final SensorPrivacyManagerInternal mSensorPrivacyManagerInternal; + /** * Class extending the callback to register with a Context Hub. */ @@ -186,6 +192,7 @@ public class ContextHubService extends IContextHubService.Stub { if (mContextHubWrapper == null) { mTransactionManager = null; mClientManager = null; + mSensorPrivacyManagerInternal = null; mDefaultClientMap = Collections.emptyMap(); mContextHubIdToInfoMap = Collections.emptyMap(); mSupportedContextHubPerms = Collections.emptyList(); @@ -208,6 +215,8 @@ public class ContextHubService extends IContextHubService.Stub { mClientManager = new ContextHubClientManager(mContext, mContextHubWrapper); mTransactionManager = new ContextHubTransactionManager( mContextHubWrapper.getHub(), mClientManager, mNanoAppStateManager); + mSensorPrivacyManagerInternal = + LocalServices.getService(SensorPrivacyManagerInternal.class); HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>(); for (int contextHubId : mContextHubIdToInfoMap.keySet()) { @@ -284,18 +293,16 @@ public class ContextHubService extends IContextHubService.Stub { } if (mContextHubWrapper.supportsMicrophoneDisableSettingNotifications()) { - sendMicrophoneDisableSettingUpdate(); + sendMicrophoneDisableSettingUpdateForCurrentUser(); + + mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers( + SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> { + if (userId == getCurrentUserId()) { + Log.d(TAG, "User: " + userId + " enabled: " + enabled); + sendMicrophoneDisableSettingUpdate(enabled); + } + }); - SensorPrivacyManager.OnSensorPrivacyChangedListener listener = - new SensorPrivacyManager.OnSensorPrivacyChangedListener() { - @Override - public void onSensorPrivacyChanged(boolean enabled) { - sendMicrophoneDisableSettingUpdate(); - } - }; - SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext); - manager.addSensorPrivacyListener( - SensorPrivacyManager.Sensors.MICROPHONE, listener); } } @@ -1074,19 +1081,48 @@ public class ContextHubService extends IContextHubService.Stub { } /** - * Obtains the latest microphone disable setting value and notifies the - * Context Hub. + * Notifies a microphone disable settings change to the Context Hub. */ - private void sendMicrophoneDisableSettingUpdate() { - SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext); - boolean disabled = manager.isSensorPrivacyEnabled( - SensorPrivacyManager.Sensors.MICROPHONE); - Log.d(TAG, "Mic Disabled Setting: " + disabled); - mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled); + private void sendMicrophoneDisableSettingUpdate(boolean enabled) { + Log.d(TAG, "Mic Disabled Setting: " + enabled); + mContextHubWrapper.onMicrophoneDisableSettingChanged(enabled); + } + + /** + * Obtains the latest microphone disabled setting for the current user + * and notifies the Context Hub. + */ + private void sendMicrophoneDisableSettingUpdateForCurrentUser() { + boolean isEnabled = mSensorPrivacyManagerInternal.isSensorPrivacyEnabled( + getCurrentUserId(), SensorPrivacyManager.Sensors.MICROPHONE); + sendMicrophoneDisableSettingUpdate(isEnabled); } private String getCallingPackageName() { return mContext.getPackageManager().getNameForUid(Binder.getCallingUid()); } + + private int getCurrentUserId() { + final long id = Binder.clearCallingIdentity(); + try { + UserInfo currentUser = ActivityManager.getService().getCurrentUser(); + return currentUser.id; + } catch (RemoteException e) { + // Activity manager not running, nothing we can do - assume user 0. + } finally { + Binder.restoreCallingIdentity(id); + } + return UserHandle.USER_SYSTEM; + } + + /** + * Send a microphone disable settings update whenever the foreground user changes. + * We always send a settings update regardless of the previous state for the same user + * since the CHRE framework is expected to handle repeated identical setting update. + */ + public void onUserChanged() { + Log.d(TAG, "User changed to id: " + getCurrentUserId()); + sendMicrophoneDisableSettingUpdateForCurrentUser(); + } } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 0a443f3fd7f9..7aaab0c4d40b 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -51,6 +51,7 @@ import android.os.FileUtils; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.Trace; import android.os.UserHandle; import android.os.WorkSource; import android.os.storage.StorageManager; @@ -62,6 +63,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.dex.ArtManagerService; +import com.android.server.pm.dex.ArtStatsLogUtils; +import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.DexoptUtils; @@ -99,6 +102,8 @@ public class PackageDexOptimizer { private final PowerManager.WakeLock mDexoptWakeLock; private volatile boolean mSystemReady; + private final ArtStatsLogger mArtStatsLogger = new ArtStatsLogger(); + PackageDexOptimizer(Installer installer, Object installLock, Context context, String wakeLockTag) { this.mInstaller = installer; @@ -252,6 +257,28 @@ public class PackageDexOptimizer { profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid, packageStats, options.isDowngrade(), profileName, dexMetadataPath, options.getCompilationReason()); + + // Only report metrics for base apk for now. + // TODO: add ISA and APK type to metrics. + if (pkg.getBaseApkPath().equals(path)) { + Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics"); + try { + long sessionId = Math.randomLongInternal(); + ArtStatsLogUtils.writeStatsLog( + mArtStatsLogger, + sessionId, + path, + compilerFilter, + sharedGid, + packageStats.getCompileTime(path), + dexMetadataPath, + options.getCompilationReason(), + newResult); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER); + } + } + // The end result is: // - FAILED if any path failed, // - PERFORMED if at least one path needed compilation, diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java new file mode 100644 index 000000000000..3b77c39cc31b --- /dev/null +++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2021 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.server.pm.dex; + +import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED; +import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY; +import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED; +import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK; +import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK; + +import android.util.jar.StrictJarFile; +import android.util.Slog; + +import com.android.internal.art.ArtStatsLog; +import com.android.server.pm.PackageManagerService; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.zip.ZipEntry; + +/** Utils class to report ART metrics to statsd. */ +public class ArtStatsLogUtils { + private static final String TAG = ArtStatsLogUtils.class.getSimpleName(); + private static final String PROFILE_DEX_METADATA = "primary.prof"; + private static final String VDEX_DEX_METADATA = "primary.vdex"; + + + private static final int ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY = + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY; + private static final int ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED = + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED; + private static final int ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED = + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED; + + private static final int ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK = + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK; + private static final int ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK = + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK; + + private static final Map<Integer, Integer> COMPILATION_REASON_MAP = new HashMap(); + + static { + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_UNKNOWN, ArtStatsLog. + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_UNKNOWN); + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_FIRST_BOOT, ArtStatsLog. + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_FIRST_BOOT); + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BOOT_AFTER_OTA, ArtStatsLog. + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT); + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_POST_BOOT, ArtStatsLog. + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_POST_BOOT); + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL, ArtStatsLog. + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL); + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_FAST, ArtStatsLog. + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_FAST); + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK, ArtStatsLog. + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INSTALL_BULK); + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_SECONDARY, + ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY); + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED, + ART_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED); + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED, + ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED); + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BACKGROUND_DEXOPT, ArtStatsLog. + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BG_DEXOPT); + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_AB_OTA, ArtStatsLog. + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_AB_OTA); + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE, + ArtStatsLog. + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_INACTIVE); + COMPILATION_REASON_MAP.put(PackageManagerService.REASON_SHARED, + ArtStatsLog.ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_SHARED); + } + + private static final Map<String, Integer> COMPILE_FILTER_MAP = new HashMap(); + + static { + COMPILE_FILTER_MAP.put("error", ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ERROR); + COMPILE_FILTER_MAP.put("unknown", ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN); + COMPILE_FILTER_MAP.put("assume-verified", ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ASSUMED_VERIFIED); + COMPILE_FILTER_MAP.put("extract", ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EXTRACT); + COMPILE_FILTER_MAP.put("verify", ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_VERIFY); + COMPILE_FILTER_MAP.put("quicken", ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_QUICKEN); + COMPILE_FILTER_MAP.put("space-profile", ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE_PROFILE); + COMPILE_FILTER_MAP.put("space", ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE); + COMPILE_FILTER_MAP.put("speed-profile", ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED_PROFILE); + COMPILE_FILTER_MAP.put("speed", ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED); + COMPILE_FILTER_MAP.put("everything-profile", ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING_PROFILE); + COMPILE_FILTER_MAP.put("everything", ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING); + COMPILE_FILTER_MAP.put("run-from-apk", ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK); + COMPILE_FILTER_MAP.put("run-from-apk-fallback", + ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK); + COMPILE_FILTER_MAP.put("run-from-vdex-fallback", + ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK); + } + + public static void writeStatsLog( + ArtStatsLogger logger, + long sessionId, + String path, + String compilerFilter, + int uid, + long compileTime, + String dexMetadataPath, + int compilationReason, + int result) { + int dexMetadataType = getDexMetadataType(dexMetadataPath); + logger.write( + sessionId, + uid, + compilationReason, + compilerFilter, + ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE, + result, + dexMetadataType); + logger.write( + sessionId, + uid, + compilationReason, + compilerFilter, + ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_BYTES, + getDexBytes(path), + dexMetadataType); + logger.write( + sessionId, + uid, + compilationReason, + compilerFilter, + ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME, + compileTime, + dexMetadataType); + } + + private static long getDexBytes(String apkPath) { + StrictJarFile jarFile = null; + long dexBytes = 0; + try { + jarFile = new StrictJarFile(apkPath, + /*verify=*/ false, + /*signatureSchemeRollbackProtectionsEnforced=*/ false); + Iterator<ZipEntry> it = jarFile.iterator(); + while (it.hasNext()) { + ZipEntry entry = it.next(); + if (entry.getName().matches("classes(\\d)*[.]dex")) { + dexBytes += entry.getSize(); + } + } + return dexBytes; + } catch (IOException ignore) { + Slog.e(TAG, "Error when parsing APK " + apkPath); + return -1L; + } finally { + try { + if (jarFile != null) { + jarFile.close(); + } + } catch (IOException ignore) { + } + } + } + + private static int getDexMetadataType(String dexMetadataPath) { + if (dexMetadataPath == null) { + return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__NONE_DEX_METADATA; + } + StrictJarFile jarFile = null; + try { + jarFile = new StrictJarFile(dexMetadataPath, + /*verify=*/ false, + /*signatureSchemeRollbackProtectionsEnforced=*/false); + boolean hasProfile = findFileName(jarFile, PROFILE_DEX_METADATA); + boolean hasVdex = findFileName(jarFile, VDEX_DEX_METADATA); + if (hasProfile && hasVdex) { + return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE_AND_VDEX; + } else if (hasProfile) { + return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE; + } else if (hasVdex) { + return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__VDEX; + } else { + return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__UNKNOWN_DEX_METADATA; + } + } catch (IOException ignore) { + Slog.e(TAG, "Error when parsing dex metadata " + dexMetadataPath); + return ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__ERROR_DEX_METADATA; + } finally { + try { + if (jarFile != null) { + jarFile.close(); + } + } catch (IOException ignore) { + } + } + } + + private static boolean findFileName(StrictJarFile jarFile, String filename) throws IOException { + Iterator<ZipEntry> it = jarFile.iterator(); + while (it.hasNext()) { + ZipEntry entry = it.next(); + if (entry.getName().equals(filename)) { + return true; + } + } + return false; + } + + public static class ArtStatsLogger { + public void write( + long sessionId, + int uid, + int compilationReason, + String compilerFilter, + int kind, + long value, + int dexMetadataType) { + ArtStatsLog.write( + ArtStatsLog.ART_DATUM_REPORTED, + sessionId, + uid, + COMPILE_FILTER_MAP.getOrDefault(compilerFilter, ArtStatsLog. + ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN), + COMPILATION_REASON_MAP.getOrDefault(compilationReason, ArtStatsLog. + ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_UNKNOWN), + /*timestamp_millis=*/ 0L, + ArtStatsLog.ART_DATUM_REPORTED__THREAD_TYPE__ART_THREAD_MAIN, + kind, + value, + dexMetadataType); + } + } +} diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index e3ccb7521b58..27bf8a13766a 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -737,10 +737,12 @@ final class DefaultPermissionGrantPolicy { } } if (locationExtraPackageNames != null) { - // Also grant location permission to location extra packages. + // Also grant location and activity recognition permission to location extra packages. for (String packageName : locationExtraPackageNames) { grantPermissionsToSystemPackage(pm, packageName, userId, ALWAYS_LOCATION_PERMISSIONS); + grantSystemFixedPermissionsToSystemPackage(pm, packageName, userId, + ACTIVITY_RECOGNITION_PERMISSIONS); } } diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java index 1e8b8a5bb576..4cc369f0a187 100644 --- a/services/core/java/com/android/server/wm/SeamlessRotator.java +++ b/services/core/java/com/android/server/wm/SeamlessRotator.java @@ -102,6 +102,10 @@ public class SeamlessRotator { * window in the new orientation. */ void finish(Transaction t, WindowContainer win) { + if (win.mSurfaceControl == null || !win.mSurfaceControl.isValid()) { + return; + } + mTransform.reset(); t.setMatrix(win.mSurfaceControl, mTransform, mFloat9); t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 1a2eee06da4f..ac2281a2dbaa 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -8817,6 +8817,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } final ComponentName supervisorComponent = ComponentName.unflattenFromString(supervisor); + if (supervisorComponent == null) { + return null; + } if (supervisorComponent.equals(doComponent) || supervisorComponent.equals( poComponent)) { return supervisorComponent; diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java new file mode 100644 index 000000000000..3ab34484ce25 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2021 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.server.pm.dex; + +import static org.mockito.Mockito.inOrder; + +import com.android.internal.art.ArtStatsLog; +import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.InOrder; +import org.mockito.MockitoAnnotations; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * Unit tests for {@link com.android.server.pm.dex.ArtStatsLogUtils}. + * + * Run with "atest ArtStatsLogUtilsTest". + */ +@RunWith(JUnit4.class) +public final class ArtStatsLogUtilsTest { + private static final String TAG = ArtStatsLogUtilsTest.class.getSimpleName(); + private static final String COMPILER_FILTER = "space-profile"; + private static final String PROFILE_DEX_METADATA = "primary.prof"; + private static final String VDEX_DEX_METADATA = "primary.vdex"; + private static final byte[] DEX_CONTENT = "dexData".getBytes(); + private static final int COMPILATION_REASON = 1; + private static final int RESULT_CODE = 222; + private static final int UID = 111; + private static final long COMPILE_TIME = 333L; + private static final long SESSION_ID = 444L; + + @Mock + ArtStatsLogger mockLogger; + + private static Path TEST_DIR; + private static Path DEX; + private static Path NON_DEX; + + @BeforeClass + public static void setUpAll() throws IOException { + TEST_DIR = Files.createTempDirectory(null); + DEX = Files.createFile(TEST_DIR.resolve("classes.dex")); + NON_DEX = Files.createFile(TEST_DIR.resolve("test.dex")); + Files.write(DEX, DEX_CONTENT); + Files.write(NON_DEX, "empty".getBytes()); + } + + @AfterClass + public static void tearnDownAll() { + deleteSliently(DEX); + deleteSliently(NON_DEX); + deleteSliently(TEST_DIR); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testProfileAndVdexDexMetadata() throws IOException { + // Setup + Path dexMetadataPath = null; + Path apk = null; + try { + dexMetadataPath = createDexMetadata(PROFILE_DEX_METADATA, VDEX_DEX_METADATA); + apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath); + + // Act + ArtStatsLogUtils.writeStatsLog( + mockLogger, + SESSION_ID, + apk.toString(), + COMPILER_FILTER, + UID, + COMPILE_TIME, + dexMetadataPath.toString(), + COMPILATION_REASON, + RESULT_CODE); + + // Assert + verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE_AND_VDEX); + } finally { + deleteSliently(dexMetadataPath); + deleteSliently(apk); + } + } + + @Test + public void testProfileOnlyDexMetadata() throws IOException { + // Setup + Path dexMetadataPath = null; + Path apk = null; + try { + dexMetadataPath = createDexMetadata(PROFILE_DEX_METADATA); + apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath); + + // Act + ArtStatsLogUtils.writeStatsLog( + mockLogger, + SESSION_ID, + apk.toString(), + COMPILER_FILTER, + UID, + COMPILE_TIME, + dexMetadataPath.toString(), + COMPILATION_REASON, + RESULT_CODE); + + // Assert + verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__PROFILE); + } finally { + deleteSliently(dexMetadataPath); + deleteSliently(apk); + } + } + + @Test + public void testVdexOnlyDexMetadata() throws IOException { + // Setup + Path dexMetadataPath = null; + Path apk = null; + try { + dexMetadataPath = createDexMetadata(VDEX_DEX_METADATA); + apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath); + + // Act + ArtStatsLogUtils.writeStatsLog( + mockLogger, + SESSION_ID, + apk.toString(), + COMPILER_FILTER, + UID, + COMPILE_TIME, + dexMetadataPath.toString(), + COMPILATION_REASON, + RESULT_CODE); + + // Assert + verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__VDEX); + } finally { + deleteSliently(dexMetadataPath); + deleteSliently(apk); + } + } + + @Test + public void testNoneDexMetadata() throws IOException { + // Setup + Path apk = null; + try { + apk = zipFiles(".apk", DEX, NON_DEX); + + // Act + ArtStatsLogUtils.writeStatsLog( + mockLogger, + SESSION_ID, + apk.toString(), + COMPILER_FILTER, + UID, + COMPILE_TIME, + /*dexMetadataPath=*/ null, + COMPILATION_REASON, + RESULT_CODE); + + // Assert + verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__NONE_DEX_METADATA); + } finally { + deleteSliently(apk); + } + } + + @Test + public void testUnKnownDexMetadata() throws IOException { + // Setup + Path dexMetadataPath = null; + Path apk = null; + try { + dexMetadataPath = createDexMetadata("unknown"); + apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath); + + // Act + ArtStatsLogUtils.writeStatsLog( + mockLogger, + SESSION_ID, + apk.toString(), + COMPILER_FILTER, + UID, + COMPILE_TIME, + dexMetadataPath.toString(), + COMPILATION_REASON, + RESULT_CODE); + + // Assert + verifyWrites(ArtStatsLog.ART_DATUM_REPORTED__DEX_METADATA_TYPE__UNKNOWN_DEX_METADATA); + } finally { + deleteSliently(dexMetadataPath); + deleteSliently(apk); + } + } + + private void verifyWrites(int dexMetadataType) { + InOrder inorder = inOrder(mockLogger); + inorder.verify(mockLogger).write( + SESSION_ID, UID, + COMPILATION_REASON, + COMPILER_FILTER, + ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE, + RESULT_CODE, + dexMetadataType); + inorder.verify(mockLogger).write( + SESSION_ID, + UID, + COMPILATION_REASON, + COMPILER_FILTER, + ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_BYTES, + DEX_CONTENT.length, + dexMetadataType); + inorder.verify(mockLogger).write( + SESSION_ID, + UID, + COMPILATION_REASON, + COMPILER_FILTER, + ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME, + COMPILE_TIME, + dexMetadataType); + } + + private Path zipFiles(String suffix, Path... files) throws IOException { + Path zipFile = Files.createTempFile(null, suffix); + try (final OutputStream os = Files.newOutputStream(zipFile)) { + try (final ZipOutputStream zos = new ZipOutputStream(os)) { + for (Path file : files) { + ZipEntry zipEntry = new ZipEntry(file.getFileName().toString()); + zos.putNextEntry(zipEntry); + zos.write(Files.readAllBytes(file)); + zos.closeEntry(); + } + } + } + return zipFile; + } + + private Path createDexMetadata(String... entryNames) throws IOException { + Path zipFile = Files.createTempFile(null, ".dm"); + try (final OutputStream os = Files.newOutputStream(zipFile)) { + try (final ZipOutputStream zos = new ZipOutputStream(os)) { + for (String entryName : entryNames) { + ZipEntry zipEntry = new ZipEntry(entryName); + zos.putNextEntry(zipEntry); + zos.write(entryName.getBytes()); + zos.closeEntry(); + } + } + } + return zipFile; + } + + private static void deleteSliently(Path file) { + if (file != null) { + try { + Files.deleteIfExists(file); + } catch (IOException e) { + // ignore + } + } + } +} diff --git a/telecomm/java/android/telecom/CallDiagnosticService.java b/telecomm/java/android/telecom/CallDiagnosticService.java index 809f2bc1bb7d..5fb6b3381360 100644 --- a/telecomm/java/android/telecom/CallDiagnosticService.java +++ b/telecomm/java/android/telecom/CallDiagnosticService.java @@ -27,6 +27,8 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; import android.os.RemoteException; +import android.telephony.Annotation; +import android.telephony.ims.ImsReasonInfo; import android.util.ArrayMap; import com.android.internal.telecom.ICallDiagnosticService; @@ -105,6 +107,12 @@ public abstract class CallDiagnosticService extends Service { throws RemoteException { handleBluetoothCallQualityReport(qualityReport); } + + @Override + public void notifyCallDisconnected(@NonNull String callId, + @NonNull DisconnectCause disconnectCause) throws RemoteException { + handleCallDisconnected(callId, disconnectCause); + } } /** @@ -329,6 +337,32 @@ public abstract class CallDiagnosticService extends Service { } /** + * Handles a request from the Telecom framework to get a disconnect message from the + * {@link CallDiagnosticService}. + * @param callId The ID of the call. + * @param disconnectCause The telecom disconnect cause. + */ + private void handleCallDisconnected(@NonNull String callId, + @NonNull DisconnectCause disconnectCause) { + Log.i(this, "handleCallDisconnected: call=%s; cause=%s", callId, disconnectCause); + DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId); + CharSequence message; + if (disconnectCause.getImsReasonInfo() != null) { + message = diagnosticCall.onCallDisconnected(disconnectCause.getImsReasonInfo()); + } else { + message = diagnosticCall.onCallDisconnected( + disconnectCause.getTelephonyDisconnectCause(), + disconnectCause.getTelephonyPreciseDisconnectCause()); + } + try { + mAdapter.overrideDisconnectMessage(callId, message); + } catch (RemoteException e) { + Log.w(this, "handleCallDisconnected: call=%s; cause=%s; %s", + callId, disconnectCause, e); + } + } + + /** * Handles an incoming bluetooth call quality report from Telecom. Notifies via * {@link CallDiagnosticService#onBluetoothCallQualityReportReceived( * BluetoothCallQualityReport)}. diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java index 1472a4ac27bc..ed7b79f62753 100644 --- a/telecomm/java/android/telecom/DisconnectCause.java +++ b/telecomm/java/android/telecom/DisconnectCause.java @@ -16,9 +16,13 @@ package android.telecom; +import android.annotation.Nullable; import android.media.ToneGenerator; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.Annotation; +import android.telephony.PreciseDisconnectCause; +import android.telephony.ims.ImsReasonInfo; import android.text.TextUtils; import java.util.Objects; @@ -112,6 +116,9 @@ public final class DisconnectCause implements Parcelable { private CharSequence mDisconnectDescription; private String mDisconnectReason; private int mToneToPlay; + private int mTelephonyDisconnectCause; + private int mTelephonyPreciseDisconnectCause; + private ImsReasonInfo mImsReasonInfo; /** * Creates a new DisconnectCause. @@ -155,11 +162,36 @@ public final class DisconnectCause implements Parcelable { */ public DisconnectCause(int code, CharSequence label, CharSequence description, String reason, int toneToPlay) { + this(code, label, description, reason, toneToPlay, + android.telephony.DisconnectCause.ERROR_UNSPECIFIED, + PreciseDisconnectCause.ERROR_UNSPECIFIED, + null /* imsReasonInfo */); + } + + /** + * Creates a new DisconnectCause instance. + * @param code The code for the disconnect cause. + * @param label The localized label to show to the user to explain the disconnect. + * @param description The localized description to show to the user to explain the disconnect. + * @param reason The reason for the disconnect. + * @param toneToPlay The tone to play on disconnect, as defined in {@link ToneGenerator}. + * @param telephonyDisconnectCause The Telephony disconnect cause. + * @param telephonyPreciseDisconnectCause The Telephony precise disconnect cause. + * @param imsReasonInfo The relevant {@link ImsReasonInfo}, or {@code null} if not available. + * @hide + */ + public DisconnectCause(int code, CharSequence label, CharSequence description, String reason, + int toneToPlay, @Annotation.DisconnectCauses int telephonyDisconnectCause, + @Annotation.PreciseDisconnectCauses int telephonyPreciseDisconnectCause, + @Nullable ImsReasonInfo imsReasonInfo) { mDisconnectCode = code; mDisconnectLabel = label; mDisconnectDescription = description; mDisconnectReason = reason; mToneToPlay = toneToPlay; + mTelephonyDisconnectCause = telephonyDisconnectCause; + mTelephonyPreciseDisconnectCause = telephonyPreciseDisconnectCause; + mImsReasonInfo = imsReasonInfo; } /** @@ -209,6 +241,33 @@ public final class DisconnectCause implements Parcelable { } /** + * Returns the telephony {@link android.telephony.DisconnectCause} for the call. + * @return The disconnect cause. + * @hide + */ + public @Annotation.DisconnectCauses int getTelephonyDisconnectCause() { + return mTelephonyDisconnectCause; + } + + /** + * Returns the telephony {@link android.telephony.PreciseDisconnectCause} for the call. + * @return The precise disconnect cause. + * @hide + */ + public @Annotation.PreciseDisconnectCauses int getTelephonyPreciseDisconnectCause() { + return mTelephonyPreciseDisconnectCause; + } + + /** + * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection. + * @return The {@link ImsReasonInfo} or {@code null} if not known. + * @hide + */ + public @Nullable ImsReasonInfo getImsReasonInfo() { + return mImsReasonInfo; + } + + /** * Returns the tone to play when disconnected. * * @return the tone as defined in {@link ToneGenerator} to play when disconnected. @@ -217,7 +276,8 @@ public final class DisconnectCause implements Parcelable { return mToneToPlay; } - public static final @android.annotation.NonNull Creator<DisconnectCause> CREATOR = new Creator<DisconnectCause>() { + public static final @android.annotation.NonNull Creator<DisconnectCause> CREATOR + = new Creator<DisconnectCause>() { @Override public DisconnectCause createFromParcel(Parcel source) { int code = source.readInt(); @@ -225,7 +285,11 @@ public final class DisconnectCause implements Parcelable { CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); String reason = source.readString(); int tone = source.readInt(); - return new DisconnectCause(code, label, description, reason, tone); + int telephonyDisconnectCause = source.readInt(); + int telephonyPreciseDisconnectCause = source.readInt(); + ImsReasonInfo imsReasonInfo = source.readParcelable(null); + return new DisconnectCause(code, label, description, reason, tone, + telephonyDisconnectCause, telephonyPreciseDisconnectCause, imsReasonInfo); } @Override @@ -241,6 +305,9 @@ public final class DisconnectCause implements Parcelable { TextUtils.writeToParcel(mDisconnectDescription, destination, flags); destination.writeString(mDisconnectReason); destination.writeInt(mToneToPlay); + destination.writeInt(mTelephonyDisconnectCause); + destination.writeInt(mTelephonyPreciseDisconnectCause); + destination.writeParcelable(mImsReasonInfo, 0); } @Override @@ -254,7 +321,10 @@ public final class DisconnectCause implements Parcelable { + Objects.hashCode(mDisconnectLabel) + Objects.hashCode(mDisconnectDescription) + Objects.hashCode(mDisconnectReason) - + Objects.hashCode(mToneToPlay); + + Objects.hashCode(mToneToPlay) + + Objects.hashCode(mTelephonyDisconnectCause) + + Objects.hashCode(mTelephonyPreciseDisconnectCause) + + Objects.hashCode(mImsReasonInfo); } @Override @@ -265,7 +335,11 @@ public final class DisconnectCause implements Parcelable { && Objects.equals(mDisconnectLabel, d.getLabel()) && Objects.equals(mDisconnectDescription, d.getDescription()) && Objects.equals(mDisconnectReason, d.getReason()) - && Objects.equals(mToneToPlay, d.getTone()); + && Objects.equals(mToneToPlay, d.getTone()) + && Objects.equals(mTelephonyDisconnectCause, d.getTelephonyDisconnectCause()) + && Objects.equals(mTelephonyPreciseDisconnectCause, + d.getTelephonyPreciseDisconnectCause()) + && Objects.equals(mImsReasonInfo, d.getImsReasonInfo()); } return false; } @@ -325,6 +399,11 @@ public final class DisconnectCause implements Parcelable { + " Label: (" + label + ")" + " Description: (" + description + ")" + " Reason: (" + reason + ")" - + " Tone: (" + mToneToPlay + ") ]"; + + " Tone: (" + mToneToPlay + ") " + + " TelephonyCause: " + mTelephonyDisconnectCause + "/" + + mTelephonyPreciseDisconnectCause + + " ImsReasonInfo: " + + mImsReasonInfo + + "]"; } } diff --git a/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl b/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl index 65b4d19b3d9b..fc9879aaf0a8 100644 --- a/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl +++ b/telecomm/java/com/android/internal/telecom/ICallDiagnosticService.aidl @@ -18,6 +18,7 @@ package com.android.internal.telecom; import android.telecom.BluetoothCallQualityReport; import android.telecom.CallAudioState; +import android.telecom.DisconnectCause; import android.telecom.ParcelableCall; import com.android.internal.telecom.ICallDiagnosticServiceAdapter; @@ -34,4 +35,5 @@ oneway interface ICallDiagnosticService { void removeDiagnosticCall(in String callId); void receiveDeviceToDeviceMessage(in String callId, int message, int value); void receiveBluetoothCallQualityReport(in BluetoothCallQualityReport qualityReport); + void notifyCallDisconnected(in String callId, in DisconnectCause disconnectCause); } diff --git a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp index ce226fdce320..926ff4d5793c 100644 --- a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp +++ b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp @@ -130,6 +130,9 @@ JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceDequeueBuffer(J return result; } sBuffers[slot] = anb; + if (timeoutMs == 0) { + return android::OK; + } android::sp<android::Fence> fence(new android::Fence(fenceFd)); int waitResult = fence->wait(timeoutMs); if (waitResult != android::OK) { @@ -197,6 +200,28 @@ JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceQueueBuffer(JNI return result; } +JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetAsyncMode(JNIEnv* /* env */, + jclass /* clazz */, + jboolean async) { + assert(sAnw); + android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw); + return surface->setAsyncMode(async); +} + +JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetDequeueTimeout( + JNIEnv* /* env */, jclass /* clazz */, jlong timeoutMs) { + assert(sAnw); + android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw); + return surface->setDequeueTimeout(timeoutMs); +} + +JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetMaxDequeuedBufferCount( + JNIEnv* /* env */, jclass /* clazz */, jint maxDequeuedBuffers) { + assert(sAnw); + android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw); + return surface->setMaxDequeuedBufferCount(maxDequeuedBuffers); +} + JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetBufferCount( JNIEnv* /* env */, jclass /* clazz */, jint count) { assert(sAnw); diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt index 7d278dc8acc0..b67dc380efab 100644 --- a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt +++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt @@ -17,6 +17,7 @@ package com.android.test import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat import junit.framework.Assert.assertEquals +import junit.framework.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -93,4 +94,80 @@ class BufferPresentationTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames) } + + @Test + // Leave IGBP in sync mode, try to dequeue and queue as fast as possible. Check that we + // occasionally get timeout errors. + fun testSyncMode_dequeueWithoutBlockingFails() { + val numFrames = 1000L + runOnUiThread { activity -> + assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L)) + var failures = false + for (i in 1..numFrames) { + if (activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */) != 0) { + failures = true + break + } + activity.mSurfaceProxy.SurfaceQueueBuffer(0) + } + assertTrue(failures) + } + } + + @Test + // Set IGBP to be in async mode, try to dequeue and queue as fast as possible. Client should be + // able to dequeue and queue buffers without being blocked. + fun testAsyncMode_dequeueWithoutBlocking() { + val numFrames = 1000L + runOnUiThread { activity -> + assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L)) + assertEquals(0, activity.mSurfaceProxy.SurfaceSetAsyncMode(async = true)) + for (i in 1..numFrames) { + assertEquals(0, activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */)) + activity.mSurfaceProxy.SurfaceQueueBuffer(0) + } + } + } + + @Test + // Disable triple buffering in the system and leave IGBP in sync mode. Check that we + // occasionally get timeout errors. + fun testSyncModeWithDisabledTripleBuffering_dequeueWithoutBlockingFails() { + val numFrames = 1000L + runOnUiThread { activity -> + assertEquals(0, activity.mSurfaceProxy.SurfaceSetMaxDequeuedBufferCount(1)) + assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L)) + var failures = false + for (i in 1..numFrames) { + if (activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */) != 0) { + failures = true + break + } + activity.mSurfaceProxy.SurfaceQueueBuffer(0) + } + assertTrue(failures) + } + } + + @Test + // Disable triple buffering in the system and set IGBP to be in async mode. Try to dequeue and + // queue as fast as possible. Without triple buffering, the client does not have an extra buffer + // to dequeue and will not be able to dequeue and queue buffers without being blocked. + fun testAsyncModeWithDisabledTripleBuffering_dequeueWithoutBlockingFails() { + val numFrames = 1000L + runOnUiThread { activity -> + assertEquals(0, activity.mSurfaceProxy.SurfaceSetMaxDequeuedBufferCount(1)) + assertEquals(0, activity.mSurfaceProxy.SurfaceSetDequeueTimeout(3L)) + assertEquals(0, activity.mSurfaceProxy.SurfaceSetAsyncMode(async = true)) + var failures = false + for (i in 1..numFrames) { + if (activity.mSurfaceProxy.SurfaceDequeueBuffer(0, 0 /* ms */) != 0) { + failures = true + break + } + activity.mSurfaceProxy.SurfaceQueueBuffer(0) + } + assertTrue(failures) + } + } }
\ No newline at end of file diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt index cfbd3ac11acb..45a70944204c 100644 --- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt +++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt @@ -54,7 +54,13 @@ class SurfaceProxy { external fun SurfaceSetScalingMode(scalingMode: Int) external fun SurfaceDequeueBuffer(slot: Int, timeoutMs: Int): Int external fun SurfaceCancelBuffer(slot: Int) - external fun SurfaceQueueBuffer(slot: Int, freeSlot: Boolean = true) + external fun SurfaceQueueBuffer(slot: Int, freeSlot: Boolean = true): Int + external fun SurfaceSetAsyncMode(async: Boolean): Int + external fun SurfaceSetDequeueTimeout(timeout: Long): Int + external fun SurfaceQuery(what: Int): Int + external fun SurfaceSetMaxDequeuedBufferCount(maxDequeuedBuffers: Int): Int + + // system/native_window.h functions external fun NativeWindowSetBufferCount(count: Int): Int external fun NativeWindowSetSharedBufferMode(shared: Boolean): Int external fun NativeWindowSetAutoRefresh(autoRefresh: Boolean): Int diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java index a07bce34c737..8932892c3aec 100644 --- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java +++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java @@ -30,12 +30,21 @@ import com.android.test.filters.SelectTest; * -e selectTest_verbose true \ * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner * </pre> + * + * <p>Use this filter when running FrameworksMockingCoreTests as + * <pre> + * adb shell am instrument -w \ + * -e filter com.android.server.wm.test.filters.FrameworksTestsFilter \ + * -e selectTest_verbose true \ + * com.android.frameworks.mockingcoretests/androidx.test.runner.AndroidJUnitRunner + * </pre> */ public final class FrameworksTestsFilter extends SelectTest { private static final String[] SELECTED_TESTS = { // Test specifications for FrameworksMockingCoreTests. "android.app.activity.ActivityThreadClientTest", + "android.view.DisplayTest", // Test specifications for FrameworksCoreTests. "android.app.servertransaction.", // all tests under the package. "android.view.CutoutSpecificationTest", |