diff options
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(), |