Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf463ab6 authored by Emilian Peev's avatar Emilian Peev
Browse files

Camera: Connect activity resizing state to camera service

Wire up WM activity AR mismatch query to camera clients

Test: CTS continues to pass
Bug: 134631897
Change-Id: I736cead41cb758a4a62aeb0075acd60af4f72087
parent 5f5c15ce
Loading
Loading
Loading
Loading
+179 −0
Original line number Diff line number Diff line
@@ -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.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);
    }