Binder implementation of Camera

Posted by kkobashi on Sat, 09 May 2020 17:05:58 +0200

api call

  • Get CameraManager object
CameraManager mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
  • openCamera
mCameraManager.openCamera(cameraId, callback, handler);

The openCameraDeviceUserAsync method in CameraManager is called

    private CameraDevice openCameraDeviceUserAsync(String cameraId,
            CameraDevice.StateCallback callback, Executor executor, final int uid)
            throws CameraAccessException {
... ...
                    // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
                    ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
                    if (cameraService == null) {
                        throw new ServiceSpecificException(
                            ICameraService.ERROR_DISCONNECTED,
                            "Camera service is currently unavailable");
                    }
                    cameraUser = cameraService.connectDevice(callbacks, cameraId,
                            mContext.getOpPackageName(), uid);
... ...

        return device;
    }

Icemeraservice is an IDL. Take a look at its object instantiation process. Its instantiation is in the connectCameraServiceLocked method of CameraManager's internal class CameraManagerGlobal

        private void connectCameraServiceLocked() {
            // Only reconnect if necessary
            if (mCameraService != null || sCameraServiceDisabled) return;

            Log.i(TAG, "Connecting to camera service");

            IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
            if (cameraServiceBinder == null) {
                // Camera service is now down, leave mCameraService as null
                return;
            }
            try {
                cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
            } catch (RemoteException e) {
                // Camera service is now down, leave mCameraService as null
                return;
            }

            ICameraService cameraService = ICameraService.Stub.asInterface(cameraServiceBinder);
... ...
        }

Servicemanager.getservice (camera ﹣ service ﹣ binder ﹣ name) also obtains the binder object registered by CameraService in Native through Aidl, and then we instantiate the icomeraservice object through this binder object. Let's see the implementation of asInterface method in icomeraservice.java generated by icomeraservice.aidl through compilation (file path: /android/out/soong/.intermediates/frameworks/base/framework/android_common/gen/aidl/frameworks/av/camera/aidl/android/hardware/ICameraService.java)

public static android.hardware.ICameraService asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof android.hardware.ICameraService))) {
        return ((android.hardware.ICameraService) iin);
    }
    return new android.hardware.ICameraService.Stub.Proxy(obj);
}

First, the query local interface will search the local interface according to the identifier DESCRIPTOR, and find the interface bound to the binder by calling attachInterface in the Stub constructor of the internal class of the icemeraservice

public Stub() {
    this.attachInterface(this, DESCRIPTOR);
}

If the Stub object is not instantiated, the object of the Proxy class will be returned (icemeraservice has no Stub object), and the Proxy class is also generated automatically by Aidl; therefore, the connectDevice method called is actually of the Proxy class

@Override
public android.hardware.camera2.ICameraDeviceUser connectDevice(android.hardware.camera2.ICameraDeviceCallbacks callbacks, java.lang.String cameraId, java.lang.String opPackageName, int clientUid) throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    android.hardware.camera2.ICameraDeviceUser _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        _data.writeStrongBinder((((callbacks != null)) ? (callbacks.asBinder()) : (null)));
        _data.writeString(cameraId);
        _data.writeString(opPackageName);
        _data.writeInt(clientUid);
        mRemote.transact(Stub.TRANSACTION_connectDevice, _data, _reply, 0);
        _reply.readException();
        _result = android.hardware.camera2.ICameraDeviceUser.Stub.asInterface(_reply.readStrongBinder());
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}

Call the transact method to initiate the RPC (remote procedure call) request. At the same time, the current thread is suspended. Then the server's onTransact method will be called until the RPC process returns. The current thread will continue to execute and take the returned results from the RPC process from the "reply"

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
... ...
                case TRANSACTION_connectDevice: {
                    data.enforceInterface(descriptor);
                    android.hardware.camera2.ICameraDeviceCallbacks _arg0;
                    _arg0 = android.hardware.camera2.ICameraDeviceCallbacks.Stub.asInterface(data.readStrongBinder());
                    java.lang.String _arg1;
                    _arg1 = data.readString();
                    java.lang.String _arg2;
                    _arg2 = data.readString();
                    int _arg3;
                    _arg3 = data.readInt();
                    android.hardware.camera2.ICameraDeviceUser _result = this.connectDevice(_arg0, _arg1, _arg2, _arg3);
                    reply.writeNoException();
                    reply.writeStrongBinder((((_result != null)) ? (_result.asBinder()) : (null)));
                    return true;
                }
... ...
            }
        }

Corresponding to ICameraService.java, aidl also generates ICameraService.cpp in Native, including BpCameraService (Native Proxy) and the implementation of BnCameraService. Finally, the connectDevice method of its subclass CameraService is called in the onTransact method of BnCameraService to realize the operation of openCamera

Status CameraService::connectDevice(
        const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
        const String16& cameraId,
        const String16& clientPackageName,
        int clientUid,
        /*out*/
        sp<hardware::camera2::ICameraDeviceUser>* device) {

    ATRACE_CALL();
    Status ret = Status::ok();
    String8 id = String8(cameraId);
    sp<CameraDeviceClient> client = nullptr;
    ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id,
            /*api1CameraId*/-1,
            CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName,
            clientUid, USE_CALLING_PID, API_2,
            /*legacyMode*/ false, /*shimUpdateOnly*/ false,
            /*out*/client);

    if(!ret.isOk()) {
        logRejected(id, getCallingPid(), String8(clientPackageName),
                ret.toString8());
        return ret;
    }

    *device = client;
    return ret;
}

Topics: Mobile Android Java