blob: 3a8c9bf2cd1420748e34a23b9477116dfddd6129 [file] [log] [blame]
/*
* Copyright 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.camera.cts.headlesssystemusertest;
import static android.Manifest.permission.CAMERA_HEADLESS_SYSTEM_USER;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.*;
import android.app.ActivityManager;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
import android.hardware.camera2.cts.CameraTestUtils;
import android.hardware.camera2.cts.CameraTestUtils.MockStateCallback;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.Log;
import com.android.bedstead.harrier.DeviceState;
import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser;
import com.android.bedstead.harrier.annotations.RequireHeadlessSystemUserMode;
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.permissions.PermissionContext;
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.SystemUtil;
import com.android.ex.camera2.blocking.BlockingStateCallback;
import com.android.internal.camera.flags.Flags;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class Camera2HeadlessSystemUserTest extends Camera2ParameterizedTestCase {
private static final String TAG = "Camera2HeadlessSystemUserTest";
private static final int SWITCH_USER_WAIT_TIME = 5000;
private HandlerThread mHandlerThread;
private Handler mHandler;
private BlockingStateCallback mCameraListener;
private ActivityManager mActivityManager;
private UserManager mUserManager;
private String[] mCameraIdsUnderTest;
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Rule
@ClassRule
public static final DeviceState sDeviceState = new DeviceState();
@Override
public void setUp() throws Exception {
super.setUp();
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mActivityManager = mContext.getSystemService(ActivityManager.class);
assertNotNull("Can't get activity manager", mActivityManager);
mUserManager = mContext.getSystemService(UserManager.class);
assertNotNull("Can't get user manager", mUserManager);
mCameraIdsUnderTest = getCameraIdsUnderTest();
assertNotNull("Camera ids shouldn't be null", mCameraIdsUnderTest);
mCameraListener = spy(new BlockingStateCallback());
}
@Override
public void tearDown() throws Exception {
mHandlerThread.quitSafely();
mHandler = null;
super.tearDown();
}
/**
* Verifies that System Headless User can open the camera.
* This test is only valid for devices running in Headless
* System User Mode.
*/
@RequireHeadlessSystemUserMode(reason = "tests headless user behaviour")
@RequiresFlagsEnabled(Flags.FLAG_CAMERA_HSUM_PERMISSION)
@Test
public void testHeadlessSystemUser_OpenCamera() throws Exception {
for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
CameraDevice camera = null;
try {
MockStateCallback mockListener = MockStateCallback.mock();
mCameraListener = new BlockingStateCallback(mockListener);
openCameraAsHeadlessSystemUser(mCameraIdsUnderTest[i]);
camera = CameraTestUtils.verifyCameraStateOpened(mCameraIdsUnderTest[i],
mockListener);
} finally {
if (camera != null) {
camera.close();
}
}
}
}
/**
* Verifies that the System Headless User cannot open the camera
* without the permission CAMERA_HEADLESS_SYSTEM_USER.
* This test is only valid for devices running in Headless
* System User Mode.
*/
@RequireHeadlessSystemUserMode(reason = "tests headless user behaviour")
@RequiresFlagsEnabled(Flags.FLAG_CAMERA_HSUM_PERMISSION)
@Test
public void testHeadlessSystemUser_InvalidAccess() throws Exception {
assumeFalse("Skipping test for system camera.", mAdoptShellPerm);
for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
MockStateCallback mockListener = MockStateCallback.mock();
mCameraListener = new BlockingStateCallback(mockListener);
mCameraManager.openCamera(mCameraIdsUnderTest[i], mCameraListener, mHandler);
fail("Open camera as headless system user without permission should have thrown"
+ "security exception" + mCameraIdsUnderTest[i]);
} catch (SecurityException e) {
Log.i(TAG, "Got the SecurityException on missing permission as expected");
}
}
}
/**
* Verifies that the System Headless User continues to access the camera
* even after foreground user has switched to a different user.
* This test is only valid for devices running in Headless
* System User Mode.
*/
@EnsureHasSecondaryUser
@RequireHeadlessSystemUserMode(reason = "tests headless user behaviour")
@RequiresFlagsEnabled(Flags.FLAG_CAMERA_HSUM_PERMISSION)
@Test
public void testHeadlessSystemUser_SwitchForegroundUser() throws Exception {
assumeTrue("Skipping test for devices which doesn't support multiple users.",
UserManager.supportsMultipleUsers());
for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
CameraDevice camera = null;
try {
MockStateCallback mockListener = MockStateCallback.mock();
mCameraListener = new BlockingStateCallback(mockListener);
openCameraAsHeadlessSystemUser(mCameraIdsUnderTest[i]);
camera = CameraTestUtils.verifyCameraStateOpened(mCameraIdsUnderTest[i],
mockListener);
switchUser(UserHandle.MIN_SECONDARY_USER_ID);
verifyNoMoreInteractions(mockListener);
} finally {
if (camera != null) {
camera.close();
}
}
}
}
private void openCameraAsHeadlessSystemUser(String cameraId) throws Exception {
if (mAdoptShellPerm) {
mCameraManager.openCamera(cameraId, mCameraListener, mHandler);
} else {
try (PermissionContext permissionContext =
TestApis.permissions().withPermission(CAMERA_HEADLESS_SYSTEM_USER)) {
mCameraManager.openCamera(cameraId, mCameraListener, mHandler);
}
}
mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED,
CameraTestUtils.CAMERA_OPEN_TIMEOUT_MS);
}
private static int getCurrentUserId() {
String cmd = "am get-current-user";
String retStr = SystemUtil.runShellCommand(cmd);
Log.d(TAG, "getCurrentUserId: " + retStr);
return Integer.parseInt(retStr.trim());
}
private static void waitUntilUserCurrent(int userId) throws Exception {
PollingCheck.waitFor(SWITCH_USER_WAIT_TIME, () -> userId == getCurrentUserId());
}
private void switchUser(int userId) throws Exception {
String cmd = "am switch-user " + userId;
SystemUtil.runShellCommand(cmd);
waitUntilUserCurrent(userId);
if (mAdoptShellPerm) {
assertEquals(userId, mActivityManager.getCurrentUser());
} else {
try (PermissionContext permissionContext =
TestApis.permissions().withPermission(INTERACT_ACROSS_USERS)) {
assertEquals(userId, mActivityManager.getCurrentUser());
}
}
}
}