Merge "CameraServiceProxy: Estimate rotate&crop override value" into sc-v2-dev

This commit is contained in:
TreeHugger Robot
2021-10-20 16:08:51 +00:00
committed by Android (Google) Code Review
3 changed files with 226 additions and 92 deletions

View File

@@ -15,6 +15,7 @@
*/
package com.android.server.camera;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.os.Build.VERSION_CODES.M;
import android.annotation.IntDef;
@@ -39,7 +40,9 @@ import android.hardware.CameraSessionStats;
import android.hardware.CameraStreamStats;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceProxy;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
import android.hardware.display.DisplayManager;
@@ -346,13 +349,13 @@ public class CameraServiceProxy extends SystemService
private final TaskStateHandler mTaskStackListener = new TaskStateHandler();
private final class TaskInfo {
private int frontTaskId;
private boolean isResizeable;
private boolean isFixedOrientationLandscape;
private boolean isFixedOrientationPortrait;
private int displayId;
private int userId;
public static final class TaskInfo {
public int frontTaskId;
public boolean isResizeable;
public boolean isFixedOrientationLandscape;
public boolean isFixedOrientationPortrait;
public int displayId;
public int userId;
}
private final class TaskStateHandler extends TaskStackListener {
@@ -367,7 +370,8 @@ public class CameraServiceProxy extends SystemService
synchronized (mMapLock) {
TaskInfo info = new TaskInfo();
info.frontTaskId = taskInfo.taskId;
info.isResizeable = taskInfo.isResizeable;
info.isResizeable =
(taskInfo.topActivityInfo.resizeMode != RESIZE_MODE_UNRESIZEABLE);
info.displayId = taskInfo.displayId;
info.userId = taskInfo.userId;
info.isFixedOrientationLandscape = ActivityInfo.isFixedOrientationLandscape(
@@ -427,97 +431,108 @@ public class CameraServiceProxy extends SystemService
}
};
private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
private boolean isMOrBelow(Context ctx, String packageName) {
try {
return ctx.getPackageManager().getPackageInfo(
packageName, 0).applicationInfo.targetSdkVersion <= M;
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG,"Package name not found!");
}
return false;
private static boolean isMOrBelow(Context ctx, String packageName) {
try {
return ctx.getPackageManager().getPackageInfo(
packageName, 0).applicationInfo.targetSdkVersion <= M;
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG,"Package name not found!");
}
return false;
}
/**
* Estimate the app crop-rotate-scale compensation value.
*/
public static int getCropRotateScale(@NonNull Context ctx, @NonNull String packageName,
@Nullable TaskInfo taskInfo, int displayRotation, int lensFacing,
boolean ignoreResizableAndSdkCheck) {
if (taskInfo == null) {
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
}
/**
* Gets whether crop-rotate-scale is needed.
*/
private boolean getNeedCropRotateScale(@NonNull Context ctx, @NonNull String packageName,
@Nullable TaskInfo taskInfo, int sensorOrientation, int lensFacing,
boolean ignoreResizableAndSdkCheck) {
if (taskInfo == null) {
return false;
}
// External cameras do not need crop-rotate-scale.
if (lensFacing != CameraMetadata.LENS_FACING_FRONT
&& lensFacing != CameraMetadata.LENS_FACING_BACK) {
Log.v(TAG, "lensFacing=" + lensFacing + ". Crop-rotate-scale is disabled.");
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
}
// External cameras do not need crop-rotate-scale.
if (lensFacing != CameraMetadata.LENS_FACING_FRONT
&& lensFacing != CameraMetadata.LENS_FACING_BACK) {
Log.v(TAG, "lensFacing=" + lensFacing + ". Crop-rotate-scale is disabled.");
return false;
}
// In case the activity behavior is not explicitly overridden, enable the
// crop-rotate-scale workaround if the app targets M (or below) or is not
// resizeable.
if (!ignoreResizableAndSdkCheck && !isMOrBelow(ctx, packageName) &&
taskInfo.isResizeable) {
Slog.v(TAG,
"The activity is N or above and claims to support resizeable-activity. "
+ "Crop-rotate-scale is disabled.");
return false;
}
DisplayManager displayManager = ctx.getSystemService(DisplayManager.class);
int rotationDegree = 0;
if (displayManager != null) {
Display display = displayManager.getDisplay(taskInfo.displayId);
if (display == null) {
Slog.e(TAG, "Invalid display id: " + taskInfo.displayId);
return false;
}
int rotation = display.getRotation();
switch (rotation) {
case Surface.ROTATION_0:
rotationDegree = 0;
break;
case Surface.ROTATION_90:
rotationDegree = 90;
break;
case Surface.ROTATION_180:
rotationDegree = 180;
break;
case Surface.ROTATION_270:
rotationDegree = 270;
break;
}
} else {
Slog.e(TAG, "Failed to query display manager!");
return false;
}
// Here we only need to know whether the camera is landscape or portrait. Therefore we
// don't need to consider whether it is a front or back camera. The formula works for
// both.
boolean landscapeCamera = ((rotationDegree + sensorOrientation) % 180 == 0);
// In case the activity behavior is not explicitly overridden, enable the
// crop-rotate-scale workaround if the app targets M (or below) or is not
// resizeable.
if (!ignoreResizableAndSdkCheck && !isMOrBelow(ctx, packageName) &&
taskInfo.isResizeable) {
Slog.v(TAG,
"Display.getRotation()=" + rotationDegree
+ " CameraCharacteristics.SENSOR_ORIENTATION=" + sensorOrientation
+ " isFixedOrientationPortrait=" + taskInfo.isFixedOrientationPortrait
+ " isFixedOrientationLandscape=" +
taskInfo.isFixedOrientationLandscape);
// We need to do crop-rotate-scale when camera is landscape and activity is portrait or
// vice versa.
return (taskInfo.isFixedOrientationPortrait && landscapeCamera)
|| (taskInfo.isFixedOrientationLandscape && !landscapeCamera);
"The activity is N or above and claims to support resizeable-activity. "
+ "Crop-rotate-scale is disabled.");
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
}
if (!taskInfo.isFixedOrientationPortrait && !taskInfo.isFixedOrientationLandscape) {
Log.v(TAG, "Non-fixed orientation activity. Crop-rotate-scale is disabled.");
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
}
int rotationDegree;
switch (displayRotation) {
case Surface.ROTATION_0:
rotationDegree = 0;
break;
case Surface.ROTATION_90:
rotationDegree = 90;
break;
case Surface.ROTATION_180:
rotationDegree = 180;
break;
case Surface.ROTATION_270:
rotationDegree = 270;
break;
default:
Log.e(TAG, "Unsupported display rotation: " + displayRotation);
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
}
Slog.v(TAG,
"Display.getRotation()=" + rotationDegree
+ " isFixedOrientationPortrait=" + taskInfo.isFixedOrientationPortrait
+ " isFixedOrientationLandscape=" +
taskInfo.isFixedOrientationLandscape);
// We are trying to estimate the necessary rotation compensation for clients that
// don't handle various display orientations.
// The logic that is missing on client side is similar to the reference code
// in {@link android.hardware.Camera#setDisplayOrientation} where "info.orientation"
// is already applied in "CameraUtils::getRotationTransform".
// Care should be taken to reverse the rotation direction depending on the camera
// lens facing.
if (rotationDegree == 0) {
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
}
if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) {
// Switch direction for front facing cameras
rotationDegree = 360 - rotationDegree;
}
switch (rotationDegree) {
case 90:
return CaptureRequest.SCALER_ROTATE_AND_CROP_90;
case 270:
return CaptureRequest.SCALER_ROTATE_AND_CROP_270;
case 180:
return CaptureRequest.SCALER_ROTATE_AND_CROP_180;
case 0:
default:
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
}
}
private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
@Override
public boolean isRotateAndCropOverrideNeeded(String packageName, int sensorOrientation,
int lensFacing) {
public int getRotateAndCropOverride(String packageName, int lensFacing) {
if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
" camera service UID!");
return false;
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
}
// TODO: Modify the sensor orientation in camera characteristics along with any 3A
@@ -531,10 +546,10 @@ public class CameraServiceProxy extends SystemService
if (CompatChanges.isChangeEnabled(OVERRIDE_CAMERA_ROTATE_AND_CROP, packageName,
UserHandle.getUserHandleForUid(taskInfo.userId))) {
Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP enabled!");
return true;
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
} else {
Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP disabled!");
return false;
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
}
}
boolean ignoreResizableAndSdkCheck = false;
@@ -544,7 +559,23 @@ public class CameraServiceProxy extends SystemService
Slog.v(TAG, "OVERRIDE_CAMERA_RESIZABLE_AND_SDK_CHECK enabled!");
ignoreResizableAndSdkCheck = true;
}
return getNeedCropRotateScale(mContext, packageName, taskInfo, sensorOrientation,
DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
int displayRotation;
if (displayManager != null) {
Display display = displayManager.getDisplay(taskInfo.displayId);
if (display == null) {
Slog.e(TAG, "Invalid display id: " + taskInfo.displayId);
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
}
displayRotation = display.getRotation();
} else {
Slog.e(TAG, "Failed to query display manager!");
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
}
return getCropRotateScale(mContext, packageName, taskInfo, displayRotation,
lensFacing, ignoreResizableAndSdkCheck);
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2021 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.server.camera;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.InstrumentationRegistry;
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraMetadata;
import android.view.Display;
import android.view.Surface;
import java.util.HashMap;
@RunWith(JUnit4.class)
public class CameraServiceProxyTest {
@Test
public void testGetCropRotateScale() {
Context ctx = InstrumentationRegistry.getContext();
// Check resizeability and SDK
CameraServiceProxy.TaskInfo taskInfo = new CameraServiceProxy.TaskInfo();
taskInfo.isResizeable = true;
taskInfo.displayId = Display.DEFAULT_DISPLAY;
taskInfo.isFixedOrientationLandscape = false;
taskInfo.isFixedOrientationPortrait = true;
// Resizeable apps should be ignored
assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
Surface.ROTATION_90 , CameraCharacteristics.LENS_FACING_BACK,
/*ignoreResizableAndSdkCheck*/false)).isEqualTo(
CameraMetadata.SCALER_ROTATE_AND_CROP_NONE);
// Resizeable apps will be considered in case the ignore flag is set
assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
Surface.ROTATION_90, CameraCharacteristics.LENS_FACING_BACK,
/*ignoreResizableAndSdkCheck*/true)).isEqualTo(
CameraMetadata.SCALER_ROTATE_AND_CROP_90);
taskInfo.isResizeable = false;
// Non-resizeable apps should be considered
assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
Surface.ROTATION_90, CameraCharacteristics.LENS_FACING_BACK,
/*ignoreResizableAndSdkCheck*/false)).isEqualTo(
CameraMetadata.SCALER_ROTATE_AND_CROP_90);
// The ignore flag for non-resizeable should have no effect
assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
Surface.ROTATION_90, CameraCharacteristics.LENS_FACING_BACK,
/*ignoreResizableAndSdkCheck*/true)).isEqualTo(
CameraMetadata.SCALER_ROTATE_AND_CROP_90);
// Non-fixed orientation should be ignored
taskInfo.isFixedOrientationLandscape = false;
taskInfo.isFixedOrientationPortrait = false;
assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
Surface.ROTATION_90, CameraCharacteristics.LENS_FACING_BACK,
/*ignoreResizableAndSdkCheck*/true)).isEqualTo(
CameraMetadata.SCALER_ROTATE_AND_CROP_NONE);
// Check rotation and lens facing combinations
HashMap<Integer, Integer> backFacingMap = new HashMap<Integer, Integer>() {{
put(Surface.ROTATION_0, CameraMetadata.SCALER_ROTATE_AND_CROP_NONE);
put(Surface.ROTATION_90, CameraMetadata.SCALER_ROTATE_AND_CROP_90);
put(Surface.ROTATION_270, CameraMetadata.SCALER_ROTATE_AND_CROP_270);
put(Surface.ROTATION_180, CameraMetadata.SCALER_ROTATE_AND_CROP_180);
}};
taskInfo.isFixedOrientationPortrait = true;
backFacingMap.forEach((key, value) -> {
assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
key, CameraCharacteristics.LENS_FACING_BACK,
/*ignoreResizableAndSdkCheck*/true)).isEqualTo(value);
});
HashMap<Integer, Integer> frontFacingMap = new HashMap<Integer, Integer>() {{
put(Surface.ROTATION_0, CameraMetadata.SCALER_ROTATE_AND_CROP_NONE);
put(Surface.ROTATION_90, CameraMetadata.SCALER_ROTATE_AND_CROP_270);
put(Surface.ROTATION_270, CameraMetadata.SCALER_ROTATE_AND_CROP_90);
put(Surface.ROTATION_180, CameraMetadata.SCALER_ROTATE_AND_CROP_180);
}};
frontFacingMap.forEach((key, value) -> {
assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo,
key, CameraCharacteristics.LENS_FACING_FRONT,
/*ignoreResizableAndSdkCheck*/true)).isEqualTo(value);
});
}
}

View File

@@ -0,0 +1 @@
include platform/frameworks/av:/camera/OWNERS