Interact with DreamOverlay through distinct clients.
DreamOverlay clients currently interact with the service endpoint through a single binder interface. From the DreamOverlay perspective, it is impossible to distinguish between clients. This makes it difficult to understand if requests are coming from the currently active client. This surfaces in the product as two dreams that interfere with each other. For example, an exiting dream might send the dream ending signal after the current dream has started. This changelist introduces a DreamOverlay client binder interface, which is minted by the DreamOverlayService on demand. The existing actions on the overlay have been moved to this client. The DreamOverlayService maintains a reference to the active client so that it can verify which one can act upon the overlay. Test: atest DreamOverlayServiceTest Fixes: 266703170 Change-Id: I23e2e2adea626361bb9d2e6969341aade33b1a23
This commit is contained in:
@@ -36,39 +36,101 @@ import android.view.WindowManager;
|
||||
public abstract class DreamOverlayService extends Service {
|
||||
private static final String TAG = "DreamOverlayService";
|
||||
private static final boolean DEBUG = false;
|
||||
private boolean mShowComplications;
|
||||
private ComponentName mDreamComponent;
|
||||
|
||||
private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
|
||||
// The last client that started dreaming and hasn't ended
|
||||
private OverlayClient mCurrentClient;
|
||||
|
||||
// An {@link IDreamOverlayClient} implementation that identifies itself when forwarding
|
||||
// requests to the {@link DreamOverlayService}
|
||||
private static class OverlayClient extends IDreamOverlayClient.Stub {
|
||||
private final DreamOverlayService mService;
|
||||
private boolean mShowComplications;
|
||||
private ComponentName mDreamComponent;
|
||||
IDreamOverlayCallback mDreamOverlayCallback;
|
||||
|
||||
OverlayClient(DreamOverlayService service) {
|
||||
mService = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDream(WindowManager.LayoutParams layoutParams,
|
||||
IDreamOverlayCallback callback, String dreamComponent,
|
||||
boolean shouldShowComplications) {
|
||||
mDreamOverlayCallback = callback;
|
||||
public void startDream(WindowManager.LayoutParams params, IDreamOverlayCallback callback,
|
||||
String dreamComponent, boolean shouldShowComplications) throws RemoteException {
|
||||
mDreamComponent = ComponentName.unflattenFromString(dreamComponent);
|
||||
mShowComplications = shouldShowComplications;
|
||||
onStartDream(layoutParams);
|
||||
mDreamOverlayCallback = callback;
|
||||
mService.startDream(this, params);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void wakeUp() {
|
||||
mService.wakeUp(this, () -> {
|
||||
try {
|
||||
mDreamOverlayCallback.onWakeUpComplete();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Could not notify dream of wakeUp", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endDream() {
|
||||
onEndDream();
|
||||
mService.endDream(this);
|
||||
}
|
||||
|
||||
private void onExitRequested() {
|
||||
try {
|
||||
mDreamOverlayCallback.onExitRequested();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Could not request exit:" + e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldShowComplications() {
|
||||
return mShowComplications;
|
||||
}
|
||||
|
||||
private ComponentName getComponent() {
|
||||
return mDreamComponent;
|
||||
}
|
||||
}
|
||||
|
||||
private void startDream(OverlayClient client, WindowManager.LayoutParams params) {
|
||||
endDream(mCurrentClient);
|
||||
mCurrentClient = client;
|
||||
onStartDream(params);
|
||||
}
|
||||
|
||||
private void endDream(OverlayClient client) {
|
||||
if (client == null || client != mCurrentClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
onEndDream();
|
||||
mCurrentClient = null;
|
||||
}
|
||||
|
||||
private void wakeUp(OverlayClient client, Runnable callback) {
|
||||
if (mCurrentClient != client) {
|
||||
return;
|
||||
}
|
||||
|
||||
onWakeUp(callback);
|
||||
}
|
||||
|
||||
private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
|
||||
@Override
|
||||
public void wakeUp() {
|
||||
onWakeUp(() -> {
|
||||
try {
|
||||
mDreamOverlayCallback.onWakeUpComplete();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Could not notify dream of wakeUp:" + e);
|
||||
}
|
||||
});
|
||||
public void getClient(IDreamOverlayClientCallback callback) {
|
||||
try {
|
||||
callback.onDreamOverlayClient(
|
||||
new OverlayClient(DreamOverlayService.this));
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "could not send client to callback", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IDreamOverlayCallback mDreamOverlayCallback;
|
||||
|
||||
public DreamOverlayService() {
|
||||
}
|
||||
|
||||
@@ -110,18 +172,23 @@ public abstract class DreamOverlayService extends Service {
|
||||
* This method is invoked to request the dream exit.
|
||||
*/
|
||||
public final void requestExit() {
|
||||
try {
|
||||
mDreamOverlayCallback.onExitRequested();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Could not request exit:" + e);
|
||||
if (mCurrentClient == null) {
|
||||
throw new IllegalStateException("requested exit with no dream present");
|
||||
}
|
||||
|
||||
mCurrentClient.onExitRequested();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether to show complications on the dream overlay.
|
||||
*/
|
||||
public final boolean shouldShowComplications() {
|
||||
return mShowComplications;
|
||||
if (mCurrentClient == null) {
|
||||
throw new IllegalStateException(
|
||||
"requested if should show complication when no dream active");
|
||||
}
|
||||
|
||||
return mCurrentClient.shouldShowComplications();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,6 +196,10 @@ public abstract class DreamOverlayService extends Service {
|
||||
* @hide
|
||||
*/
|
||||
public final ComponentName getDreamComponent() {
|
||||
return mDreamComponent;
|
||||
if (mCurrentClient == null) {
|
||||
throw new IllegalStateException("requested dream component when no dream active");
|
||||
}
|
||||
|
||||
return mCurrentClient.getComponent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,25 +248,39 @@ public class DreamService extends Service implements Window.Callback {
|
||||
private OverlayConnection mOverlayConnection;
|
||||
|
||||
private static class OverlayConnection extends PersistentServiceConnection<IDreamOverlay> {
|
||||
// Overlay set during onBind.
|
||||
private IDreamOverlay mOverlay;
|
||||
// Retrieved Client
|
||||
private IDreamOverlayClient mClient;
|
||||
|
||||
// A list of pending requests to execute on the overlay.
|
||||
private final ArrayList<Consumer<IDreamOverlay>> mConsumers = new ArrayList<>();
|
||||
private final ArrayList<Consumer<IDreamOverlayClient>> mConsumers = new ArrayList<>();
|
||||
|
||||
private final IDreamOverlayClientCallback mClientCallback =
|
||||
new IDreamOverlayClientCallback.Stub() {
|
||||
@Override
|
||||
public void onDreamOverlayClient(IDreamOverlayClient client) {
|
||||
mClient = client;
|
||||
|
||||
for (Consumer<IDreamOverlayClient> consumer : mConsumers) {
|
||||
consumer.accept(mClient);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Callback<IDreamOverlay> mCallback = new Callback<IDreamOverlay>() {
|
||||
@Override
|
||||
public void onConnected(ObservableServiceConnection<IDreamOverlay> connection,
|
||||
IDreamOverlay service) {
|
||||
mOverlay = service;
|
||||
for (Consumer<IDreamOverlay> consumer : mConsumers) {
|
||||
consumer.accept(mOverlay);
|
||||
try {
|
||||
service.getClient(mClientCallback);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "could not get DreamOverlayClient", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected(ObservableServiceConnection<IDreamOverlay> connection,
|
||||
int reason) {
|
||||
mOverlay = null;
|
||||
mClient = null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -296,16 +310,16 @@ public class DreamService extends Service implements Window.Callback {
|
||||
super.unbind();
|
||||
}
|
||||
|
||||
public void addConsumer(Consumer<IDreamOverlay> consumer) {
|
||||
public void addConsumer(Consumer<IDreamOverlayClient> consumer) {
|
||||
execute(() -> {
|
||||
mConsumers.add(consumer);
|
||||
if (mOverlay != null) {
|
||||
consumer.accept(mOverlay);
|
||||
if (mClient != null) {
|
||||
consumer.accept(mClient);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void removeConsumer(Consumer<IDreamOverlay> consumer) {
|
||||
public void removeConsumer(Consumer<IDreamOverlayClient> consumer) {
|
||||
execute(() -> mConsumers.remove(consumer));
|
||||
}
|
||||
|
||||
@@ -1365,7 +1379,7 @@ public class DreamService extends Service implements Window.Callback {
|
||||
|
||||
mWindow.getDecorView().addOnAttachStateChangeListener(
|
||||
new View.OnAttachStateChangeListener() {
|
||||
private Consumer<IDreamOverlay> mDreamStartOverlayConsumer;
|
||||
private Consumer<IDreamOverlayClient> mDreamStartOverlayConsumer;
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) {
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
|
||||
package android.service.dreams;
|
||||
|
||||
import android.service.dreams.IDreamOverlayCallback;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.service.dreams.IDreamOverlayClientCallback;
|
||||
|
||||
/**
|
||||
* {@link IDreamOverlay} provides a way for a component to annotate a dream with additional view
|
||||
@@ -28,20 +27,7 @@ import android.view.WindowManager.LayoutParams;
|
||||
*/
|
||||
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.
|
||||
* @param dreamComponent The component name of the dream service requesting overlay.
|
||||
* @param shouldShowComplications Whether the dream overlay should show complications, e.g. clock
|
||||
* and weather.
|
||||
* Retrieves a client the caller can use to interact with the dream overlay.
|
||||
*/
|
||||
void startDream(in LayoutParams params, in IDreamOverlayCallback callback,
|
||||
in String dreamComponent, in boolean shouldShowComplications);
|
||||
|
||||
/** Called when the dream is waking, to do any exit animations */
|
||||
void wakeUp();
|
||||
|
||||
/** Called when the dream has ended. */
|
||||
void endDream();
|
||||
void getClient(in IDreamOverlayClientCallback callback);
|
||||
}
|
||||
|
||||
45
core/java/android/service/dreams/IDreamOverlayClient.aidl
Normal file
45
core/java/android/service/dreams/IDreamOverlayClient.aidl
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2023, 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 IDreamOverlayClient} allows {@link DreamService} instances to act upon the dream overlay.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
interface IDreamOverlayClient {
|
||||
/**
|
||||
* @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.
|
||||
* @param dreamComponent The component name of the dream service requesting overlay.
|
||||
* @param shouldShowComplications Whether the dream overlay should show complications, e.g. clock
|
||||
* and weather.
|
||||
*/
|
||||
void startDream(in LayoutParams params, in IDreamOverlayCallback callback,
|
||||
in String dreamComponent, in boolean shouldShowComplications);
|
||||
|
||||
/** Called when the dream is waking, to do any exit animations */
|
||||
void wakeUp();
|
||||
|
||||
/** Called when the dream has ended. */
|
||||
void endDream();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2023, 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.IDreamOverlayClient;
|
||||
|
||||
/**
|
||||
* {@link IDreamOverlayClientCallback} allows receiving a requested {@link IDreamOverlayClient}.
|
||||
* @hide
|
||||
*/
|
||||
interface IDreamOverlayClientCallback {
|
||||
/**
|
||||
* Called with a unique {@link IDreamOverlayClient}.
|
||||
*/
|
||||
void onDreamOverlayClient(in IDreamOverlayClient client);
|
||||
}
|
||||
@@ -31,6 +31,8 @@ import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.service.dreams.IDreamOverlay;
|
||||
import android.service.dreams.IDreamOverlayCallback;
|
||||
import android.service.dreams.IDreamOverlayClient;
|
||||
import android.service.dreams.IDreamOverlayClientCallback;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -58,6 +60,7 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@SmallTest
|
||||
@@ -148,13 +151,25 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
||||
mDreamOverlayCallbackController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStartMetricsLogged() throws Exception {
|
||||
public IDreamOverlayClient getClient() throws RemoteException {
|
||||
final IBinder proxy = mService.onBind(new Intent());
|
||||
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
|
||||
final IDreamOverlayClientCallback callback =
|
||||
Mockito.mock(IDreamOverlayClientCallback.class);
|
||||
overlay.getClient(callback);
|
||||
final ArgumentCaptor<IDreamOverlayClient> clientCaptor =
|
||||
ArgumentCaptor.forClass(IDreamOverlayClient.class);
|
||||
verify(callback).onDreamOverlayClient(clientCaptor.capture());
|
||||
|
||||
return clientCaptor.getValue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStartMetricsLogged() throws Exception {
|
||||
final IDreamOverlayClient client = getClient();
|
||||
|
||||
// Inform the overlay service of dream starting.
|
||||
overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
false /*shouldShowComplication*/);
|
||||
mMainExecutor.runAllReady();
|
||||
|
||||
@@ -165,11 +180,10 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testOverlayContainerViewAddedToWindow() throws Exception {
|
||||
final IBinder proxy = mService.onBind(new Intent());
|
||||
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
|
||||
final IDreamOverlayClient client = getClient();
|
||||
|
||||
// Inform the overlay service of dream starting.
|
||||
overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
false /*shouldShowComplication*/);
|
||||
mMainExecutor.runAllReady();
|
||||
|
||||
@@ -178,11 +192,10 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testDreamOverlayContainerViewControllerInitialized() throws Exception {
|
||||
final IBinder proxy = mService.onBind(new Intent());
|
||||
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
|
||||
final IDreamOverlayClient client = getClient();
|
||||
|
||||
// Inform the overlay service of dream starting.
|
||||
overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
false /*shouldShowComplication*/);
|
||||
mMainExecutor.runAllReady();
|
||||
|
||||
@@ -196,11 +209,10 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
||||
.thenReturn(mDreamOverlayContainerViewParent)
|
||||
.thenReturn(null);
|
||||
|
||||
final IBinder proxy = mService.onBind(new Intent());
|
||||
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
|
||||
final IDreamOverlayClient client = getClient();
|
||||
|
||||
// Inform the overlay service of dream starting.
|
||||
overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
false /*shouldShowComplication*/);
|
||||
mMainExecutor.runAllReady();
|
||||
|
||||
@@ -209,11 +221,10 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testShouldShowComplicationsSetByStartDream() throws RemoteException {
|
||||
final IBinder proxy = mService.onBind(new Intent());
|
||||
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
|
||||
final IDreamOverlayClient client = getClient();
|
||||
|
||||
// Inform the overlay service of dream starting.
|
||||
overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
true /*shouldShowComplication*/);
|
||||
|
||||
assertThat(mService.shouldShowComplications()).isTrue();
|
||||
@@ -221,11 +232,10 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testLowLightSetByStartDream() throws RemoteException {
|
||||
final IBinder proxy = mService.onBind(new Intent());
|
||||
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
|
||||
final IDreamOverlayClient client = getClient();
|
||||
|
||||
// Inform the overlay service of dream starting.
|
||||
overlay.startDream(mWindowParams, mDreamOverlayCallback,
|
||||
client.startDream(mWindowParams, mDreamOverlayCallback,
|
||||
LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
|
||||
mMainExecutor.runAllReady();
|
||||
|
||||
@@ -235,11 +245,10 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testOnEndDream() throws RemoteException {
|
||||
final IBinder proxy = mService.onBind(new Intent());
|
||||
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
|
||||
final IDreamOverlayClient client = getClient();
|
||||
|
||||
// Inform the overlay service of dream starting.
|
||||
overlay.startDream(mWindowParams, mDreamOverlayCallback,
|
||||
client.startDream(mWindowParams, mDreamOverlayCallback,
|
||||
LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
|
||||
mMainExecutor.runAllReady();
|
||||
|
||||
@@ -261,11 +270,10 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testDestroy() throws RemoteException {
|
||||
final IBinder proxy = mService.onBind(new Intent());
|
||||
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
|
||||
final IDreamOverlayClient client = getClient();
|
||||
|
||||
// Inform the overlay service of dream starting.
|
||||
overlay.startDream(mWindowParams, mDreamOverlayCallback,
|
||||
client.startDream(mWindowParams, mDreamOverlayCallback,
|
||||
LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
|
||||
mMainExecutor.runAllReady();
|
||||
|
||||
@@ -305,15 +313,14 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testDecorViewNotAddedToWindowAfterDestroy() throws Exception {
|
||||
final IBinder proxy = mService.onBind(new Intent());
|
||||
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
|
||||
final IDreamOverlayClient client = getClient();
|
||||
|
||||
// Destroy the service.
|
||||
mService.onDestroy();
|
||||
mMainExecutor.runAllReady();
|
||||
|
||||
// Inform the overlay service of dream starting.
|
||||
overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
false /*shouldShowComplication*/);
|
||||
mMainExecutor.runAllReady();
|
||||
|
||||
@@ -331,11 +338,10 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testResetCurrentOverlayWhenConnectedToNewDream() throws RemoteException {
|
||||
final IBinder proxy = mService.onBind(new Intent());
|
||||
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
|
||||
final IDreamOverlayClient client = getClient();
|
||||
|
||||
// Inform the overlay service of dream starting. Do not show dream complications.
|
||||
overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
false /*shouldShowComplication*/);
|
||||
mMainExecutor.runAllReady();
|
||||
|
||||
@@ -352,7 +358,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
||||
// New dream starting with dream complications showing. Note that when a new dream is
|
||||
// binding to the dream overlay service, it receives the same instance of IBinder as the
|
||||
// first one.
|
||||
overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
true /*shouldShowComplication*/);
|
||||
mMainExecutor.runAllReady();
|
||||
|
||||
@@ -371,11 +377,10 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testWakeUp() throws RemoteException {
|
||||
final IBinder proxy = mService.onBind(new Intent());
|
||||
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
|
||||
final IDreamOverlayClient client = getClient();
|
||||
|
||||
// Inform the overlay service of dream starting.
|
||||
overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
|
||||
true /*shouldShowComplication*/);
|
||||
mMainExecutor.runAllReady();
|
||||
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.dreams;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.service.dreams.DreamOverlayService;
|
||||
import android.service.dreams.IDreamOverlay;
|
||||
import android.service.dreams.IDreamOverlayCallback;
|
||||
import android.service.dreams.IDreamOverlayClient;
|
||||
import android.service.dreams.IDreamOverlayClientCallback;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* A collection of tests to exercise {@link DreamOverlayService}.
|
||||
*/
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DreamOverlayServiceTest {
|
||||
private static final ComponentName FIRST_DREAM_COMPONENT =
|
||||
ComponentName.unflattenFromString("com.foo.bar/.DreamService");
|
||||
private static final ComponentName SECOND_DREAM_COMPONENT =
|
||||
ComponentName.unflattenFromString("com.foo.baz/.DreamService");
|
||||
|
||||
@Mock
|
||||
WindowManager.LayoutParams mLayoutParams;
|
||||
|
||||
@Mock
|
||||
IDreamOverlayCallback mOverlayCallback;
|
||||
|
||||
/**
|
||||
* {@link TestDreamOverlayService} is a simple {@link DreamOverlayService} implementation for
|
||||
* tracking interactions across {@link IDreamOverlay} binder interface. The service reports
|
||||
* interactions to a {@link Monitor} instance provided at construction.
|
||||
*/
|
||||
private static class TestDreamOverlayService extends DreamOverlayService {
|
||||
/**
|
||||
* An interface implemented to be informed when the corresponding methods in
|
||||
* {@link TestDreamOverlayService} are invoked.
|
||||
*/
|
||||
interface Monitor {
|
||||
void onStartDream();
|
||||
void onEndDream();
|
||||
void onWakeUp();
|
||||
}
|
||||
|
||||
private final Monitor mMonitor;
|
||||
|
||||
TestDreamOverlayService(Monitor monitor) {
|
||||
super();
|
||||
mMonitor = monitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
|
||||
mMonitor.onStartDream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEndDream() {
|
||||
mMonitor.onEndDream();
|
||||
super.onEndDream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWakeUp(@NonNull Runnable onCompleteCallback) {
|
||||
mMonitor.onWakeUp();
|
||||
super.onWakeUp(onCompleteCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link IDreamOverlayClientCallback} implementation that captures the requested client.
|
||||
*/
|
||||
private static class OverlayClientCallback extends IDreamOverlayClientCallback.Stub {
|
||||
public IDreamOverlayClient retrievedClient;
|
||||
@Override
|
||||
public void onDreamOverlayClient(IDreamOverlayClient client) throws RemoteException {
|
||||
retrievedClient = client;
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that only the currently started dream is able to affect the overlay.
|
||||
*/
|
||||
@Test
|
||||
public void testOverlayClientInteraction() throws RemoteException {
|
||||
final TestDreamOverlayService.Monitor monitor = Mockito.mock(
|
||||
TestDreamOverlayService.Monitor.class);
|
||||
final TestDreamOverlayService service = new TestDreamOverlayService(monitor);
|
||||
final IBinder binder = service.onBind(new Intent());
|
||||
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(binder);
|
||||
|
||||
// Create two overlay clients and ensure they are unique.
|
||||
final IDreamOverlayClient firstClient = getClient(overlay);
|
||||
assertThat(firstClient).isNotNull();
|
||||
|
||||
final IDreamOverlayClient secondClient = getClient(overlay);
|
||||
assertThat(secondClient).isNotNull();
|
||||
|
||||
assertThat(firstClient).isNotEqualTo(secondClient);
|
||||
|
||||
// Start a dream with the first client and ensure the dream is now active from the
|
||||
// overlay's perspective.
|
||||
firstClient.startDream(mLayoutParams, mOverlayCallback,
|
||||
FIRST_DREAM_COMPONENT.flattenToString(), false);
|
||||
|
||||
|
||||
verify(monitor).onStartDream();
|
||||
assertThat(service.getDreamComponent()).isEqualTo(FIRST_DREAM_COMPONENT);
|
||||
|
||||
Mockito.clearInvocations(monitor);
|
||||
|
||||
// Start a dream from the second client and verify that the overlay has both cycled to
|
||||
// the new dream (ended/started).
|
||||
secondClient.startDream(mLayoutParams, mOverlayCallback,
|
||||
SECOND_DREAM_COMPONENT.flattenToString(), false);
|
||||
|
||||
verify(monitor).onEndDream();
|
||||
verify(monitor).onStartDream();
|
||||
assertThat(service.getDreamComponent()).isEqualTo(SECOND_DREAM_COMPONENT);
|
||||
|
||||
Mockito.clearInvocations(monitor);
|
||||
|
||||
// Verify that interactions with the first, now inactive client don't affect the overlay.
|
||||
firstClient.endDream();
|
||||
verify(monitor, never()).onEndDream();
|
||||
|
||||
firstClient.wakeUp();
|
||||
verify(monitor, never()).onWakeUp();
|
||||
}
|
||||
|
||||
private static IDreamOverlayClient getClient(IDreamOverlay overlay) throws RemoteException {
|
||||
final OverlayClientCallback callback = new OverlayClientCallback();
|
||||
overlay.getClient(callback);
|
||||
return callback.retrievedClient;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user