blob: f50ad32c6c88341ba7c6f53a802de6bc2b10a25d [file] [log] [blame]
/*
* Copyright (C) 2020 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.car.user;
import static android.car.test.mocks.AndroidMockitoHelper.mockContextGetService;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.car.AbstractExtendedMockitoCarServiceTestCase;
import android.car.hardware.power.CarPowerManager;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
import android.car.settings.CarSettings;
import android.car.test.mocks.JavaMockitoHelper;
import android.car.user.CarUserManager;
import android.car.user.CarUserManager.UserLifecycleEvent;
import android.car.user.CarUserManager.UserLifecycleListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.car.CarLocalServices;
import com.android.car.CarPowerManagementService;
import com.android.car.R;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import java.util.concurrent.CountDownLatch;
public class CarUserNoticeServiceTest extends AbstractExtendedMockitoCarServiceTestCase {
private static final long TIMEOUT_MS = 10_000;
@Mock
private Context mMockContext;
@Mock
private Context mOtherMockContext;
@Mock
private Resources mMockedResources;
@Mock
private CarPowerManagementService mMockCarPowerManagementService;
@Mock
private CarUserService mMockCarUserService;
@Mock
private PowerManager mMockPowerManager;
@Mock
private AppOpsManager mMockAppOpsManager;
@Mock
private PackageManager mMockPackageManager;
@Mock
private CarPowerManager mCarPowerManager;
@Captor
private ArgumentCaptor<BroadcastReceiver> mDisplayBroadcastReceiver;
@Captor
private ArgumentCaptor<UserLifecycleListener> mUserLifecycleListenerArgumentCaptor;
@Captor
private ArgumentCaptor<CarPowerStateListener> mPowerStateListener;
private CarUserNoticeService mCarUserNoticeService;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@Override
protected void onSessionBuilder(CustomMockitoSessionBuilder session) {
session
.spyStatic(CarLocalServices.class);
}
/**
* Initialize all of the objects with the @Mock annotation.
*/
@Before
public void setUpMocks() throws Exception {
doReturn(mCarPowerManager).when(() -> CarLocalServices.createCarPowerManager(mMockContext));
mockGetCarLocalService(CarPowerManagementService.class, mMockCarPowerManagementService);
mockGetCarLocalService(CarUserService.class, mMockCarUserService);
putSettingsInt(CarSettings.Secure.KEY_ENABLE_INITIAL_NOTICE_SCREEN_TO_USER, 1);
when(mMockContext.getResources()).thenReturn(mMockedResources);
when(mMockContext.getContentResolver())
.thenReturn(InstrumentationRegistry.getInstrumentation().getTargetContext()
.getContentResolver());
when(mMockedResources.getString(anyInt())).thenReturn("com.foo/.Blah");
mockContextGetService(mMockContext, PowerManager.class, mMockPowerManager);
mockContextGetService(mMockContext, AppOpsManager.class, mMockAppOpsManager);
mockContextGetService(mMockContext, PackageManager.class, mMockPackageManager);
when(mMockPackageManager.getPackageUidAsUser(any(), anyInt())).thenReturn(1);
mCarUserNoticeService = new CarUserNoticeService(mMockContext, mHandler);
mCarUserNoticeService.init();
verify(mMockCarUserService).addUserLifecycleListener(
mUserLifecycleListenerArgumentCaptor.capture());
verify(mMockContext).registerReceiver(mDisplayBroadcastReceiver.capture(),
any(IntentFilter.class));
verify(mCarPowerManager).setListener(mPowerStateListener.capture());
}
@Test
public void featureDisabledTest() {
// Feature is disabled when the string is empty
when(mMockedResources.getString(R.string.config_userNoticeUiService)).thenReturn("");
when(mOtherMockContext.getResources()).thenReturn(mMockedResources);
CarUserNoticeService otherService = new CarUserNoticeService(mOtherMockContext);
otherService.init();
verify(mOtherMockContext, never()).registerReceiver(any(), any());
}
@Test
public void uiHiddenWhenBroadcastOffReceived() throws Exception {
setUser();
// reset UI
setDisplayOff();
CountDownLatch latch = mockUnbindService();
sendBroadcast(Intent.ACTION_SCREEN_OFF);
assertLatchCalled(latch);
}
@Test
public void uiShownWhenBroadcastOnReceived() throws Exception {
setUser();
// reset UI
setDisplayOff();
CountDownLatch latch = mockUnbindService();
sendBroadcast(Intent.ACTION_SCREEN_OFF);
assertLatchCalled(latch);
// send screen on broadcast
setDisplayOn();
latch = mockBindService();
sendBroadcast(Intent.ACTION_SCREEN_ON);
assertLatchCalled(latch);
}
@Test
public void uiHiddenWhenPowerShutDown() throws Exception {
setUser();
// reset UI
setDisplayOff();
CountDownLatch latch = mockUnbindService();
sendPowerStateChange(CarPowerManager.CarPowerStateListener.SHUTDOWN_PREPARE);
assertLatchCalled(latch);
}
@Test
public void uiShownWhenPowerOn() throws Exception {
setUser();
// reset UI
setDisplayOff();
CountDownLatch latch = mockUnbindService();
sendPowerStateChange(CarPowerManager.CarPowerStateListener.SHUTDOWN_PREPARE);
assertLatchCalled(latch);
// send Power On
setDisplayOn();
latch = mockBindService();
sendPowerStateChange(CarPowerManager.CarPowerStateListener.ON);
assertLatchCalled(latch);
}
@Test
public void uiNotShownIfKeyDisabled() throws Exception {
setUser();
// reset UI
setDisplayOff();
CountDownLatch latch = mockUnbindService();
sendBroadcast(Intent.ACTION_SCREEN_OFF);
assertLatchCalled(latch);
// UI not shown if key is disabled
setDisplayOn();
latch = mockKeySettings(
CarSettings.Secure.KEY_ENABLE_INITIAL_NOTICE_SCREEN_TO_USER, 0);
sendBroadcast(Intent.ACTION_SCREEN_ON);
assertLatchCalled(latch);
// invoked only once, when user switched
verify(mMockContext, times(1)).bindServiceAsUser(any(), any(), anyInt(), any());
}
private static void assertLatchCalled(CountDownLatch latch) throws Exception {
JavaMockitoHelper.await(latch, TIMEOUT_MS);
}
private void switchUser(int userId) throws Exception {
// Notify listeners about user switch.
mUserLifecycleListenerArgumentCaptor.getValue().onEvent(new UserLifecycleEvent(
CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, userId));
}
private CountDownLatch mockBindService() {
CountDownLatch latch = new CountDownLatch(1);
when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any()))
.thenAnswer(inv -> {
latch.countDown();
return true;
});
return latch;
}
private CountDownLatch mockUnbindService() {
CountDownLatch latch = new CountDownLatch(1);
doAnswer(inv -> {
latch.countDown();
return null;
}).when(mMockContext).unbindService(any());
return latch;
}
private static CountDownLatch mockKeySettings(String key, int value) {
CountDownLatch latch = new CountDownLatch(1);
when(Settings.Secure.getIntForUser(any(),
eq(key), anyInt(),
anyInt()))
.thenAnswer(inv -> {
latch.countDown();
return value;
});
return latch;
}
private void setDisplayOn() {
when(mMockPowerManager.isInteractive()).thenReturn(true);
}
private void setDisplayOff() {
when(mMockPowerManager.isInteractive()).thenReturn(false);
}
private void sendBroadcast(String action) {
Intent intent = new Intent();
intent.setAction(action);
mHandler.post(() -> mDisplayBroadcastReceiver.getValue().onReceive(mMockContext, intent));
}
private void sendPowerStateChange(int state) {
mPowerStateListener.getValue().onStateChanged(state);
}
private void setUser() throws Exception {
// switch user (required to set user)
setDisplayOn();
CountDownLatch latch = mockBindService();
switchUser(UserHandle.MIN_SECONDARY_USER_ID);
assertLatchCalled(latch);
}
}