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

Commit 4c913801 authored by Zhijun He's avatar Zhijun He
Browse files

Camera: Implement HAL1 and higher HAL API coexistence

A higher hal version device like HAL3.2 can be opened as HAL1.0 device
if HAL supports it. This only applies to camera API1.

Change-Id: I4ae9f59f4317158cc1bd7ed7726e4032cdd1fa07
parent 0d8acb2c
Loading
Loading
Loading
Loading
+129 −2
Original line number Original line Diff line number Diff line
@@ -172,6 +172,11 @@ public class Camera {
    private static final int NO_ERROR = 0;
    private static final int NO_ERROR = 0;
    private static final int EACCESS = -13;
    private static final int EACCESS = -13;
    private static final int ENODEV = -19;
    private static final int ENODEV = -19;
    private static final int EBUSY = -16;
    private static final int EINVAL = -22;
    private static final int ENOSYS = -38;
    private static final int EUSERS = -87;
    private static final int EOPNOTSUPP = -95;


    /**
    /**
     * Broadcast Action:  A new picture is taken by the camera, and the entry of
     * Broadcast Action:  A new picture is taken by the camera, and the entry of
@@ -189,6 +194,22 @@ public class Camera {
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_NEW_VIDEO = "android.hardware.action.NEW_VIDEO";
    public static final String ACTION_NEW_VIDEO = "android.hardware.action.NEW_VIDEO";


    /**
     * Camera HAL device API version 1.0
     * @hide
     */
    public static final int CAMERA_HAL_API_VERSION_1_0 = 0x100;

    /**
     * A constant meaning the normal camera connect/open will be used.
     * @hide
     */
    public static final int CAMERA_HAL_API_VERSION_NORMAL_OPEN = -2;

    /**
     * Used to indicate HAL version un-specified.
     */
    private static final int CAMERA_HAL_API_VERSION_UNSPECIFIED = -1;
    /**
    /**
     * Hardware face detection. It does not use much CPU.
     * Hardware face detection. It does not use much CPU.
     */
     */
@@ -331,6 +352,111 @@ public class Camera {
        return null;
        return null;
    }
    }


    /**
     * Creates a new Camera object to access a particular hardware camera with
     * given hal API version. If the same camera is opened by other applications
     * or the hal API version is not supported by this device, this will throw a
     * RuntimeException.
     * <p>
     * You must call {@link #release()} when you are done using the camera,
     * otherwise it will remain locked and be unavailable to other applications.
     * <p>
     * Your application should only have one Camera object active at a time for
     * a particular hardware camera.
     * <p>
     * Callbacks from other methods are delivered to the event loop of the
     * thread which called open(). If this thread has no event loop, then
     * callbacks are delivered to the main application event loop. If there is
     * no main application event loop, callbacks are not delivered.
     * <p class="caution">
     * <b>Caution:</b> On some devices, this method may take a long time to
     * complete. It is best to call this method from a worker thread (possibly
     * using {@link android.os.AsyncTask}) to avoid blocking the main
     * application UI thread.
     *
     * @param cameraId The hardware camera to access, between 0 and
     * {@link #getNumberOfCameras()}-1.
     * @param halVersion The HAL API version this camera device to be opened as. When
     * it is {@value #CAMERA_HAL_API_VERSION_NORMAL_OPEN}, the methods will be equivalent
     * to {@link #open}, but more detailed error information will be returned to managed code.
     * @return a new Camera object, connected, locked and ready for use.
     * @throws RuntimeException if opening the camera fails (for example, if the
     * camera is in use by another process or device policy manager has disabled
     * the camera).
     * @see android.app.admin.DevicePolicyManager#getCameraDisabled(android.content.ComponentName)
     *
     * @hide
     */
    public static Camera openLegacy(int cameraId, int halVersion) {
        return new Camera(cameraId, halVersion);
    }

    /**
     * Create a legacy camera object.
     *
     * @param cameraId The hardware camera to access, between 0 and
     * {@link #getNumberOfCameras()}-1.
     * @param halVersion The HAL API version this camera device to be opened as.
     */
    private Camera(int cameraId, int halVersion) {
        int err = cameraInit(cameraId, halVersion);
        if (checkInitErrors(err)) {
            switch(err) {
                case EACCESS:
                    throw new RuntimeException("Fail to connect to camera service");
                case ENODEV:
                    throw new RuntimeException("Camera initialization failed");
                case ENOSYS:
                    throw new RuntimeException("Camera initialization failed because some methods"
                            + " are not implemented");
                case EOPNOTSUPP:
                    throw new RuntimeException("Camera initialization failed because the hal"
                            + " version is not supported by this device");
                case EINVAL:
                    throw new RuntimeException("Camera initialization failed because the input"
                            + " arugments are invalid");
                case EBUSY:
                    throw new RuntimeException("Camera initialization failed because the camera"
                            + " device was already opened");
                case EUSERS:
                    throw new RuntimeException("Camera initialization failed because the max"
                            + " number of camera devices were already opened");
                default:
                    // Should never hit this.
                    throw new RuntimeException("Unknown camera error");
            }
        }
    }

    private int cameraInit(int cameraId, int halVersion) {
        // This function should be only called by Camera(int cameraId, int halVersion).
        if (halVersion < CAMERA_HAL_API_VERSION_1_0 &&
                halVersion != CAMERA_HAL_API_VERSION_NORMAL_OPEN) {
            throw new IllegalArgumentException("Invalid HAL version " + halVersion);
        }

        mShutterCallback = null;
        mRawImageCallback = null;
        mJpegCallback = null;
        mPreviewCallback = null;
        mPostviewCallback = null;
        mUsingPreviewAllocation = false;
        mZoomListener = null;

        Looper looper;
        if ((looper = Looper.myLooper()) != null) {
            mEventHandler = new EventHandler(this, looper);
        } else if ((looper = Looper.getMainLooper()) != null) {
            mEventHandler = new EventHandler(this, looper);
        } else {
            mEventHandler = null;
        }

        String packageName = ActivityThread.currentPackageName();

        return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, packageName);
    }

    Camera(int cameraId) {
    Camera(int cameraId) {
        int err = cameraInit(cameraId);
        int err = cameraInit(cameraId);
        if (checkInitErrors(err)) {
        if (checkInitErrors(err)) {
@@ -369,7 +495,8 @@ public class Camera {


        String packageName = ActivityThread.currentPackageName();
        String packageName = ActivityThread.currentPackageName();


        return native_setup(new WeakReference<Camera>(this), cameraId, packageName);
        return native_setup(new WeakReference<Camera>(this), cameraId,
                CAMERA_HAL_API_VERSION_UNSPECIFIED, packageName);
    }
    }


    /**
    /**
@@ -396,7 +523,7 @@ public class Camera {
        release();
        release();
    }
    }


    private native final int native_setup(Object camera_this, int cameraId,
    private native final int native_setup(Object camera_this, int cameraId, int halVersion,
                                           String packageName);
                                           String packageName);


    private native final void native_release();
    private native final void native_release();
+7 −0
Original line number Original line Diff line number Diff line
@@ -74,4 +74,11 @@ interface ICameraService
    int getLegacyParameters(int cameraId, out String[] parameters);
    int getLegacyParameters(int cameraId, out String[] parameters);
    // Determines if a particular API version is supported; see ICameraService.h for version defines
    // Determines if a particular API version is supported; see ICameraService.h for version defines
    int supportsCameraApi(int cameraId, int apiVersion);
    int supportsCameraApi(int cameraId, int apiVersion);

    int connectLegacy(ICameraClient client, int cameraId,
                    int halVersion,
                    String clientPackageName,
                    int clientUid,
                    // Container for an ICamera object
                    out BinderHolder device);
}
}
+14 −5
Original line number Original line Diff line number Diff line
@@ -466,7 +466,7 @@ static void android_hardware_Camera_getCameraInfo(JNIEnv *env, jobject thiz,


// connect to camera service
// connect to camera service
static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
    jobject weak_this, jint cameraId, jstring clientPackageName)
    jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)
{
{
    // Convert jstring to String16
    // Convert jstring to String16
    const char16_t *rawClientName = env->GetStringChars(clientPackageName, NULL);
    const char16_t *rawClientName = env->GetStringChars(clientPackageName, NULL);
@@ -474,8 +474,18 @@ static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
    String16 clientName(rawClientName, rawClientNameLen);
    String16 clientName(rawClientName, rawClientNameLen);
    env->ReleaseStringChars(clientPackageName, rawClientName);
    env->ReleaseStringChars(clientPackageName, rawClientName);


    sp<Camera> camera = Camera::connect(cameraId, clientName,
    sp<Camera> camera;
    if (halVersion == ICameraService::CAMERA_HAL_API_VERSION_UNSPECIFIED) {
        // Default path: hal version is unspecified, do normal camera open.
        camera = Camera::connect(cameraId, clientName,
                Camera::USE_CALLING_UID);
                Camera::USE_CALLING_UID);
    } else {
        jint status = Camera::connectLegacy(cameraId, halVersion, clientName,
                Camera::USE_CALLING_UID, camera);
        if (status != NO_ERROR) {
            return status;
        }
    }


    if (camera == NULL) {
    if (camera == NULL) {
        return -EACCES;
        return -EACCES;
@@ -510,7 +520,6 @@ static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
// finalizer is invoked later.
// finalizer is invoked later.
static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
{
{
    // TODO: Change to ALOGV
    ALOGV("release camera");
    ALOGV("release camera");
    JNICameraContext* context = NULL;
    JNICameraContext* context = NULL;
    sp<Camera> camera;
    sp<Camera> camera;
@@ -891,7 +900,7 @@ static JNINativeMethod camMethods[] = {
    "(ILandroid/hardware/Camera$CameraInfo;)V",
    "(ILandroid/hardware/Camera$CameraInfo;)V",
    (void*)android_hardware_Camera_getCameraInfo },
    (void*)android_hardware_Camera_getCameraInfo },
  { "native_setup",
  { "native_setup",
    "(Ljava/lang/Object;ILjava/lang/String;)I",
    "(Ljava/lang/Object;IILjava/lang/String;)I",
    (void*)android_hardware_Camera_native_setup },
    (void*)android_hardware_Camera_native_setup },
  { "native_release",
  { "native_release",
    "()V",
    "()V",
+31 −0
Original line number Original line Diff line number Diff line
@@ -205,6 +205,37 @@ public class CameraBinderTest extends AndroidTestCase {
        }
        }
    }
    }


    @SmallTest
    public void testConnectLegacy() throws Exception {
        final int CAMERA_HAL_API_VERSION_1_0 = 0x100;
        for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
            ICamera cameraUser = null;
            ICameraClient dummyCallbacks = new DummyCameraClient();

            String clientPackageName = getContext().getPackageName();

            BinderHolder holder = new BinderHolder();

            try {
                CameraBinderDecorator.newInstance(mUtils.getCameraService())
                        .connectLegacy(dummyCallbacks, cameraId, CAMERA_HAL_API_VERSION_1_0,
                        clientPackageName,
                        CameraBinderTestUtils.USE_CALLING_UID, holder);
                cameraUser = ICamera.Stub.asInterface(holder.getBinder());
                assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);

                Log.v(TAG, String.format("Camera %s connected as HAL1 legacy device", cameraId));
            } catch (RuntimeException e) {
                // Not all camera device support openLegacy.
                Log.i(TAG, "Unable to open camera as HAL1 legacy camera device " + e);
            } finally {
                if (cameraUser != null) {
                    cameraUser.disconnect();
                }
            }
        }
    }

    static class DummyCameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
    static class DummyCameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {


        /*
        /*
+53 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2013 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.mediaframeworktest.unit;

import android.hardware.Camera;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;

/**
 * <pre>
 * adb shell am instrument \
 *      -e class 'com.android.mediaframeworktest.unit.CameraOpenTest' \
 *      -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
 * </pre>
 */
public class CameraOpenTest extends junit.framework.TestCase {
    private static String TAG = "CameraOpenTest";

    private Camera mCamera;

    /**
     * Test @hide android.hardware.Camera#openLegacy API that cannot be tested in CTS.
     */
    @SmallTest
    public void testOpenLegacy() {
        int nCameras = Camera.getNumberOfCameras();
        for (int id = 0; id < nCameras; id++) {
            try {
                mCamera.openLegacy(id, Camera.CAMERA_HAL_API_VERSION_1_0);
            } catch (RuntimeException e) {
                Log.i(TAG, "Unable to open camera as HAL1 legacy camera device " + e);
            } finally {
                if (mCamera != null) {
                    mCamera.release();
                }
            }
        }
    }
}