Merge "Camera: Connect activity resizing state to camera service" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
cd5c6bc667
@@ -15,17 +15,33 @@
|
||||
*/
|
||||
package com.android.server.camera;
|
||||
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
import static android.os.Build.VERSION_CODES.M;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.TaskStackListener;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.CameraSessionStats;
|
||||
import android.hardware.CameraStreamStats;
|
||||
import android.hardware.ICameraService;
|
||||
import android.hardware.ICameraServiceProxy;
|
||||
import android.hardware.camera2.CameraMetadata;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.media.AudioManager;
|
||||
import android.metrics.LogMaker;
|
||||
import android.nfc.INfcAdapter;
|
||||
@@ -41,7 +57,10 @@ import android.os.UserManager;
|
||||
import android.stats.camera.nano.CameraProtos.CameraStreamProto;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.view.Display;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.framework.protobuf.nano.MessageNano;
|
||||
@@ -61,6 +80,7 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -203,6 +223,63 @@ 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 final class TaskStateHandler extends TaskStackListener {
|
||||
private final Object mMapLock = new Object();
|
||||
|
||||
// maps the current top level task id to its corresponding package name
|
||||
@GuardedBy("mMapLock")
|
||||
private final ArrayMap<String, TaskInfo> mTaskInfoMap = new ArrayMap<>();
|
||||
|
||||
@Override
|
||||
public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
|
||||
throws RemoteException {
|
||||
synchronized (mMapLock) {
|
||||
TaskInfo info = new TaskInfo();
|
||||
info.frontTaskId = taskInfo.taskId;
|
||||
info.isResizeable = taskInfo.isResizeable;
|
||||
info.displayId = taskInfo.displayId;
|
||||
info.isFixedOrientationLandscape = ActivityInfo.isFixedOrientationLandscape(
|
||||
taskInfo.topActivityInfo.screenOrientation);
|
||||
info.isFixedOrientationPortrait = ActivityInfo.isFixedOrientationPortrait(
|
||||
taskInfo.topActivityInfo.screenOrientation);
|
||||
mTaskInfoMap.put(taskInfo.topActivityInfo.packageName, info);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskRemoved(int taskId) throws RemoteException {
|
||||
synchronized (mMapLock) {
|
||||
for (Map.Entry<String, TaskInfo> entry : mTaskInfoMap.entrySet()){
|
||||
if (entry.getValue().frontTaskId == taskId) {
|
||||
mTaskInfoMap.remove(entry.getKey());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable TaskInfo getFrontTaskInfo(String packageName) {
|
||||
synchronized (mMapLock) {
|
||||
if (mTaskInfoMap.containsKey(packageName)) {
|
||||
return mTaskInfoMap.get(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
Log.e(TAG, "Top task with package name: " + packageName + " not found!");
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@@ -229,6 +306,102 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether crop-rotate-scale is needed.
|
||||
*/
|
||||
private boolean getNeedCropRotateScale(Context ctx, String packageName,
|
||||
@Nullable TaskInfo taskInfo, int sensorOrientation, int lensFacing) {
|
||||
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 false;
|
||||
}
|
||||
|
||||
// Only enable the crop-rotate-scale workaround if the app targets M or below and is not
|
||||
// resizeable.
|
||||
if ((ctx != null) && !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);
|
||||
Display display = displayManager.getDisplay(taskInfo.displayId);
|
||||
int rotation = display.getRotation();
|
||||
int rotationDegree = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
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.
|
||||
if ((taskInfo.isFixedOrientationPortrait && landscapeCamera)
|
||||
|| (taskInfo.isFixedOrientationLandscape && !landscapeCamera)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRotateAndCropOverrideNeeded(String packageName, int sensorOrientation,
|
||||
int lensFacing) {
|
||||
if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
|
||||
Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " +
|
||||
" camera service UID!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// A few remaining todos:
|
||||
// 1) Do the same check when working in WM compatible mode. The sequence needs
|
||||
// to be adjusted and use orientation events as triggers for all active camera
|
||||
// clients.
|
||||
// 2) Modify the sensor orientation in camera characteristics along with any 3A regions
|
||||
// in capture requests/results to account for thea physical rotation. The former
|
||||
// is somewhat tricky as it assumes that camera clients always check for the current
|
||||
// value by retrieving the camera characteristics from the camera device.
|
||||
return getNeedCropRotateScale(mContext, packageName,
|
||||
mTaskStackListener.getFrontTaskInfo(packageName), sensorOrientation,
|
||||
lensFacing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pingForUserUpdate() {
|
||||
if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
|
||||
@@ -350,6 +523,12 @@ public class CameraServiceProxy extends SystemService
|
||||
publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
|
||||
publishLocalService(CameraServiceProxy.class, this);
|
||||
|
||||
try {
|
||||
ActivityTaskManager.getService().registerTaskStackListener(mTaskStackListener);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to register task stack listener!");
|
||||
}
|
||||
|
||||
CameraStatsJobService.schedule(mContext);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user