1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.dreams; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.Mockito.any; 22 import static org.mockito.Mockito.doAnswer; 23 import static org.mockito.Mockito.never; 24 import static org.mockito.Mockito.verify; 25 26 import android.content.ComponentName; 27 import android.content.Intent; 28 import android.os.IBinder; 29 import android.os.RemoteException; 30 import android.service.dreams.DreamOverlayService; 31 import android.service.dreams.IDreamOverlay; 32 import android.service.dreams.IDreamOverlayCallback; 33 import android.service.dreams.IDreamOverlayClient; 34 import android.service.dreams.IDreamOverlayClientCallback; 35 import android.view.WindowManager; 36 37 import androidx.annotation.NonNull; 38 import androidx.test.filters.SmallTest; 39 import androidx.test.runner.AndroidJUnit4; 40 41 import org.junit.Before; 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 import org.mockito.ArgumentCaptor; 45 import org.mockito.Mock; 46 import org.mockito.Mockito; 47 import org.mockito.MockitoAnnotations; 48 49 import java.util.concurrent.Executor; 50 51 /** 52 * A collection of tests to exercise {@link DreamOverlayService}. 53 */ 54 @SmallTest 55 @RunWith(AndroidJUnit4.class) 56 public class DreamOverlayServiceTest { 57 private static final ComponentName FIRST_DREAM_COMPONENT = 58 ComponentName.unflattenFromString("com.foo.bar/.DreamService"); 59 private static final ComponentName SECOND_DREAM_COMPONENT = 60 ComponentName.unflattenFromString("com.foo.baz/.DreamService"); 61 62 @Mock 63 WindowManager.LayoutParams mLayoutParams; 64 65 @Mock 66 IDreamOverlayCallback mOverlayCallback; 67 68 @Mock 69 Executor mExecutor; 70 71 /** 72 * {@link TestDreamOverlayService} is a simple {@link DreamOverlayService} implementation for 73 * tracking interactions across {@link IDreamOverlay} binder interface. The service reports 74 * interactions to a {@link Monitor} instance provided at construction. 75 */ 76 private static class TestDreamOverlayService extends DreamOverlayService { 77 /** 78 * An interface implemented to be informed when the corresponding methods in 79 * {@link TestDreamOverlayService} are invoked. 80 */ 81 interface Monitor { onStartDream()82 void onStartDream(); onEndDream()83 void onEndDream(); onWakeUp()84 void onWakeUp(); 85 } 86 87 private final Monitor mMonitor; 88 TestDreamOverlayService(Monitor monitor, Executor executor)89 TestDreamOverlayService(Monitor monitor, Executor executor) { 90 super(executor); 91 mMonitor = monitor; 92 } 93 94 @Override onStartDream(@onNull WindowManager.LayoutParams layoutParams)95 public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) { 96 mMonitor.onStartDream(); 97 } 98 99 @Override onEndDream()100 public void onEndDream() { 101 mMonitor.onEndDream(); 102 super.onEndDream(); 103 } 104 } 105 106 /** 107 * A {@link IDreamOverlayClientCallback} implementation that captures the requested client. 108 */ 109 private static class OverlayClientCallback extends IDreamOverlayClientCallback.Stub { 110 public IDreamOverlayClient retrievedClient; 111 @Override onDreamOverlayClient(IDreamOverlayClient client)112 public void onDreamOverlayClient(IDreamOverlayClient client) throws RemoteException { 113 retrievedClient = client; 114 } 115 } 116 117 @Before setup()118 public void setup() { 119 MockitoAnnotations.initMocks(this); 120 } 121 122 /** 123 * Verifies that callbacks for subclasses are run on the provided executor. 124 */ 125 @Test testCallbacksRunOnExecutor()126 public void testCallbacksRunOnExecutor() throws RemoteException { 127 final TestDreamOverlayService.Monitor monitor = Mockito.mock( 128 TestDreamOverlayService.Monitor.class); 129 final TestDreamOverlayService service = new TestDreamOverlayService(monitor, mExecutor); 130 final IBinder binder = service.onBind(new Intent()); 131 final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(binder); 132 133 final IDreamOverlayClient client = getClient(overlay); 134 135 // Start the dream. 136 client.startDream(mLayoutParams, mOverlayCallback, 137 FIRST_DREAM_COMPONENT.flattenToString(), false); 138 139 // The callback should not have run yet. 140 verify(monitor, never()).onStartDream(); 141 142 // Run the Runnable sent to the executor. 143 ArgumentCaptor<Runnable> mRunnableCaptor = ArgumentCaptor.forClass(Runnable.class); 144 verify(mExecutor).execute(mRunnableCaptor.capture()); 145 mRunnableCaptor.getValue().run(); 146 147 // Callback is run. 148 verify(monitor).onStartDream(); 149 150 // Verify onWakeUp is run on the executor. 151 client.wakeUp(); 152 verify(monitor, never()).onWakeUp(); 153 mRunnableCaptor = ArgumentCaptor.forClass(Runnable.class); 154 verify(mExecutor).execute(mRunnableCaptor.capture()); 155 mRunnableCaptor.getValue().run(); 156 verify(monitor).onWakeUp(); 157 158 // Verify onEndDream is run on the executor. 159 client.endDream(); 160 verify(monitor, never()).onEndDream(); 161 mRunnableCaptor = ArgumentCaptor.forClass(Runnable.class); 162 verify(mExecutor).execute(mRunnableCaptor.capture()); 163 mRunnableCaptor.getValue().run(); 164 verify(monitor).onEndDream(); 165 } 166 167 /** 168 * Verifies that only the currently started dream is able to affect the overlay. 169 */ 170 @Test testOverlayClientInteraction()171 public void testOverlayClientInteraction() throws RemoteException { 172 doAnswer(invocation -> { 173 ((Runnable) invocation.getArgument(0)).run(); 174 return null; 175 }).when(mExecutor).execute(any()); 176 177 final TestDreamOverlayService.Monitor monitor = Mockito.mock( 178 TestDreamOverlayService.Monitor.class); 179 final TestDreamOverlayService service = new TestDreamOverlayService(monitor, mExecutor); 180 final IBinder binder = service.onBind(new Intent()); 181 final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(binder); 182 183 // Create two overlay clients and ensure they are unique. 184 final IDreamOverlayClient firstClient = getClient(overlay); 185 assertThat(firstClient).isNotNull(); 186 187 final IDreamOverlayClient secondClient = getClient(overlay); 188 assertThat(secondClient).isNotNull(); 189 190 assertThat(firstClient).isNotEqualTo(secondClient); 191 192 // Start a dream with the first client and ensure the dream is now active from the 193 // overlay's perspective. 194 firstClient.startDream(mLayoutParams, mOverlayCallback, 195 FIRST_DREAM_COMPONENT.flattenToString(), false); 196 197 198 verify(monitor).onStartDream(); 199 assertThat(service.getDreamComponent()).isEqualTo(FIRST_DREAM_COMPONENT); 200 201 Mockito.clearInvocations(monitor); 202 203 // Start a dream from the second client and verify that the overlay has both cycled to 204 // the new dream (ended/started). 205 secondClient.startDream(mLayoutParams, mOverlayCallback, 206 SECOND_DREAM_COMPONENT.flattenToString(), false); 207 208 verify(monitor).onEndDream(); 209 verify(monitor).onStartDream(); 210 assertThat(service.getDreamComponent()).isEqualTo(SECOND_DREAM_COMPONENT); 211 212 Mockito.clearInvocations(monitor); 213 214 // Verify that interactions with the first, now inactive client don't affect the overlay. 215 firstClient.endDream(); 216 verify(monitor, never()).onEndDream(); 217 218 firstClient.wakeUp(); 219 verify(monitor, never()).onWakeUp(); 220 } 221 getClient(IDreamOverlay overlay)222 private static IDreamOverlayClient getClient(IDreamOverlay overlay) throws RemoteException { 223 final OverlayClientCallback callback = new OverlayClientCallback(); 224 overlay.getClient(callback); 225 return callback.retrievedClient; 226 } 227 } 228