1 /*
2  * Copyright (C) 2022 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 android.os.PowerManager.USER_ACTIVITY_EVENT_OTHER;
20 import static android.os.PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS;
21 
22 import static org.mockito.ArgumentMatchers.anyInt;
23 import static org.mockito.ArgumentMatchers.anyLong;
24 import static org.mockito.ArgumentMatchers.anyString;
25 import static org.mockito.ArgumentMatchers.eq;
26 import static org.mockito.Mockito.any;
27 import static org.mockito.Mockito.clearInvocations;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.when;
31 
32 import android.app.ActivityTaskManager;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.ServiceConnection;
36 import android.os.Binder;
37 import android.os.Handler;
38 import android.os.IBinder;
39 import android.os.IPowerManager;
40 import android.os.IRemoteCallback;
41 import android.os.PowerManager;
42 import android.os.RemoteException;
43 import android.os.test.TestLooper;
44 import android.service.dreams.IDreamService;
45 
46 import androidx.test.filters.SmallTest;
47 import androidx.test.runner.AndroidJUnit4;
48 
49 import org.junit.Before;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.mockito.ArgumentCaptor;
53 import org.mockito.Captor;
54 import org.mockito.Mock;
55 import org.mockito.MockitoAnnotations;
56 
57 @SmallTest
58 @RunWith(AndroidJUnit4.class)
59 public class DreamControllerTest {
60     @Mock
61     private DreamController.Listener mListener;
62     @Mock
63     private Context mContext;
64 
65     @Mock
66     private ActivityTaskManager mActivityTaskManager;
67     @Mock
68     private IPowerManager mPowerManager;
69 
70     @Mock
71     private IBinder mIBinder;
72     @Mock
73     private IDreamService mIDreamService;
74 
75     @Captor
76     private ArgumentCaptor<ServiceConnection> mServiceConnectionACaptor;
77     @Captor
78     private ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientCaptor;
79     @Captor
80     private ArgumentCaptor<IRemoteCallback> mRemoteCallbackCaptor;
81 
82     private final TestLooper mLooper = new TestLooper();
83     private final Handler mHandler = new Handler(mLooper.getLooper());
84 
85     private DreamController mDreamController;
86 
87     private Binder mToken;
88     private ComponentName mDreamName;
89     private ComponentName mOverlayName;
90 
91     @Before
setup()92     public void setup() {
93         MockitoAnnotations.initMocks(this);
94 
95         when(mIDreamService.asBinder()).thenReturn(mIBinder);
96         when(mIBinder.queryLocalInterface(anyString())).thenReturn(mIDreamService);
97         when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
98         when(mContext.getSystemService(Context.ACTIVITY_TASK_SERVICE))
99                 .thenReturn(mActivityTaskManager);
100         when(mContext.getSystemServiceName(ActivityTaskManager.class))
101                 .thenReturn(Context.ACTIVITY_TASK_SERVICE);
102 
103         final PowerManager powerManager = new PowerManager(mContext, mPowerManager, null, null);
104         when(mContext.getSystemService(Context.POWER_SERVICE))
105                 .thenReturn(powerManager);
106         when(mContext.getSystemServiceName(PowerManager.class))
107                 .thenReturn(Context.POWER_SERVICE);
108 
109         mToken = new Binder();
110         mDreamName = ComponentName.unflattenFromString("dream");
111         mOverlayName = ComponentName.unflattenFromString("dream_overlay");
112         mDreamController = new DreamController(mContext, mHandler, mListener);
113     }
114 
115     @Test
startDream_attachOnServiceConnected()116     public void startDream_attachOnServiceConnected() throws RemoteException {
117         // Call dream controller to start dreaming.
118         mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
119                 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
120 
121         // Mock service connected.
122         final ServiceConnection serviceConnection = captureServiceConnection();
123         serviceConnection.onServiceConnected(mDreamName, mIBinder);
124         mLooper.dispatchAll();
125 
126         // Verify that dream service is called to attach.
127         verify(mIDreamService).attach(eq(mToken), eq(false) /*doze*/,
128                 eq(false) /*preview*/, any());
129     }
130 
131     @Test
startDream_dreamListenerNotified()132     public void startDream_dreamListenerNotified() {
133         // Call dream controller to start dreaming.
134         mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
135                 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
136 
137         // Mock service connected.
138         final ServiceConnection serviceConnection = captureServiceConnection();
139         serviceConnection.onServiceConnected(mDreamName, mIBinder);
140         mLooper.dispatchAll();
141 
142         // Verify that dream service is called to attach.
143         verify(mListener).onDreamStarted(any());
144     }
145 
146     @Test
stopDream_dreamListenerNotified()147     public void stopDream_dreamListenerNotified() {
148         // Start dream.
149         mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
150                 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
151         captureServiceConnection().onServiceConnected(mDreamName, mIBinder);
152         mLooper.dispatchAll();
153 
154         // Stop dream.
155         mDreamController.stopDream(true /*immediate*/, "test stop dream" /*reason*/);
156         mLooper.dispatchAll();
157 
158         // Verify that dream service is called to detach.
159         verify(mListener).onDreamStopped(any());
160     }
161 
162     @Test
startDream_attachOnServiceConnectedInPreviewMode()163     public void startDream_attachOnServiceConnectedInPreviewMode() throws RemoteException {
164         // Call dream controller to start dreaming.
165         mDreamController.startDream(mToken, mDreamName, true /*isPreview*/, false /*doze*/,
166                 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
167 
168         // Mock service connected.
169         final ServiceConnection serviceConnection = captureServiceConnection();
170         serviceConnection.onServiceConnected(mDreamName, mIBinder);
171         mLooper.dispatchAll();
172 
173         // Verify that dream service is called to attach.
174         verify(mIDreamService).attach(eq(mToken), eq(false) /*doze*/,
175                 eq(true) /*preview*/, any());
176     }
177 
178     @Test
startDream_startASecondDream_detachOldDreamOnceNewDreamIsStarted()179     public void startDream_startASecondDream_detachOldDreamOnceNewDreamIsStarted()
180             throws RemoteException {
181         // Start first dream.
182         mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
183                 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
184         captureServiceConnection().onServiceConnected(mDreamName, mIBinder);
185         mLooper.dispatchAll();
186         clearInvocations(mContext);
187 
188         // Set up second dream.
189         final Binder newToken = new Binder();
190         final ComponentName newDreamName = ComponentName.unflattenFromString("new_dream");
191         final ComponentName newOverlayName = ComponentName.unflattenFromString("new_dream_overlay");
192         final IDreamService newDreamService = mock(IDreamService.class);
193         final IBinder newBinder = mock(IBinder.class);
194         when(newDreamService.asBinder()).thenReturn(newBinder);
195         when(newBinder.queryLocalInterface(anyString())).thenReturn(newDreamService);
196 
197         // Start second dream.
198         mDreamController.startDream(newToken, newDreamName, false /*isPreview*/, false /*doze*/,
199                 0 /*userId*/, null /*wakeLock*/, newOverlayName, "test" /*reason*/);
200         captureServiceConnection().onServiceConnected(newDreamName, newBinder);
201         mLooper.dispatchAll();
202 
203         // Mock second dream started.
204         verify(newDreamService).attach(eq(newToken), eq(false) /*doze*/,
205                 eq(false) /*preview*/, mRemoteCallbackCaptor.capture());
206         mRemoteCallbackCaptor.getValue().sendResult(null /*data*/);
207         mLooper.dispatchAll();
208 
209         // Verify that the first dream is called to detach.
210         verify(mIDreamService).detach();
211     }
212 
213     @Test
stopDream_detachFromService()214     public void stopDream_detachFromService() throws RemoteException {
215         // Start dream.
216         mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
217                 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
218         captureServiceConnection().onServiceConnected(mDreamName, mIBinder);
219         mLooper.dispatchAll();
220 
221         // Stop dream.
222         mDreamController.stopDream(true /*immediate*/, "test stop dream" /*reason*/);
223 
224         // Verify that dream service is called to detach.
225         verify(mIDreamService).detach();
226     }
227 
228     @Test
serviceDisconnect_resetsScreenTimeout()229     public void serviceDisconnect_resetsScreenTimeout() throws RemoteException {
230         // Start dream.
231         mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
232                 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
233         ServiceConnection serviceConnection = captureServiceConnection();
234         serviceConnection.onServiceConnected(mDreamName, mIBinder);
235         mLooper.dispatchAll();
236 
237         // Dream disconnects unexpectedly.
238         serviceConnection.onServiceDisconnected(mDreamName);
239         mLooper.dispatchAll();
240 
241         // Power manager receives user activity signal.
242         verify(mPowerManager).userActivity(/*displayId=*/ anyInt(), /*time=*/ anyLong(),
243                 eq(USER_ACTIVITY_EVENT_OTHER),
244                 eq(USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS));
245     }
246 
247     @Test
binderDied_resetsScreenTimeout()248     public void binderDied_resetsScreenTimeout() throws RemoteException {
249         // Start dream.
250         mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
251                 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
252         captureServiceConnection().onServiceConnected(mDreamName, mIBinder);
253         mLooper.dispatchAll();
254 
255         // Dream binder dies.
256         captureDeathRecipient().binderDied();
257         mLooper.dispatchAll();
258 
259         // Power manager receives user activity signal.
260         verify(mPowerManager).userActivity(/*displayId=*/ anyInt(), /*time=*/ anyLong(),
261                 eq(USER_ACTIVITY_EVENT_OTHER),
262                 eq(USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS));
263     }
264 
captureServiceConnection()265     private ServiceConnection captureServiceConnection() {
266         verify(mContext).bindServiceAsUser(any(), mServiceConnectionACaptor.capture(), anyInt(),
267                 any());
268         return mServiceConnectionACaptor.getValue();
269     }
270 
captureDeathRecipient()271     private IBinder.DeathRecipient captureDeathRecipient() throws RemoteException {
272         verify(mIBinder).linkToDeath(mDeathRecipientCaptor.capture(), anyInt());
273         return mDeathRecipientCaptor.getValue();
274     }
275 }
276