Merge "IDreamOverlay Introduction."

This commit is contained in:
Bryce Lee
2021-10-27 23:02:53 +00:00
committed by Android (Google) Code Review
6 changed files with 179 additions and 2 deletions

View File

@@ -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").
@@ -169,6 +174,13 @@ public class DreamService extends Service implements Window.Callback {
public static final String SERVICE_INTERFACE =
"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
@@ -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) {

View File

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

View File

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

View File

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

View File

@@ -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,

View File

@@ -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);
}));
}
@@ -591,6 +594,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);