diff options
| author | 2021-09-30 15:57:26 -0700 | |
|---|---|---|
| committer | 2021-10-27 08:39:20 -0700 | |
| commit | 87a5024ef5ed68f36b2c6618b13fa4512e06c47c (patch) | |
| tree | fac9abe9bc8a0493df82639cab871143f9b9f1fd | |
| parent | 22f575a03657110b736705daecb5bc4cdefb1a13 (diff) | |
IDreamOverlay Introduction.
This changelist introduces IDreamOverlay, an interface
for rendering an overlay on top of a Dream. IDreamOverlay
implementations can register with DreamManagerService to
be bound to by DreamService when dream commences. The
overlay will be passed the Dream Window information so
that it may render within this window space.
Bug: 201676854
Test: atest DreamOverlayTest
Change-Id: I382ba2acb4a6922f19250753f8f0afc4181a6454
6 files changed, 179 insertions, 2 deletions
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 98196489a2ed..096595f30b05 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -26,7 +26,10 @@ import android.app.ActivityTaskManager; import android.app.AlarmManager; import android.app.Service; import android.compat.annotation.UnsupportedAppUsage; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -58,6 +61,8 @@ import com.android.internal.util.DumpUtils.Dump; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayDeque; +import java.util.function.Consumer; /** * Extend this class to implement a custom dream (available to the user as a "Daydream"). @@ -170,6 +175,13 @@ public class DreamService extends Service implements Window.Callback { "android.service.dreams.DreamService"; /** + * The name of the extra where the dream overlay component is stored. + * @hide + */ + public static final String EXTRA_DREAM_OVERLAY_COMPONENT = + "android.service.dream.DreamService.dream_overlay_component"; + + /** * Name under which a Dream publishes information about itself. * This meta-data must reference an XML resource containing * a <code><{@link android.R.styleable#Dream dream}></code> @@ -191,6 +203,7 @@ public class DreamService extends Service implements Window.Callback { private boolean mCanDoze; private boolean mDozing; private boolean mWindowless; + private boolean mOverlayServiceBound; private int mDozeScreenState = Display.STATE_UNKNOWN; private int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT; @@ -199,8 +212,62 @@ public class DreamService extends Service implements Window.Callback { private DreamServiceWrapper mDreamServiceWrapper; private Runnable mDispatchAfterOnAttachedToWindow; + private OverlayConnection mOverlayConnection; + + private static class OverlayConnection implements ServiceConnection { + // Overlay set during onBind. + private IDreamOverlay mOverlay; + // A Queue of pending requests to execute on the overlay. + private ArrayDeque<Consumer<IDreamOverlay>> mRequests; + + OverlayConnection() { + mRequests = new ArrayDeque<>(); + } + + public void request(Consumer<IDreamOverlay> request) { + mRequests.push(request); + evaluate(); + } + + private void evaluate() { + if (mOverlay == null) { + return; + } + + // Any new requests that arrive during this loop will be processed synchronously after + // the loop exits. + while (!mRequests.isEmpty()) { + final Consumer<IDreamOverlay> request = mRequests.pop(); + request.accept(mOverlay); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + // Store Overlay and execute pending requests. + mOverlay = IDreamOverlay.Stub.asInterface(service); + evaluate(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + // Clear Overlay binder to prevent further request processing. + mOverlay = null; + } + } + + private IDreamOverlayCallback mOverlayCallback = new IDreamOverlayCallback.Stub() { + @Override + public void onExitRequested() { + // Simply finish dream when exit is requested. + finish(); + } + }; + + public DreamService() { mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); + mOverlayConnection = new OverlayConnection(); } /** @@ -861,6 +928,18 @@ public class DreamService extends Service implements Window.Callback { public final IBinder onBind(Intent intent) { if (mDebug) Slog.v(TAG, "onBind() intent = " + intent); mDreamServiceWrapper = new DreamServiceWrapper(); + + // Connect to the overlay service if present. + final ComponentName overlayComponent = + intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT); + if (overlayComponent != null && !mWindowless) { + final Intent overlayIntent = new Intent(); + overlayIntent.setComponent(overlayComponent); + + mOverlayServiceBound = getApplicationContext().bindService(overlayIntent, + mOverlayConnection, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE); + } + return mDreamServiceWrapper; } @@ -894,6 +973,11 @@ public class DreamService extends Service implements Window.Callback { return; } + if (!mWindowless && mOverlayServiceBound) { + unbindService(mOverlayConnection); + mOverlayServiceBound = false; + } + try { // finishSelf will unbind the dream controller from the dream service. This will // trigger DreamService.this.onDestroy and DreamService.this will die. @@ -1101,6 +1185,16 @@ public class DreamService extends Service implements Window.Callback { } } }); + + // Request the DreamOverlay be told to dream with dream's window parameters once the service + // has connected. + mOverlayConnection.request(overlay -> { + try { + overlay.startDream(mWindow.getAttributes(), mOverlayCallback); + } catch (RemoteException e) { + Log.e(TAG, "could not send window attributes:" + e); + } + }); } private boolean getWindowFlagValue(int flag, boolean defaultValue) { diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index 0ce9cfa7a0bf..3e0deeb556e9 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -41,4 +41,5 @@ interface IDreamManager { void forceAmbientDisplayEnabled(boolean enabled); ComponentName[] getDreamComponentsForUser(int userId); void setDreamComponentsForUser(int userId, in ComponentName[] componentNames); + void registerDreamOverlayService(in ComponentName componentName); } diff --git a/core/java/android/service/dreams/IDreamOverlay.aidl b/core/java/android/service/dreams/IDreamOverlay.aidl new file mode 100644 index 000000000000..2b6633d93dc5 --- /dev/null +++ b/core/java/android/service/dreams/IDreamOverlay.aidl @@ -0,0 +1,37 @@ +/** + * 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 android.service.dreams; + +import android.service.dreams.IDreamOverlayCallback; +import android.view.WindowManager.LayoutParams; + +/** +* {@link IDreamOverlay} provides a way for a component to annotate a dream with additional view +* elements. Registered through the DreamManager, a IDreamOverlay is bound to by the dream and +* passed the necessary window details to participate in the user interface. + +* @hide +*/ +interface IDreamOverlay { + /** + * @param params The {@link LayoutParams} for the associated DreamWindow, including the window + token of the Dream Activity. + * @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the + * dream. + */ + void startDream(in LayoutParams params, in IDreamOverlayCallback callback); +} diff --git a/core/java/android/service/dreams/IDreamOverlayCallback.aidl b/core/java/android/service/dreams/IDreamOverlayCallback.aidl new file mode 100644 index 000000000000..ec76a334d5b2 --- /dev/null +++ b/core/java/android/service/dreams/IDreamOverlayCallback.aidl @@ -0,0 +1,31 @@ +/** + * 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 android.service.dreams; + +/** +* {@link IDreamOverlayCallback} defines the interactions a dream overlay can have with its +* associated dream. It is the discretion of the {@link DreamService}) to honor any inbound requests +* from this callback. +* +* @hide +*/ +interface IDreamOverlayCallback { + /** + * Invoked to request the dream exit. + */ + void onExitRequested(); +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index 5bc69943033e..76754d3e95d5 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -117,7 +117,8 @@ final class DreamController { } public void startDream(Binder token, ComponentName name, - boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) { + boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock, + ComponentName overlayComponentName) { stopDream(true /*immediate*/, "starting new dream"); Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream"); @@ -138,6 +139,7 @@ final class DreamController { Intent intent = new Intent(DreamService.SERVICE_INTERFACE); intent.setComponent(name); intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + intent.putExtra(DreamService.EXTRA_DREAM_OVERLAY_COMPONENT, overlayComponentName); try { if (!mContext.bindServiceAsUser(intent, mCurrentDream, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 3a7220f7592f..258689a3ed93 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -95,6 +95,8 @@ public final class DreamManagerService extends SystemService { private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN; private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT; + private ComponentName mDreamOverlayServiceName; + private AmbientDisplayConfiguration mDozeConfig; @VisibleForTesting @@ -421,7 +423,8 @@ public final class DreamManagerService extends SystemService { if (!mCurrentDreamName.equals(mAmbientDisplayComponent)) { mUiEventLogger.log(DreamManagerEvent.DREAM_START); } - mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock); + mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock, + mDreamOverlayServiceName); })); } @@ -592,6 +595,15 @@ public final class DreamManagerService extends SystemService { } @Override // Binder call + public void registerDreamOverlayService(ComponentName overlayComponent) { + checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + + // Store the overlay service component so that it can be passed to the dream when it is + // invoked. + mDreamOverlayServiceName = overlayComponent; + } + + @Override // Binder call public ComponentName getDefaultDreamComponentForUser(int userId) { checkPermission(android.Manifest.permission.READ_DREAM_STATE); userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), |