Android 8.0系統原始碼分析--openCamera啟動過程原始碼分析
說起Android相機的東西,從應用層的角度來看,基本就是四個重要的節點了:openCamera、createCaptureSession、preview、capture,最複雜的就是preview了,要理解preview,那麼就要求大家對Android的View顯示系統有一定的理解,才能更好的理解相機的預覽。相機的預覽其實就是使用預覽區的SurfaceView對應的surface建立一條預覽流,然後framework從預覽surface當中獲取到顯示buffer,這裡用於顯示的buffer會根據數量來獲取,華為手機的相機framework+HAL兩部分一般總共需要7個buffer,每個buffer都對應預覽區的一屏的大小,它就是HAL、演算法各層填充完畢後,要交給SurfaceFlinger用於顯示的預覽區大小的所有畫素點的byte陣列,這7個buffer每次在CameraServer程序獲取一個,然後通過HIDL下發給CameraDaemon程序,交給演算法、HAL層進行著色渲染,完成後再通過CameraServer程序交給SurfaceFlinger,最後顯示在螢幕上,這樣不斷的輪轉,我們就看到了預覽區會不斷的變動,這裡的buffer輪轉也就是相機最核心的部分了。我們後期的部落格具體在講關於buffer輪轉的知識。
這節我們先來說說從應用層呼叫openCamera之後的執行邏輯。openCamera的方法實現是在frameworks\base\core\java\android\hardware\camera2\CameraManager.java類中完成的,CameraManager和我們應用中經常使用的WindowManager、PackageManager一樣,都可以通過context上下文獲取到,它也是在frameworks\base\core\java\android\app\SystemServiceRegistry.java類中通過Context.CAMERA_SERVICE鍵值註冊的,原始碼如下:
我們拿到CameraManager類的物件之後,就可以呼叫它的openCamera方法來開啟相機了,CameraManager類的openCamera方法的原始碼如下:
@RequiresPermission(android.Manifest.permission.CAMERA)
public void openCamera(@NonNull String cameraId,
@NonNull
throws CameraAccessException {
openCameraForUid(cameraId, callback, handler, USE_CALLING_UID);
}
這個方法的實現很簡單,就是呼叫openCameraForUid來進一步處理,我們先來看看呼叫該方法需要傳遞的引數,第一個表示要開啟的目標Camera的id,華為手機上該值一般有兩個:0和1,0表示後攝,當然也是主攝,1表示前攝,我們怎麼知道該值的取值呢?可以通過呼叫CameraManager類的getCameraIdList()方法來獲取,該方法會將當前已經註冊成功的camera硬體對應的id列表返回給我們應用層,硬體註冊都是驅動層的東西了,那一步離我們現在的階段還很遠。我們再來看一下第二個引數CameraDevice.StateCallback,它是定義在frameworks\base\core\java\android\hardware\camera2\CameraDevice.java類中的一個內部類,StateCallback類的定義原始碼如下:
public static abstract class StateCallback {
/**
* An error code that can be reported by {@link #onError}
* indicating that the camera device is in use already.
*
* <p>
* This error can be produced when opening the camera fails due to the camera
* being used by a higher-priority camera API client.
* </p>
*
* @see #onError
*/
public static final int ERROR_CAMERA_IN_USE = 1;
/**
* An error code that can be reported by {@link #onError}
* indicating that the camera device could not be opened
* because there are too many other open camera devices.
*
* <p>
* The system-wide limit for number of open cameras has been reached,
* and more camera devices cannot be opened until previous instances are
* closed.
* </p>
*
* <p>
* This error can be produced when opening the camera fails.
* </p>
*
* @see #onError
*/
public static final int ERROR_MAX_CAMERAS_IN_USE = 2;
/**
* An error code that can be reported by {@link #onError}
* indicating that the camera device could not be opened due to a device
* policy.
*
* @see android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName, boolean)
* @see #onError
*/
public static final int ERROR_CAMERA_DISABLED = 3;
/**
* An error code that can be reported by {@link #onError}
* indicating that the camera device has encountered a fatal error.
*
* <p>The camera device needs to be re-opened to be used again.</p>
*
* @see #onError
*/
public static final int ERROR_CAMERA_DEVICE = 4;
/**
* An error code that can be reported by {@link #onError}
* indicating that the camera service has encountered a fatal error.
*
* <p>The Android device may need to be shut down and restarted to restore
* camera function, or there may be a persistent hardware problem.</p>
*
* <p>An attempt at recovery <i>may</i> be possible by closing the
* CameraDevice and the CameraManager, and trying to acquire all resources
* again from scratch.</p>
*
* @see #onError
*/
public static final int ERROR_CAMERA_SERVICE = 5;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"ERROR_"}, value =
{ERROR_CAMERA_IN_USE,
ERROR_MAX_CAMERAS_IN_USE,
ERROR_CAMERA_DISABLED,
ERROR_CAMERA_DEVICE,
ERROR_CAMERA_SERVICE })
public @interface ErrorCode {};
/**
* The method called when a camera device has finished opening.
*
* <p>At this point, the camera device is ready to use, and
* {@link CameraDevice#createCaptureSession} can be called to set up the first capture
* session.</p>
*
* @param camera the camera device that has become opened
*/
public abstract void onOpened(@NonNull CameraDevice camera); // Must implement
/**
* The method called when a camera device has been closed with
* {@link CameraDevice#close}.
*
* <p>Any attempt to call methods on this CameraDevice in the
* future will throw a {@link IllegalStateException}.</p>
*
* <p>The default implementation of this method does nothing.</p>
*
* @param camera the camera device that has become closed
*/
public void onClosed(@NonNull CameraDevice camera) {
// Default empty implementation
}
/**
* The method called when a camera device is no longer available for
* use.
*
* <p>This callback may be called instead of {@link #onOpened}
* if opening the camera fails.</p>
*
* <p>Any attempt to call methods on this CameraDevice will throw a
* {@link CameraAccessException}. The disconnection could be due to a
* change in security policy or permissions; the physical disconnection
* of a removable camera device; or the camera being needed for a
* higher-priority camera API client.</p>
*
* <p>There may still be capture callbacks that are invoked
* after this method is called, or new image buffers that are delivered
* to active outputs.</p>
*
* <p>The default implementation logs a notice to the system log
* about the disconnection.</p>
*
* <p>You should clean up the camera with {@link CameraDevice#close} after
* this happens, as it is not recoverable until the camera can be opened
* again. For most use cases, this will be when the camera again becomes
* {@link CameraManager.AvailabilityCallback#onCameraAvailable available}.
* </p>
*
* @param camera the device that has been disconnected
*/
public abstract void onDisconnected(@NonNull CameraDevice camera); // Must implement
/**
* The method called when a camera device has encountered a serious error.
*
* <p>This callback may be called instead of {@link #onOpened}
* if opening the camera fails.</p>
*
* <p>This indicates a failure of the camera device or camera service in
* some way. Any attempt to call methods on this CameraDevice in the
* future will throw a {@link CameraAccessException} with the
* {@link CameraAccessException#CAMERA_ERROR CAMERA_ERROR} reason.
* </p>
*
* <p>There may still be capture completion or camera stream callbacks
* that will be called after this error is received.</p>
*
* <p>You should clean up the camera with {@link CameraDevice#close} after
* this happens. Further attempts at recovery are error-code specific.</p>
*
* @param camera The device reporting the error
* @param error The error code.
*
* @see #ERROR_CAMERA_IN_USE
* @see #ERROR_MAX_CAMERAS_IN_USE
* @see #ERROR_CAMERA_DISABLED
* @see #ERROR_CAMERA_DEVICE
* @see #ERROR_CAMERA_SERVICE
*/
public abstract void onError(@NonNull CameraDevice camera,
@ErrorCode int error); // Must implement
}
我們從這個類所定義的方法就能夠非常清楚的看到,它這幾個回撥的意圖了:onOpened就是成功開啟camera之後的回撥,而且它會返回一個CameraDevice camera物件給我們應用層,基本上操作相機所有重要的工作都是由它來中轉實現的,所以應用層拿到這個物件之後,就可以使用它作很多其他的工作了,在接下來的分析過程中,我們也會看到,這個物件在framework中是怎麼構建好,然後又是怎麼回傳給我們應用層的;onClosed方法就是當相機關閉時的回調了;onDisconnected方法就是相機斷開連線時的回撥;onError方法就是相機出錯時的回調了。
我們再來看看最後一個引數Handler handler,為什麼要傳一個Handler進來呢?它的目的就是為了保證執行緒不切換,假如我們在應用層在工作執行緒B中執行openCamera的方法,同時將執行緒B對應的Handler物件傳進來,那麼開啟成功之後,framework為了保證執行緒同步,也會使用該handler物件,將訊息傳送到執行緒B的Looper迴圈上,這就是傳入一個Handler物件的原因了,我們在後面的分析過程中會看到它的使用。
好,引數分析完了,我們繼續來看一下openCameraForUid方法的實現,原始碼如下:
public void openCameraForUid(@NonNull String cameraId,
@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler,
int clientUid)
throws CameraAccessException {
if (cameraId == null) {
throw new IllegalArgumentException("cameraId was null");
} else if (callback == null) {
throw new IllegalArgumentException("callback was null");
} else if (handler == null) {
if (Looper.myLooper() != null) {
handler = new Handler();
} else {
throw new IllegalArgumentException(
"Handler argument is null, but no looper exists in the calling thread");
}
}
openCameraDeviceUserAsync(cameraId, callback, handler, clientUid);
}
首先還是引數判斷,我們呼叫方法時傳入的引數必須合法,否則直接丟擲異常,引數合法之後,再呼叫openCameraDeviceUserAsync來進一步執行camera的開啟工作,原始碼如下:
private CameraDevice openCameraDeviceUserAsync(String cameraId,
CameraDevice.StateCallback callback, Handler handler, final int uid)
throws CameraAccessException {
CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
CameraDevice device = null;
synchronized (mLock) {
ICameraDeviceUser cameraUser = null;
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
new android.hardware.camera2.impl.CameraDeviceImpl(
cameraId,
callback,
handler,
characteristics,
mContext.getApplicationInfo().targetSdkVersion);
ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
try {
if (supportsCamera2ApiLocked(cameraId)) {
// 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);
} else {
// Use legacy camera implementation for HAL1 devices
int id;
try {
id = Integer.parseInt(cameraId);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
+ cameraId);
}
Log.i(TAG, "Using legacy camera HAL.");
cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
}
} catch (ServiceSpecificException e) {
if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) {
throw new AssertionError("Should've gone down the shim path");
} else if (e.errorCode == ICameraService.ERROR_CAMERA_IN_USE ||
e.errorCode == ICameraService.ERROR_MAX_CAMERAS_IN_USE ||
e.errorCode == ICameraService.ERROR_DISABLED ||
e.errorCode == ICameraService.ERROR_DISCONNECTED ||
e.errorCode == ICameraService.ERROR_INVALID_OPERATION) {
// Received one of the known connection errors
// The remote camera device cannot be connected to, so
// set the local camera to the startup error state
deviceImpl.setRemoteFailure(e);
if (e.errorCode == ICameraService.ERROR_DISABLED ||
e.errorCode == ICameraService.ERROR_DISCONNECTED ||
e.errorCode == ICameraService.ERROR_CAMERA_IN_USE) {
// Per API docs, these failures call onError and throw
throwAsPublicException(e);
}
} else {
// Unexpected failure - rethrow
throwAsPublicException(e);
}
} catch (RemoteException e) {
// Camera service died - act as if it's a CAMERA_DISCONNECTED case
ServiceSpecificException sse = new ServiceSpecificException(
ICameraService.ERROR_DISCONNECTED,
"Camera service is currently unavailable");
deviceImpl.setRemoteFailure(sse);
throwAsPublicException(sse);
}
// TODO: factor out callback to be non-nested, then move setter to constructor
// For now, calling setRemoteDevice will fire initial
// onOpened/onUnconfigured callbacks.
// This function call may post onDisconnected and throw CAMERA_DISCONNECTED if
// cameraUser dies during setup.
deviceImpl.setRemoteDevice(cameraUser);
device = deviceImpl;
}
return device;
}
該方法中首先呼叫getCameraCharacteristics(cameraId)獲取到目標camera的特性引數,這個方法的實現就是在我們前面提到的CameraServer程序當中,因為手機開機時,camera驅動的註冊就開始了,註冊完成之後,所有camera硬體裝置的特性引數都已經儲存在CameraServer程序當中了,我們這裡只需要獲取就可以了,大家如果想了解,可以繼續往下追查。接下來使用我們傳入的引數構造一個CameraDeviceImpl物件,它其實也就是最終要通過StateCallback回撥介面的onOpened方法返回給我們應用層的那個引數物件了,只不過這裡剛構建好,它裡邊最核心的一個成員變數還沒有賦值,就和Surface一樣,我們雖然可以new Surface創造一個Surface物件,但是它在native層還要執行很多的初始化邏輯,還有它所對應的buffer空間和native層的指標沒有初始化,那麼這個Surface只是個殼子,沒有什麼實際的用處。我們來看一下frameworks\base\core\java\android\hardware\camera2\impl\CameraDeviceImpl.java類的構造方法,原始碼如下:
public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler,
CameraCharacteristics characteristics, int appTargetSdkVersion) {
if (cameraId == null || callback == null || handler == null || characteristics == null) {
throw new IllegalArgumentException("Null argument given");
}
mCameraId = cameraId;
mDeviceCallback = callback;
mDeviceHandler = handler;
mCharacteristics = characteristics;
mAppTargetSdkVersion = appTargetSdkVersion;
final int MAX_TAG_LEN = 23;
String tag = String.format("CameraDevice-JV-%s", mCameraId);
if (tag.length() > MAX_TAG_LEN) {
tag = tag.substring(0, MAX_TAG_LEN);
}
TAG = tag;
Integer partialCount =
mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT);
if (partialCount == null) {
// 1 means partial result is not supported.
mTotalPartialCount = 1;
} else {
mTotalPartialCount = partialCount;
}
}
這裡比較重要的資訊就是關於tag的賦值了,它是執行String.format("CameraDevice-JV-%s", mCameraId)邏輯來賦值的,我們也可以在開啟相機時搜尋到這個關鍵字,它對我們實際的處理問題可以說是一個點,我們可以從這裡判斷肯定有應用層想要開啟相機裝置,而且可以根據id看到想要開啟哪個目標裝置。回到openCameraDeviceUserAsync方法中,接下來呼叫剛才建立好的CameraDeviceImpl物件的getCallbacks()方法獲取一個ICameraDeviceCallbacks物件,它是通過ICameraDeviceCallbacks.aidl定義的,所以它就是一個binder物件,而它的作用就是把這個物件傳遞到CameraServer程序當中,來配合回撥各個節點方法的,我們馬上就會看到。接下來判斷if (supportsCamera2ApiLocked(cameraId))條件,它的意思就是當前是否支援camera2.0的協議,這是Android對於相機的優化,我們假設這裡支援,那麼繼續呼叫cameraUser = cameraService.connectDevice(callbacks, cameraId, mContext.getOpPackageName(), uid)邏輯來給區域性變數cameraUser賦值,這句邏輯往下就是我們最核心的地方了,而這個返回值也就是CameraServer程序派給我們的操作代表,我們應用的各項工作都是由它進行中轉來實現了。
我們繼續往下看完這個方法的實現,然後再回頭來分析cameraService.connectDevice的邏輯實現。接下來最後的兩句就是呼叫deviceImpl.setRemoteDevice(cameraUser)方法將CameraServer返回給我們的物件儲存下來,這句邏輯執行完成後,這個deviceImpl才算真正具備功能了,最後就是給區域性變數device賦值。我們繼續看一下frameworks\base\core\java\android\hardware\camera2\impl\CameraDeviceImpl.java類的setRemoteDevice方法,原始碼如下:
public void setRemoteDevice(ICameraDeviceUser remoteDevice) throws CameraAccessException {
synchronized(mInterfaceLock) {
// TODO: Move from decorator to direct binder-mediated exceptions
// If setRemoteFailure already called, do nothing
if (mInError) return;
mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice);
IBinder remoteDeviceBinder = remoteDevice.asBinder();
// For legacy camera device, remoteDevice is in the same process, and
// asBinder returns NULL.
if (remoteDeviceBinder != null) {
try {
remoteDeviceBinder.linkToDeath(this, /*flag*/ 0);
} catch (RemoteException e) {
CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
"The camera device has encountered a serious error");
}
}
mDeviceHandler.post(mCallOnOpened);
mDeviceHandler.post(mCallOnUnconfigured);
}
}
這裡會使用CameraServer返回給我們的remoteDevice構建一個ICameraDeviceUserWrapper物件,就是將它再包裝一層,然後賦值給成員變數mRemoteDevice,我們可以看到,最終我們拿到的那個CameraDevice物件其實離真正給我們幹活的物件已經很遠了,中間都經過了好幾層包裝;然後再呼叫mDeviceHandler.post(mCallOnOpened)通過應用層,開啟相機的工作已經完成了,mDeviceHandler就是我們一開始呼叫openCamera方法時傳入的第三個引數了,所以使用它來post傳送一個訊息,那麼回撥也會在當時執行openCamera方法的執行緒上,這樣就可以保證執行緒不會切換了。mCallOnOpened是CameraDeviceImpl類的一個成員變數,定義原始碼如下:
private final Runnable mCallOnOpened = new Runnable() {
@Override
public void run() {
StateCallbackKK sessionCallback = null;
synchronized(mInterfaceLock) {
if (mRemoteDevice == null) return; // Camera already closed
sessionCallback = mSessionStateCallback;
}
if (sessionCallback != null) {
sessionCallback.onOpened(CameraDeviceImpl.this);
}
mDeviceCallback.onOpened(CameraDeviceImpl.this);
}
};
這裡的mDeviceCallback成員變數也就是前面構造CameraDeviceImpl物件時傳入的,也就是我們呼叫openCamera方法的第二個引數了,這時的CameraDeviceImpl物件已經準備好了,於是通過回撥介面將CameraDeviceImpl物件返回給應用層,應用層就可以通過它來執行各種邏輯真正的控制camera了。
好,回撥應用層的邏輯分析完,我們回過頭來繼續看一下cameraService.connectDevice方法是如何開啟camera的。
cameraService是通過binder程序間通訊,執行CameraManagerGlobal.get().getCameraService()獲取到的Camera服務端的一個binder代理物件,它實際的實現是native層的CameraService。CameraServer程序的啟動函式定義在frameworks\av\camera\cameraserver\main_cameraserver.cpp檔案中,它的main函式原始碼如下:
using namespace android;
int main(int argc __unused, char** argv __unused)
{
signal(SIGPIPE, SIG_IGN);
// Set 3 threads for HIDL calls
hardware::configureRpcThreadpool(3, /*willjoin*/ false);
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
ALOGI("ServiceManager: %p", sm.get());
CameraService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
這裡會呼叫CameraService::instantiate()方法,而CameraService是繼承了BinderService,所以instantiate()方法實際也是執行的BinderService物件的instantiate()方法,該方法定義在frameworks\native\libs\binder\include\binder\BinderService.h檔案中,原始碼如下:
template<typename SERVICE>
class BinderService
{
public:
static status_t publish(bool allowIsolated = false) {
sp<IServiceManager> sm(defaultServiceManager());
return sm->addService(
String16(SERVICE::getServiceName()),
new SERVICE(), allowIsolated);
}
static void publishAndJoinThreadPool(bool allowIsolated = false) {
publish(allowIsolated);
joinThreadPool();
}
static void instantiate() { publish(); }
static status_t shutdown() { return NO_ERROR; }
private:
static void joinThreadPool() {
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
ps->giveThreadPoolName();
IPCThreadState::self()->joinThreadPool();
}
};
在這裡執行publish方法時,以CameraService類的getServiceName()方法返回的char*為鍵值(media.camera)將CameraService物件新增到service_manager當中的。所以接下來我們就來看一下CameraService類的connectDevice方法的實現,原始碼如下:
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,
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;
}
這裡的幾個引數我們要看一下,第一個cameraCb就是我們在framework中得到的那個binder物件,以後的部分邏輯都會通過它來進行中轉;cameraId表示我們要開啟的目標裝置;clientPackageName表示請求執行開啟camera裝置的應用程序包名;clientUid表示應用程序的uid;最後的device就是返回給framework的物件了,註釋已經寫的非常清楚了,它是一個輸出引數。接下來執行sp<CameraDeviceClient> client = nullptr宣告一個CameraDeviceClient物件,在給它賦值成功之後,將它再賦值給輸出引數device,所以說我們在framework中看到的那個cameraUser實際上就是這裡的CameraDeviceClient物件了。
接下來我們繼續分析connectHelper方法的實現,原始碼如下:
template<class CALLBACK, class CLIENT>
Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
int halVersion, const String16& clientPackageName, int clientUid, int clientPid,
apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
/*out*/sp<CLIENT>& device) {
binder::Status ret = binder::Status::ok();
String8 clientName8(clientPackageName);
int originalClientPid = 0;
ALOGI("CameraService::connect call (PID %d \"%s\", camera ID %s) for HAL version %s and "
"Camera API version %d", clientPid, clientName8.string(), cameraId.string(),
(halVersion == -1) ? "default" : std::to_string(halVersion).c_str(),
static_cast<int>(effectiveApiLevel));
sp<CLIENT> client = nullptr;
{
// Acquire mServiceLock and prevent other clients from connecting
std::unique_ptr<AutoConditionLock> lock =
AutoConditionLock::waitAndAcquire(mServiceLockWrapper, DEFAULT_CONNECT_TIMEOUT_NS);
if (lock == nullptr) {
ALOGE("CameraService::connect (PID %d) rejected (too many other clients connecting)."
, clientPid);
return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE,
"Cannot open camera %s for \"%s\" (PID %d): Too many other clients connecting",
cameraId.string(), clientName8.string(), clientPid);
}
// Enforce client permissions and do basic sanity checks
if(!(ret = validateConnectLocked(cameraId, clientName8,
/*inout*/clientUid, /*inout*/clientPid, /*out*/originalClientPid)).isOk()) {
return ret;
}
// Check the shim parameters after acquiring lock, if they have already been updated and
// we were doing a shim update, return immediately
if (shimUpdateOnly) {
auto cameraState = getCameraState(cameraId);
if (cameraState != nullptr) {
if (!cameraState->getShimParams().isEmpty()) return ret;
}
}
status_t err;
sp<BasicClient> clientTmp = nullptr;
std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>> partial;
if ((err = handleEvictionsLocked(cameraId, originalClientPid, effectiveApiLevel,
IInterface::asBinder(cameraCb), clientName8, /*out*/&clientTmp,
/*out*/&partial)) != NO_ERROR) {
switch (err) {
case -ENODEV:
return STATUS_ERROR_FMT(ERROR_DISCONNECTED,
"No camera device with ID \"%s\" currently available",
cameraId.string());
case -EBUSY:
return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE,
"Higher-priority client using camera, ID \"%s\" currently unavailable",
cameraId.string());
default:
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Unexpected error %s (%d) opening camera \"%s\"",
strerror(-err), err, cameraId.string());
}
}
if (clientTmp.get() != nullptr) {
// Handle special case for API1 MediaRecorder where the existing client is returned
device = static_cast<CLIENT*>(clientTmp.get());
return ret;
}
// give flashlight a chance to close devices if necessary.
mFlashlight->prepareDeviceOpen(cameraId);
int facing = -1;
int deviceVersion = getDeviceVersion(cameraId, /*out*/&facing);
if (facing == -1) {
ALOGE("%s: Unable to get camera device \"%s\" facing", __FUNCTION__, cameraId.string());
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Unable to get camera device \"%s\" facing", cameraId.string());
}
sp<BasicClient> tmp = nullptr;
if(!(ret = makeClient(this, cameraCb, clientPackageName, cameraId, facing, clientPid,
clientUid, getpid(), legacyMode, halVersion, deviceVersion, effectiveApiLevel,
/*out*/&tmp)).isOk()) {
return ret;
}
client = static_cast<CLIENT*>(tmp.get());
LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",
__FUNCTION__);
err = client->initialize(mCameraProviderManager);
if (err != OK) {
ALOGE("%s: Could not initialize client from HAL.", __FUNCTION__);
// Errors could be from the HAL module open call or from AppOpsManager
switch(err) {
case BAD_VALUE:
return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
"Illegal argument to HAL module for camera \"%s\"", cameraId.string());
case -EBUSY:
return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE,
"Camera \"%s\" is already open", cameraId.string());
case -EUSERS:
return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE,
"Too many cameras already open, cannot open camera \"%s\"",
cameraId.string());
case PERMISSION_DENIED:
return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
"No permission to open camera \"%s\"", cameraId.string());
case -EACCES:
return STATUS_ERROR_FMT(ERROR_DISABLED,
"Camera \"%s\" disabled by policy", cameraId.string());
case -ENODEV:
default:
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Failed to initialize camera \"%s\": %s (%d)", cameraId.string(),
strerror(-err), err);
}
}
// Update shim paremeters for legacy clients
if (effectiveApiLevel == API_1) {
// Assume we have always received a Client subclass for API1
sp<Client> shimClient = reinterpret_cast<Client*>(client.get());
String8 rawParams = shimClient->getParameters();
CameraParameters params(rawParams);
auto cameraState = getCameraState(cameraId);
if (cameraState != nullptr) {
cameraState->setShimParams(params);
} else {
ALOGE("%s: Cannot update shim parameters for camera %s, no such device exists.",
__FUNCTION__, cameraId.string());
}
}
if (shimUpdateOnly) {
// If only updating legacy shim parameters, immediately disconnect client
mServiceLock.unlock();
client->disconnect();
mServiceLock.lock();
} else {
// Otherwise, add client to active clients list
finishConnectLocked(client, partial);
}
} // lock is destroyed, allow further connect calls
// Important: release the mutex here so the client can call back into the service from its
// destructor (can be at the end of the call)
device = client;
return ret;
}
該方法是一個模板方法,這裡的handleEvictionsLocked、client->initialize方法執行出錯之後的日誌列印對於我們分析問題也會有非常大的幫助,我們平時工作中經常碰到相機開啟失敗的問題,如果有這些日誌,那就說明問題肯定是出在CameraServer程序往下哪裡的邏輯中了。
往下我們就主要來看一下makeClient、client->initialize這兩句方法的實現。makeClient方法的原始碼如下:
Status CameraService::makeClient(const sp<CameraService>& cameraService,
const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
/*out*/sp<BasicClient>* client) {
if (halVersion < 0 || halVersion == deviceVersion) {
// Default path: HAL version is unspecified by caller, create CameraClient
// based on device version reported by the HAL.
switch(deviceVersion) {
case CAMERA_DEVICE_API_VERSION_1_0:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new CameraClient(cameraService, tmp, packageName, cameraIdToInt(cameraId),
facing, clientPid, clientUid, getpid(), legacyMode);
} else { // Camera2 API route
ALOGW("Camera using old HAL version: %d", deviceVersion);
return STATUS_ERROR_FMT(ERROR_DEPRECATED_HAL,
"Camera device \"%s\" HAL version %d does not support camera2 API",
cameraId.string(), deviceVersion);
}
break;
case CAMERA_DEVICE_API_VERSION_3_0:
case CAMERA_DEVICE_API_VERSION_3_1:
case CAMERA_DEVICE_API_VERSION_3_2:
case CAMERA_DEVICE_API_VERSION_3_3:
case CAMERA_DEVICE_API_VERSION_3_4:
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new Camera2Client(cameraService, tmp, packageName, cameraIdToInt(cameraId),
facing, clientPid, clientUid, servicePid, legacyMode);
} else { // Camera2 API route
sp<hardware::camera2::ICameraDeviceCallbacks> tmp =
static_cast<hardware::camera2::ICameraDeviceCallbacks*>(cameraCb.get());
*client = new CameraDeviceClient(cameraService, tmp, packageName, cameraId,
facing, clientPid, clientUid, servicePid);
}
break;
default:
// Should not be reachable
ALOGE("Unknown camera device HAL version: %d", deviceVersion);
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Camera device \"%s\" has unknown HAL version %d",
cameraId.string(), deviceVersion);
}
} else {
// A particular HAL version is requested by caller. Create CameraClient
// based on the requested HAL version.
if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 &&
halVersion == CAMERA_DEVICE_API_VERSION_1_0) {
// Only support higher HAL version device opened as HAL1.0 device.
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new CameraClient(cameraService, tmp, packageName, cameraIdToInt(cameraId),
facing, clientPid, clientUid, servicePid, legacyMode);
} else {
// Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
" opened as HAL %x device", halVersion, deviceVersion,
CAMERA_DEVICE_API_VERSION_1_0);
return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
"Camera device \"%s\" (HAL version %d) cannot be opened as HAL version %d",
cameraId.string(), deviceVersion, halVersion);
}
}
return Status::ok();
}
一般驅動版本halVersion和裝置版本deviceVersion是相同的,所以進入第一個if分支,effectiveApiLevel引數是在呼叫connectHelper方法時傳入的,值為API_2,所以進入else分支,直接使用我們上邊傳進來的引數構造一個CameraDeviceClient物件,而該物件也就是我們應用程序和CameraServer程序通訊的使者了,所有的工作都是由它來進行中轉的。我們繼續看一下它的構造方法的實現。frameworks\av\services\camera\libcameraservice\api2\CameraDeviceClient.cpp類的構造方法原始碼如下:
CameraDeviceClient::CameraDeviceClient(const sp<CameraService>& cameraService,
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
const String16& clientPackageName,
const String8& cameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
int servicePid) :
Camera2ClientBase(cameraService, remoteCallback, clientPackageName,
cameraId, cameraFacing, clientPid, clientUid, servicePid),
mInputStream(),
mStreamingRequestId(REQUEST_ID_NONE),
mRequestIdCounter(0) {
ATRACE_CALL();
ALOGI("CameraDeviceClient %s: Opened", cameraId.string());
}
因為它是繼承Camera2ClientBase的,所以也會執行Camera2ClientBase類的構造方法。frameworks\av\services\camera\libcameraservice\common\Camera2ClientBase.cpp類的構造方法的原始碼如下:
template <typename TClientBase>
Camera2ClientBase<TClientBase>::Camera2ClientBase(
const sp<CameraService>& cameraService,
const sp<TCamCallbacks>& remoteCallback,
const String16& clientPackageName,
const String8& cameraId,
int cameraFacing,
int clientPid,
uid_t clientUid,
int servicePid):
TClientBase(cameraService, remoteCallback, clientPackageName,
cameraId, cameraFacing, clientPid, clientUid, servicePid),
mSharedCameraCallbacks(remoteCallback),
mDeviceVersion(cameraService->getDeviceVersion(TClientBase::mCameraIdStr)),
mDeviceActive(false)
{
ALOGI("Camera %s: Opened. Client: %s (PID %d, UID %d)", cameraId.string(),
String8(clientPackageName).string(), clientPid, clientUid);
mInitialClientPid = clientPid;
mDevice = new Camera3Device(cameraId);
LOG_ALWAYS_FATAL_IF(mDevice == 0, "Device should never be NULL here.");
}
在這裡又出現了一個非常重要的物件Camera3Device,後邊我們就會看到,我們操作相機的所有工作在CameraServer程序都是由它中轉來和CameraDaemon程序進行通訊的。frameworks\av\services\camera\libcameraservice\device3\Camera3Device.cpp的構造方法的原始碼如下:
Camera3Device::Camera3Device(const String8 &id):
mId(id),
mOperatingMode(NO_MODE),
mIsConstrainedHighSpeedConfiguration(false),
mStatus(STATUS_UNINITIALIZED),
mStatusWaiters(0),
mUsePartialResult(false),
mNumPartialResults(1),
mTimestampOffset(0),
mNextResultFrameNumber(0),
mNextReprocessResultFrameNumber(0),
mNextShutterFrameNumber(0),
mNextReprocessShutterFrameNumber(0),
mListener(NULL),
mVendorTagId(CAMERA_METADATA_INVALID_VENDOR_ID)
{
ATRACE_CALL();
camera3_callback_ops::notify = &sNotify;
camera3_callback_ops::process_capture_result = &sProcessCaptureResult;
ALOGV("%s: Created device for camera %s", __FUNCTION__, mId.string());
}
這裡的sProcessCaptureResult是一個函式指標,從它的命名也很容易判斷出來,它就是處理結果回撥用,那是哪個結果回撥呢?當然是CameraDaemon程序對一幀圖片處理完成之後的結果回調了,注意這裡的capture不單指拍照,預覽的回撥也是通過該介面傳回來進行處理的。
該構造的物件都建立好了,再回到CameraService類的connectHelper方法中,繼續來看一下client->initialize(mCameraProviderManager)邏輯的實現,這裡的client就是CameraDeviceClient了,frameworks\av\services\camera\libcameraservice\api2\CameraDeviceClient.cpp類的initialize方法的原始碼如下:
status_t Camera