blob: 09c6deb7eb0b8228158329d697458c988c5ac925 [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 com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.google.common.truth.Truth.assertThat;
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.doReturn;
import static org.mockito.Mockito.mock;
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.hardware.power.CarPowerManager;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
import android.car.settings.CarSettings;
import android.car.test.mocks.AbstractExtendMockitoTestCase;
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;
import java.util.concurrent.TimeUnit;
public class CarUserNoticeServiceTest extends AbstractExtendMockitoTestCase {
@Mock
private Context mMockContext;
@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
.mockStatic(CarLocalServices.class)
.mockStatic(Settings.Secure.class);
}
/**
* Initialize all of the objects with the @Mock annotation.
*/
@Before
public void setUpMocks() throws Exception {
doReturn(mCarPowerManager).when(() -> CarLocalServices.createCarPowerManager(mMockContext));
doReturn(mMockCarPowerManagementService)
.when(() -> CarLocalServices.getService(CarPowerManagementService.class));
doReturn(mCarPowerManager).when(() -> CarLocalServices.createCarPowerManager(mMockContext));
doReturn(mMockCarUserService)
.when(() -> CarLocalServices.getService(CarUserService.class));
doReturn(1).when(() -> Settings.Secure.getIntForUser(any(),
eq(CarSettings.Secure.KEY_ENABLE_INITIAL_NOTICE_SCREEN_TO_USER), anyInt(),
anyInt()));
doReturn(mMockedResources).when(mMockContext).getResources();
doReturn(InstrumentationRegistry.getInstrumentation().getTargetContext()
.getContentResolver())
.when(mMockContext).getContentResolver();
doReturn("com.foo/.Blah").when(mMockedResources).getString(anyInt());
doReturn(mMockPowerManager).when(mMockContext).getSystemService(PowerManager.class);
doReturn(mMockAppOpsManager).when(mMockContext).getSystemService(AppOpsManager.class);
doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
doReturn(1).when(mMockPackageManager).getPackageUidAsUser(any(), anyInt());
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() {
Context mockContext = mock(Context.class);
// if feature is disabled, Resources.getString will return an
// empty string
doReturn("").when(mMockedResources).getString(R.string.config_userNoticeUiService);
doReturn(mMockedResources).when(mockContext).getResources();
CarUserNoticeService carUserNoticeService = new CarUserNoticeService(mockContext);
carUserNoticeService.init();
verify(mockContext, never()).registerReceiver(any(), any());
}
@Test
public void uiHiddenWhenBroadcastOffReceived() throws Exception {
setUser();
// reset UI
setDisplayOff();
CountDownLatch latch = mockUnbindService();
sendBroadcast(Intent.ACTION_SCREEN_OFF);
assetLatchCalled(latch);
}
@Test
public void uiShownWhenBroadcastOnReceived() throws Exception {
setUser();
// reset UI
setDisplayOff();
CountDownLatch latch = mockUnbindService();
sendBroadcast(Intent.ACTION_SCREEN_OFF);
assetLatchCalled(latch);
// send screen on broadcast
setDisplayOn();
latch = mockBindService();
sendBroadcast(Intent.ACTION_SCREEN_ON);
assetLatchCalled(latch);
}
@Test
public void uiHiddenWhenPowerShutDown() throws Exception {
setUser();
// reset UI
setDisplayOff();
CountDownLatch latch = mockUnbindService();
sendPowerStateChange(CarPowerManager.CarPowerStateListener.SHUTDOWN_PREPARE);
assetLatchCalled(latch);
}
@Test
public void uiShownWhenPowerOn() throws Exception {
setUser();
// reset UI
setDisplayOff();
CountDownLatch latch = mockUnbindService();
sendPowerStateChange(CarPowerManager.CarPowerStateListener.SHUTDOWN_PREPARE);
assetLatchCalled(latch);
// send Power On
setDisplayOn();
latch = mockBindService();
sendPowerStateChange(CarPowerManager.CarPowerStateListener.ON);
assetLatchCalled(latch);
}
@Test
public void uiNotShownIfKeyDisabled() throws Exception {
setUser();
// reset UI
setDisplayOff();
CountDownLatch latch = mockUnbindService();
sendBroadcast(Intent.ACTION_SCREEN_OFF);
assetLatchCalled(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);
assetLatchCalled(latch);
// invoked only once, when user switched
verify(mMockContext, times(1)).bindServiceAsUser(any(), any(), anyInt(), any());
}
private void assetLatchCalled(CountDownLatch latch) throws Exception {
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
}
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 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() {
doReturn(true).when(mMockPowerManager).isInteractive();
}
private void setDisplayOff() {
doReturn(false).when(mMockPowerManager).isInteractive();
}
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);
assetLatchCalled(latch);
}
}