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

Commit 4961bc88 authored by Igor Murashkin's avatar Igor Murashkin
Browse files

camera2: Map camera characteristics in the managed layer

Change-Id: Ic86c8df3d703e7cf89caa856387e2c0a1b977401
parent 5834ee75
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -69,4 +69,9 @@ interface ICameraService
     * well-formatted in the generated java method.
     */
    int getCameraVendorTagDescriptor(out BinderHolder desc);

    // Writes the camera1 parameters into a single-element array.
    int getLegacyParameters(int cameraId, out String[] parameters);
    // Determines if a particular API version is supported; see ICameraService.h for version defines
    int supportsCameraApi(int cameraId, int apiVersion);
}
+95 −9
Original line number Diff line number Diff line
@@ -19,8 +19,10 @@ package android.hardware.camera2;
import android.content.Context;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
import android.hardware.CameraInfo;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.legacy.CameraDeviceUserShim;
import android.hardware.camera2.legacy.LegacyMetadataMapper;
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
import android.hardware.camera2.utils.BinderHolder;
@@ -57,6 +59,10 @@ public final class CameraManager {
    private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
    private static final int USE_CALLING_UID = -1;

    @SuppressWarnings("unused")
    private static final int API_VERSION_1 = 1;
    private static final int API_VERSION_2 = 2;

    private final ICameraService mCameraService;
    private ArrayList<String> mDeviceIdList;

@@ -142,6 +148,9 @@ public final class CameraManager {

        synchronized (mLock) {
            mListenerMap.put(listener, handler);

            // TODO: fire the current oldest known state when adding a new listener
            //    (must be done while holding lock)
        }
    }

@@ -185,17 +194,47 @@ public final class CameraManager {
            }
        }

        int id = Integer.valueOf(cameraId);

        /*
         * Get the camera characteristics from the camera service directly if it supports it,
         * otherwise get them from the legacy shim instead.
         */

        if (!supportsCamera2Api(cameraId)) {
            // Legacy backwards compatibility path; build static info from the camera parameters
            String[] outParameters = new String[1];
            try {
                mCameraService.getLegacyParameters(id, /*out*/outParameters);
                String parameters = outParameters[0];

                CameraInfo info = new CameraInfo();
                mCameraService.getCameraInfo(id, /*out*/info);

                return LegacyMetadataMapper.createCharacteristics(parameters, info);
            } catch (RemoteException e) {
                // Impossible
                return null;
            } catch (CameraRuntimeException e) {
                throw e.asChecked();
            }

        } else {
            // Normal path: Get the camera characteristics directly from the camera service
            CameraMetadataNative info = new CameraMetadataNative();

            try {
            mCameraService.getCameraCharacteristics(Integer.valueOf(cameraId), info);
                mCameraService.getCameraCharacteristics(id, info);
            } catch(CameraRuntimeException e) {
                throw e.asChecked();
            } catch(RemoteException e) {
                // impossible
                return null;
            }

            return new CameraCharacteristics(info);
        }
    }

    /**
     * Helper for openning a connection to a camera with the given ID.
@@ -456,6 +495,53 @@ public final class CameraManager {
        }
    }

    /**
     * Queries the camera service if it supports the camera2 api directly, or needs a shim.
     *
     * @param cameraId a non-{@code null} camera identifier
     * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise.
     */
    private boolean supportsCamera2Api(String cameraId) {
        return supportsCameraApi(cameraId, API_VERSION_2);
    }

    /**
     * Queries the camera service if it supports a camera api directly, or needs a shim.
     *
     * @param cameraId a non-{@code null} camera identifier
     * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2}
     * @return {@code true} if connecting will work for that device version.
     */
    private boolean supportsCameraApi(String cameraId, int apiVersion) {
        int id = Integer.parseInt(cameraId);

        /*
         * Possible return values:
         * - NO_ERROR => Camera2 API is supported
         * - CAMERA_DEPRECATED_HAL => Camera2 API is *not* supported (thrown as an exception)
         *
         * Anything else is an unexpected error we don't want to recover from.
         */

        try {
            int res = mCameraService.supportsCameraApi(id, apiVersion);

            if (res != CameraBinderDecorator.NO_ERROR) {
                throw new AssertionError("Unexpected value " + res);
            }

            return true;
        } catch (CameraRuntimeException e) {
            if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
                return false;
            } else {
                throw e;
            }
        } catch (RemoteException e) {
            throw new AssertionError("Camera service unreachable", e);
        }
    }

    // TODO: this class needs unit tests
    // TODO: extract class into top level
    private class CameraServiceListener extends ICameraServiceListener.Stub {
+139 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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.hardware.camera2.legacy;

import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Size;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.params.StreamConfigurationDuration;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

import static com.android.internal.util.Preconditions.*;
import static android.hardware.camera2.CameraCharacteristics.*;

/**
 * Provide legacy-specific implementations of camera2 metadata for legacy devices, such as the
 * camera characteristics.
 */
public class LegacyMetadataMapper {
    private static final String TAG = "LegacyMetadataMapper";
    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);

    // from graphics.h
    private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
    private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;

    /**
     * Create characteristics for a legacy device by mapping the {@code parameters}
     * and {@code info}
     *
     * @param parameters A string parseable by {@link Camera.Parameters#unflatten}
     * @param info Camera info with camera facing direction and angle of orientation
     * @return static camera characteristics for a camera device
     *
     * @throws NullPointerException if any of the args were {@code null}
     */
    public static CameraCharacteristics createCharacteristics(String parameters,
            android.hardware.CameraInfo info) {
        checkNotNull(parameters, "parameters must not be null");
        checkNotNull(info, "info must not be null");
        checkNotNull(info.info, "info.info must not be null");

        CameraMetadataNative m = new CameraMetadataNative();

        mapCameraInfo(m, info.info);

        Camera.Parameters params = Camera.getEmptyParameters();
        params.unflatten(parameters);
        mapCameraParameters(m, params);

        if (VERBOSE) {
            Log.v(TAG, "createCharacteristics metadata:");
            Log.v(TAG, "--------------------------------------------------- (start)");
            m.dumpToLog();
            Log.v(TAG, "--------------------------------------------------- (end)");
        }

        return new CameraCharacteristics(m);
    }

    private static void mapCameraInfo(CameraMetadataNative m, CameraInfo i) {
        m.set(LENS_FACING, i.facing == CameraInfo.CAMERA_FACING_BACK ?
                LENS_FACING_BACK : LENS_FACING_FRONT);
        m.set(SENSOR_ORIENTATION, i.orientation);
    }

    private static void mapCameraParameters(CameraMetadataNative m, Camera.Parameters p) {
        mapStreamConfigs(m, p);

        // TODO: map other fields
    }

    private static void mapStreamConfigs(CameraMetadataNative m, Camera.Parameters p) {
        // TODO: set non-empty durations
        m.set(SCALER_AVAILABLE_MIN_FRAME_DURATIONS, new StreamConfigurationDuration[] {} );
        m.set(SCALER_AVAILABLE_STALL_DURATIONS, new StreamConfigurationDuration[] {} );

        ArrayList<StreamConfiguration> availableStreamConfigs = new ArrayList<>();
        /*
         * Implementation-defined (preview, recording, etc) -> use camera1 preview sizes
         * YUV_420_888 cpu callbacks -> use camera1 preview sizes
         * Other preview callbacks (CPU) -> use camera1 preview sizes
         * JPEG still capture -> use camera1 still capture sizes
         *
         * Use platform-internal format constants here, since StreamConfigurationMap does the
         * remapping to public format constants.
         */
        List<Size> previewSizes = p.getSupportedPreviewSizes();
        appendStreamConfig(availableStreamConfigs,
                HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, previewSizes);
        appendStreamConfig(availableStreamConfigs,
                ImageFormat.YUV_420_888, previewSizes);
        for (int format : p.getSupportedPreviewFormats()) {
            if (ImageFormat.isPublicFormat(format)) {
                appendStreamConfig(availableStreamConfigs, format, previewSizes);
            } else {
                /*
                 *  Do not add any formats unknown to us
                 * (since it would fail runtime checks in StreamConfigurationMap)
                 */
                Log.w(TAG,
                        String.format("mapStreamConfigs - Skipping non-public format %x", format));
            }
        }
        appendStreamConfig(availableStreamConfigs,
                HAL_PIXEL_FORMAT_BLOB, p.getSupportedPictureSizes());
        m.set(SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
                availableStreamConfigs.toArray(new StreamConfiguration[0]));
    }

    private static void appendStreamConfig(
            ArrayList<StreamConfiguration> configs, int format, List<Camera.Size> sizes) {
        for (Camera.Size size : sizes) {
            StreamConfiguration config =
                    new StreamConfiguration(format, size.width, size.height, /*input*/false);
            configs.add(config);
        }
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -47,6 +47,8 @@ public class CameraBinderDecorator {
     * - POLICY_PROHIBITS
     * - RESOURCE_BUSY
     * - NO_SUCH_DEVICE
     * - NOT_SUPPORTED
     * - TOO_MANY_USERS
     */
    public static final int EACCES = -13;
    public static final int EBUSY = -16;
+60 −1
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import android.hardware.ICameraClient;
import android.hardware.ICameraServiceListener;
import android.hardware.IProCameraCallbacks;
import android.hardware.IProCameraUser;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.impl.CameraMetadataNative;
@@ -51,8 +50,14 @@ import android.util.Log;
 * </pre>
 */
public class CameraBinderTest extends AndroidTestCase {
    private static final int MAX_PARAMETERS_LENGTH = 100;

    static String TAG = "CameraBinderTest";

    // From ICameraService.h
    private static final int API_VERSION_1 = 1;
    private static final int API_VERSION_2 = 2;

    protected CameraBinderTestUtils mUtils;

    public CameraBinderTest() {
@@ -95,6 +100,56 @@ public class CameraBinderTest extends AndroidTestCase {
        }
    }

    @SmallTest
    public void testGetLegacyParameters() throws Exception {
        for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {

            String[] parameters = new String[1];
            assertEquals("Camera service returned parameters for camera " + cameraId,
                    CameraBinderTestUtils.NO_ERROR,
                    mUtils.getCameraService().getLegacyParameters(cameraId, /*out*/parameters));
            assertNotNull(parameters[0]);
            assertTrue("Parameters should have at least one character in it",
                    parameters[0].length() > 0);

            int end = parameters[0].length();
            if (end > MAX_PARAMETERS_LENGTH) {
                end = MAX_PARAMETERS_LENGTH;
            }

            Log.v(TAG, "Camera " + cameraId + " parameters: " + parameters[0].substring(0, end));
        }
    }

    /** The camera2 api is only supported on HAL3.2+ devices */
    @SmallTest
    public void testSupportsCamera2Api() throws Exception {
        for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {

            int res = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_2);

            if (res != CameraBinderTestUtils.NO_ERROR && res != CameraBinderTestUtils.EOPNOTSUPP) {
                fail("Camera service returned bad value when queried if it supports camera2 api: "
                        + res + " for camera ID " + cameraId);
            }

            boolean supports = res == CameraBinderTestUtils.NO_ERROR;
            Log.v(TAG, "Camera " + cameraId + " supports api2: " + supports);
        }
    }

    /** The camera1 api is supported on *all* devices regardless of HAL version */
    @SmallTest
    public void testSupportsCamera1Api() throws Exception {
        for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {

            int res = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_1);
            assertEquals(
                    "Camera service returned bad value when queried if it supports camera1 api: "
                    + res + " for camera ID " + cameraId, CameraBinderTestUtils.NO_ERROR, res);
        }
    }

    static abstract class DummyBase extends Binder implements android.os.IInterface {
        @Override
        public IBinder asBinder() {
@@ -158,6 +213,7 @@ public class CameraBinderTest extends AndroidTestCase {
         * android.hardware.camera2.ICameraDeviceCallbacks#onCameraError(int,
         * android.hardware.camera2.CaptureResultExtras)
         */
        @Override
        public void onCameraError(int errorCode, CaptureResultExtras resultExtras)
                throws RemoteException {
            // TODO Auto-generated method stub
@@ -170,6 +226,7 @@ public class CameraBinderTest extends AndroidTestCase {
         * android.hardware.camera2.ICameraDeviceCallbacks#onCaptureStarted(
         * android.hardware.camera2.CaptureResultExtras, long)
         */
        @Override
        public void onCaptureStarted(CaptureResultExtras resultExtras, long timestamp)
                throws RemoteException {
            // TODO Auto-generated method stub
@@ -183,6 +240,7 @@ public class CameraBinderTest extends AndroidTestCase {
         * android.hardware.camera2.impl.CameraMetadataNative,
         * android.hardware.camera2.CaptureResultExtras)
         */
        @Override
        public void onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras)
                throws RemoteException {
            // TODO Auto-generated method stub
@@ -193,6 +251,7 @@ public class CameraBinderTest extends AndroidTestCase {
         * (non-Javadoc)
         * @see android.hardware.camera2.ICameraDeviceCallbacks#onCameraIdle()
         */
        @Override
        public void onCameraIdle() throws RemoteException {
            // TODO Auto-generated method stub

Loading