Merge "Report key request message type for unprefixed EME"
diff --git a/camera/Android.mk b/camera/Android.mk
index da5ac59..4c4700b 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -30,12 +30,10 @@
ICameraServiceListener.cpp \
ICameraRecordingProxy.cpp \
ICameraRecordingProxyListener.cpp \
- IProCameraUser.cpp \
- IProCameraCallbacks.cpp \
camera2/ICameraDeviceUser.cpp \
camera2/ICameraDeviceCallbacks.cpp \
camera2/CaptureRequest.cpp \
- ProCamera.cpp \
+ camera2/OutputConfiguration.cpp \
CameraBase.cpp \
CameraUtils.cpp \
VendorTagDescriptor.cpp
diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp
index 65a1a47..5d50aa8 100644
--- a/camera/CameraBase.cpp
+++ b/camera/CameraBase.cpp
@@ -29,7 +29,6 @@
#include <camera/ICameraService.h>
// needed to instantiate
-#include <camera/ProCamera.h>
#include <camera/Camera.h>
#include <system/camera_metadata.h>
@@ -217,7 +216,6 @@
return cs->removeListener(listener);
}
-template class CameraBase<ProCamera>;
template class CameraBase<Camera>;
} // namespace android
diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp
index 3dbf75e..68969cf 100644
--- a/camera/CameraParameters.cpp
+++ b/camera/CameraParameters.cpp
@@ -530,4 +530,8 @@
-1;
}
+bool CameraParameters::isEmpty() const {
+ return mMap.isEmpty();
+}
+
}; // namespace android
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index a75cb48..63c82cc 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -2,16 +2,16 @@
**
** Copyright 2008, 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
+** 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
+** 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
+** 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.
*/
@@ -29,8 +29,6 @@
#include <camera/ICameraService.h>
#include <camera/ICameraServiceListener.h>
-#include <camera/IProCameraUser.h>
-#include <camera/IProCameraCallbacks.h>
#include <camera/ICamera.h>
#include <camera/ICameraClient.h>
#include <camera/camera2/ICameraDeviceUser.h>
@@ -223,28 +221,6 @@
return reply.readInt32();
}
- // connect to camera service (pro client)
- virtual status_t connectPro(const sp<IProCameraCallbacks>& cameraCb, int cameraId,
- const String16 &clientPackageName, int clientUid,
- /*out*/
- sp<IProCameraUser>& device)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(cameraCb));
- data.writeInt32(cameraId);
- data.writeString16(clientPackageName);
- data.writeInt32(clientUid);
- remote()->transact(BnCameraService::CONNECT_PRO, data, &reply);
-
- if (readExceptionCode(reply)) return -EPROTO;
- status_t status = reply.readInt32();
- if (reply.readInt32() != 0) {
- device = interface_cast<IProCameraUser>(reply.readStrongBinder());
- }
- return status;
- }
-
// connect to camera service (android.hardware.camera2.CameraDevice)
virtual status_t connectDevice(
const sp<ICameraDeviceCallbacks>& cameraCb,
@@ -404,26 +380,6 @@
}
return NO_ERROR;
} break;
- case CONNECT_PRO: {
- CHECK_INTERFACE(ICameraService, data, reply);
- sp<IProCameraCallbacks> cameraClient =
- interface_cast<IProCameraCallbacks>(data.readStrongBinder());
- int32_t cameraId = data.readInt32();
- const String16 clientName = data.readString16();
- int32_t clientUid = data.readInt32();
- sp<IProCameraUser> camera;
- status_t status = connectPro(cameraClient, cameraId,
- clientName, clientUid, /*out*/camera);
- reply->writeNoException();
- reply->writeInt32(status);
- if (camera != NULL) {
- reply->writeInt32(1);
- reply->writeStrongBinder(IInterface::asBinder(camera));
- } else {
- reply->writeInt32(0);
- }
- return NO_ERROR;
- } break;
case CONNECT_DEVICE: {
CHECK_INTERFACE(ICameraService, data, reply);
sp<ICameraDeviceCallbacks> cameraClient =
diff --git a/camera/IProCameraCallbacks.cpp b/camera/IProCameraCallbacks.cpp
deleted file mode 100644
index bd3d420..0000000
--- a/camera/IProCameraCallbacks.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
-**
-** Copyright 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.
-*/
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "IProCameraCallbacks"
-#include <utils/Log.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/Parcel.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/Surface.h>
-#include <utils/Mutex.h>
-
-#include <camera/IProCameraCallbacks.h>
-
-#include "camera/CameraMetadata.h"
-
-namespace android {
-
-enum {
- NOTIFY_CALLBACK = IBinder::FIRST_CALL_TRANSACTION,
- LOCK_STATUS_CHANGED,
- RESULT_RECEIVED,
-};
-
-class BpProCameraCallbacks: public BpInterface<IProCameraCallbacks>
-{
-public:
- BpProCameraCallbacks(const sp<IBinder>& impl)
- : BpInterface<IProCameraCallbacks>(impl)
- {
- }
-
- // generic callback from camera service to app
- void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
- {
- ALOGV("notifyCallback");
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
- data.writeInt32(msgType);
- data.writeInt32(ext1);
- data.writeInt32(ext2);
- remote()->transact(NOTIFY_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- void onLockStatusChanged(LockStatus newLockStatus) {
- ALOGV("onLockStatusChanged");
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
- data.writeInt32(newLockStatus);
- remote()->transact(LOCK_STATUS_CHANGED, data, &reply,
- IBinder::FLAG_ONEWAY);
- }
-
- void onResultReceived(int32_t requestId, camera_metadata* result) {
- ALOGV("onResultReceived");
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
- data.writeInt32(requestId);
- CameraMetadata::writeToParcel(data, result);
- remote()->transact(RESULT_RECEIVED, data, &reply, IBinder::FLAG_ONEWAY);
- }
-};
-
-IMPLEMENT_META_INTERFACE(ProCameraCallbacks,
- "android.hardware.IProCameraCallbacks");
-
-// ----------------------------------------------------------------------
-
-status_t BnProCameraCallbacks::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- ALOGV("onTransact - code = %d", code);
- switch(code) {
- case NOTIFY_CALLBACK: {
- ALOGV("NOTIFY_CALLBACK");
- CHECK_INTERFACE(IProCameraCallbacks, data, reply);
- int32_t msgType = data.readInt32();
- int32_t ext1 = data.readInt32();
- int32_t ext2 = data.readInt32();
- notifyCallback(msgType, ext1, ext2);
- return NO_ERROR;
- } break;
- case LOCK_STATUS_CHANGED: {
- ALOGV("LOCK_STATUS_CHANGED");
- CHECK_INTERFACE(IProCameraCallbacks, data, reply);
- LockStatus newLockStatus
- = static_cast<LockStatus>(data.readInt32());
- onLockStatusChanged(newLockStatus);
- return NO_ERROR;
- } break;
- case RESULT_RECEIVED: {
- ALOGV("RESULT_RECEIVED");
- CHECK_INTERFACE(IProCameraCallbacks, data, reply);
- int32_t requestId = data.readInt32();
- camera_metadata_t *result = NULL;
- CameraMetadata::readFromParcel(data, &result);
- onResultReceived(requestId, result);
- return NO_ERROR;
- break;
- }
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
diff --git a/camera/IProCameraUser.cpp b/camera/IProCameraUser.cpp
deleted file mode 100644
index 9bd7597..0000000
--- a/camera/IProCameraUser.cpp
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
-**
-** Copyright 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.
-*/
-
-// #define LOG_NDEBUG 0
-#define LOG_TAG "IProCameraUser"
-#include <utils/Log.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <binder/Parcel.h>
-#include <camera/IProCameraUser.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/Surface.h>
-#include "camera/CameraMetadata.h"
-
-namespace android {
-
-enum {
- DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
- CONNECT,
- EXCLUSIVE_TRY_LOCK,
- EXCLUSIVE_LOCK,
- EXCLUSIVE_UNLOCK,
- HAS_EXCLUSIVE_LOCK,
- SUBMIT_REQUEST,
- CANCEL_REQUEST,
- DELETE_STREAM,
- CREATE_STREAM,
- CREATE_DEFAULT_REQUEST,
- GET_CAMERA_INFO,
-};
-
-class BpProCameraUser: public BpInterface<IProCameraUser>
-{
-public:
- BpProCameraUser(const sp<IBinder>& impl)
- : BpInterface<IProCameraUser>(impl)
- {
- }
-
- // disconnect from camera service
- void disconnect()
- {
- ALOGV("disconnect");
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
- remote()->transact(DISCONNECT, data, &reply);
- reply.readExceptionCode();
- }
-
- virtual status_t connect(const sp<IProCameraCallbacks>& cameraClient)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(cameraClient));
- remote()->transact(CONNECT, data, &reply);
- return reply.readInt32();
- }
-
- /* Shared ProCameraUser */
-
- virtual status_t exclusiveTryLock()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
- remote()->transact(EXCLUSIVE_TRY_LOCK, data, &reply);
- return reply.readInt32();
- }
- virtual status_t exclusiveLock()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
- remote()->transact(EXCLUSIVE_LOCK, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t exclusiveUnlock()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
- remote()->transact(EXCLUSIVE_UNLOCK, data, &reply);
- return reply.readInt32();
- }
-
- virtual bool hasExclusiveLock()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
- remote()->transact(HAS_EXCLUSIVE_LOCK, data, &reply);
- return !!reply.readInt32();
- }
-
- virtual int submitRequest(camera_metadata_t* metadata, bool streaming)
- {
-
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
-
- // arg0+arg1
- CameraMetadata::writeToParcel(data, metadata);
-
- // arg2 = streaming (bool)
- data.writeInt32(streaming);
-
- remote()->transact(SUBMIT_REQUEST, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t cancelRequest(int requestId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
- data.writeInt32(requestId);
-
- remote()->transact(CANCEL_REQUEST, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t deleteStream(int streamId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
- data.writeInt32(streamId);
-
- remote()->transact(DELETE_STREAM, data, &reply);
- return reply.readInt32();
- }
-
- virtual status_t createStream(int width, int height, int format,
- const sp<IGraphicBufferProducer>& bufferProducer,
- /*out*/
- int* streamId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
- data.writeInt32(width);
- data.writeInt32(height);
- data.writeInt32(format);
-
- sp<IBinder> b(IInterface::asBinder(bufferProducer));
- data.writeStrongBinder(b);
-
- remote()->transact(CREATE_STREAM, data, &reply);
-
- int sId = reply.readInt32();
- if (streamId) {
- *streamId = sId;
- }
- return reply.readInt32();
- }
-
- // Create a request object from a template.
- virtual status_t createDefaultRequest(int templateId,
- /*out*/
- camera_metadata** request)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
- data.writeInt32(templateId);
- remote()->transact(CREATE_DEFAULT_REQUEST, data, &reply);
- CameraMetadata::readFromParcel(reply, /*out*/request);
- return reply.readInt32();
- }
-
-
- virtual status_t getCameraInfo(int cameraId, camera_metadata** info)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
- data.writeInt32(cameraId);
- remote()->transact(GET_CAMERA_INFO, data, &reply);
- CameraMetadata::readFromParcel(reply, /*out*/info);
- return reply.readInt32();
- }
-
-
-private:
-
-
-};
-
-IMPLEMENT_META_INTERFACE(ProCameraUser, "android.hardware.IProCameraUser");
-
-// ----------------------------------------------------------------------
-
-status_t BnProCameraUser::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case DISCONNECT: {
- ALOGV("DISCONNECT");
- CHECK_INTERFACE(IProCameraUser, data, reply);
- disconnect();
- reply->writeNoException();
- return NO_ERROR;
- } break;
- case CONNECT: {
- CHECK_INTERFACE(IProCameraUser, data, reply);
- sp<IProCameraCallbacks> cameraClient =
- interface_cast<IProCameraCallbacks>(data.readStrongBinder());
- reply->writeInt32(connect(cameraClient));
- return NO_ERROR;
- } break;
-
- /* Shared ProCameraUser */
- case EXCLUSIVE_TRY_LOCK: {
- CHECK_INTERFACE(IProCameraUser, data, reply);
- reply->writeInt32(exclusiveTryLock());
- return NO_ERROR;
- } break;
- case EXCLUSIVE_LOCK: {
- CHECK_INTERFACE(IProCameraUser, data, reply);
- reply->writeInt32(exclusiveLock());
- return NO_ERROR;
- } break;
- case EXCLUSIVE_UNLOCK: {
- CHECK_INTERFACE(IProCameraUser, data, reply);
- reply->writeInt32(exclusiveUnlock());
- return NO_ERROR;
- } break;
- case HAS_EXCLUSIVE_LOCK: {
- CHECK_INTERFACE(IProCameraUser, data, reply);
- reply->writeInt32(hasExclusiveLock());
- return NO_ERROR;
- } break;
- case SUBMIT_REQUEST: {
- CHECK_INTERFACE(IProCameraUser, data, reply);
- camera_metadata_t* metadata;
- CameraMetadata::readFromParcel(data, /*out*/&metadata);
-
- // arg2 = streaming (bool)
- bool streaming = data.readInt32();
-
- // return code: requestId (int32)
- reply->writeInt32(submitRequest(metadata, streaming));
-
- return NO_ERROR;
- } break;
- case CANCEL_REQUEST: {
- CHECK_INTERFACE(IProCameraUser, data, reply);
- int requestId = data.readInt32();
- reply->writeInt32(cancelRequest(requestId));
- return NO_ERROR;
- } break;
- case DELETE_STREAM: {
- CHECK_INTERFACE(IProCameraUser, data, reply);
- int streamId = data.readInt32();
- reply->writeInt32(deleteStream(streamId));
- return NO_ERROR;
- } break;
- case CREATE_STREAM: {
- CHECK_INTERFACE(IProCameraUser, data, reply);
- int width, height, format;
-
- width = data.readInt32();
- height = data.readInt32();
- format = data.readInt32();
-
- sp<IGraphicBufferProducer> bp =
- interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
-
- int streamId = -1;
- status_t ret;
- ret = createStream(width, height, format, bp, &streamId);
-
- reply->writeInt32(streamId);
- reply->writeInt32(ret);
-
- return NO_ERROR;
- } break;
-
- case CREATE_DEFAULT_REQUEST: {
- CHECK_INTERFACE(IProCameraUser, data, reply);
-
- int templateId = data.readInt32();
-
- camera_metadata_t* request = NULL;
- status_t ret;
- ret = createDefaultRequest(templateId, &request);
-
- CameraMetadata::writeToParcel(*reply, request);
- reply->writeInt32(ret);
-
- free_camera_metadata(request);
-
- return NO_ERROR;
- } break;
- case GET_CAMERA_INFO: {
- CHECK_INTERFACE(IProCameraUser, data, reply);
-
- int cameraId = data.readInt32();
-
- camera_metadata_t* info = NULL;
- status_t ret;
- ret = getCameraInfo(cameraId, &info);
-
- CameraMetadata::writeToParcel(*reply, info);
- reply->writeInt32(ret);
-
- free_camera_metadata(info);
-
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp
deleted file mode 100644
index 48f8e8e..0000000
--- a/camera/ProCamera.cpp
+++ /dev/null
@@ -1,436 +0,0 @@
-/*
-**
-** 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.
-*/
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "ProCamera"
-#include <utils/Log.h>
-#include <utils/threads.h>
-#include <utils/Mutex.h>
-
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/IMemory.h>
-
-#include <camera/ProCamera.h>
-#include <camera/IProCameraUser.h>
-#include <camera/IProCameraCallbacks.h>
-
-#include <gui/IGraphicBufferProducer.h>
-
-#include <system/camera_metadata.h>
-
-namespace android {
-
-sp<ProCamera> ProCamera::connect(int cameraId)
-{
- return CameraBaseT::connect(cameraId, String16(),
- ICameraService::USE_CALLING_UID);
-}
-
-ProCamera::ProCamera(int cameraId)
- : CameraBase(cameraId)
-{
-}
-
-CameraTraits<ProCamera>::TCamConnectService CameraTraits<ProCamera>::fnConnectService =
- &ICameraService::connectPro;
-
-ProCamera::~ProCamera()
-{
-
-}
-
-/* IProCameraUser's implementation */
-
-// callback from camera service
-void ProCamera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
-{
- return CameraBaseT::notifyCallback(msgType, ext1, ext2);
-}
-
-void ProCamera::onLockStatusChanged(
- IProCameraCallbacks::LockStatus newLockStatus)
-{
- ALOGV("%s: newLockStatus = %d", __FUNCTION__, newLockStatus);
-
- sp<ProCameraListener> listener;
- {
- Mutex::Autolock _l(mLock);
- listener = mListener;
- }
- if (listener != NULL) {
- switch (newLockStatus) {
- case IProCameraCallbacks::LOCK_ACQUIRED:
- listener->onLockAcquired();
- break;
- case IProCameraCallbacks::LOCK_RELEASED:
- listener->onLockReleased();
- break;
- case IProCameraCallbacks::LOCK_STOLEN:
- listener->onLockStolen();
- break;
- default:
- ALOGE("%s: Unknown lock status: %d",
- __FUNCTION__, newLockStatus);
- }
- }
-}
-
-void ProCamera::onResultReceived(int32_t requestId, camera_metadata* result) {
- ALOGV("%s: requestId = %d, result = %p", __FUNCTION__, requestId, result);
-
- sp<ProCameraListener> listener;
- {
- Mutex::Autolock _l(mLock);
- listener = mListener;
- }
-
- CameraMetadata tmp(result);
-
- // Unblock waitForFrame(id) callers
- {
- Mutex::Autolock al(mWaitMutex);
- mMetadataReady = true;
- mLatestMetadata = tmp; // make copy
- mWaitCondition.broadcast();
- }
-
- result = tmp.release();
-
- if (listener != NULL) {
- listener->onResultReceived(requestId, result);
- } else {
- free_camera_metadata(result);
- }
-
-}
-
-status_t ProCamera::exclusiveTryLock()
-{
- sp <IProCameraUser> c = mCamera;
- if (c == 0) return NO_INIT;
-
- return c->exclusiveTryLock();
-}
-status_t ProCamera::exclusiveLock()
-{
- sp <IProCameraUser> c = mCamera;
- if (c == 0) return NO_INIT;
-
- return c->exclusiveLock();
-}
-status_t ProCamera::exclusiveUnlock()
-{
- sp <IProCameraUser> c = mCamera;
- if (c == 0) return NO_INIT;
-
- return c->exclusiveUnlock();
-}
-bool ProCamera::hasExclusiveLock()
-{
- sp <IProCameraUser> c = mCamera;
- if (c == 0) return NO_INIT;
-
- return c->hasExclusiveLock();
-}
-
-// Note that the callee gets a copy of the metadata.
-int ProCamera::submitRequest(const struct camera_metadata* metadata,
- bool streaming)
-{
- sp <IProCameraUser> c = mCamera;
- if (c == 0) return NO_INIT;
-
- return c->submitRequest(const_cast<struct camera_metadata*>(metadata),
- streaming);
-}
-
-status_t ProCamera::cancelRequest(int requestId)
-{
- sp <IProCameraUser> c = mCamera;
- if (c == 0) return NO_INIT;
-
- return c->cancelRequest(requestId);
-}
-
-status_t ProCamera::deleteStream(int streamId)
-{
- sp <IProCameraUser> c = mCamera;
- if (c == 0) return NO_INIT;
-
- status_t s = c->deleteStream(streamId);
-
- mStreams.removeItem(streamId);
-
- return s;
-}
-
-status_t ProCamera::createStream(int width, int height, int format,
- const sp<Surface>& surface,
- /*out*/
- int* streamId)
-{
- *streamId = -1;
-
- ALOGV("%s: createStreamW %dx%d (fmt=0x%x)", __FUNCTION__, width, height,
- format);
-
- if (surface == 0) {
- return BAD_VALUE;
- }
-
- return createStream(width, height, format,
- surface->getIGraphicBufferProducer(),
- streamId);
-}
-
-status_t ProCamera::createStream(int width, int height, int format,
- const sp<IGraphicBufferProducer>& bufferProducer,
- /*out*/
- int* streamId) {
- *streamId = -1;
-
- ALOGV("%s: createStreamT %dx%d (fmt=0x%x)", __FUNCTION__, width, height,
- format);
-
- if (bufferProducer == 0) {
- return BAD_VALUE;
- }
-
- sp <IProCameraUser> c = mCamera;
- status_t stat = c->createStream(width, height, format, bufferProducer,
- streamId);
-
- if (stat == OK) {
- StreamInfo s(*streamId);
-
- mStreams.add(*streamId, s);
- }
-
- return stat;
-}
-
-status_t ProCamera::createStreamCpu(int width, int height, int format,
- int heapCount,
- /*out*/
- sp<CpuConsumer>* cpuConsumer,
- int* streamId) {
- return createStreamCpu(width, height, format, heapCount,
- /*synchronousMode*/true,
- cpuConsumer, streamId);
-}
-
-status_t ProCamera::createStreamCpu(int width, int height, int format,
- int heapCount,
- bool synchronousMode,
- /*out*/
- sp<CpuConsumer>* cpuConsumer,
- int* streamId)
-{
- ALOGV("%s: createStreamW %dx%d (fmt=0x%x)", __FUNCTION__, width, height,
- format);
-
- *cpuConsumer = NULL;
-
- sp <IProCameraUser> c = mCamera;
- if (c == 0) return NO_INIT;
-
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<CpuConsumer> cc = new CpuConsumer(consumer, heapCount
- /*, synchronousMode*/);
- cc->setName(String8("ProCamera::mCpuConsumer"));
-
- sp<Surface> stc = new Surface(producer);
-
- status_t s = createStream(width, height, format,
- stc->getIGraphicBufferProducer(),
- streamId);
-
- if (s != OK) {
- ALOGE("%s: Failure to create stream %dx%d (fmt=0x%x)", __FUNCTION__,
- width, height, format);
- return s;
- }
-
- sp<ProFrameListener> frameAvailableListener =
- new ProFrameListener(this, *streamId);
-
- getStreamInfo(*streamId).cpuStream = true;
- getStreamInfo(*streamId).cpuConsumer = cc;
- getStreamInfo(*streamId).synchronousMode = synchronousMode;
- getStreamInfo(*streamId).stc = stc;
- // for lifetime management
- getStreamInfo(*streamId).frameAvailableListener = frameAvailableListener;
-
- cc->setFrameAvailableListener(frameAvailableListener);
-
- *cpuConsumer = cc;
-
- return s;
-}
-
-camera_metadata* ProCamera::getCameraInfo(int cameraId) {
- ALOGV("%s: cameraId = %d", __FUNCTION__, cameraId);
-
- sp <IProCameraUser> c = mCamera;
- if (c == 0) return NULL;
-
- camera_metadata* ptr = NULL;
- status_t status = c->getCameraInfo(cameraId, &ptr);
-
- if (status != OK) {
- ALOGE("%s: Failed to get camera info, error = %d", __FUNCTION__, status);
- }
-
- return ptr;
-}
-
-status_t ProCamera::createDefaultRequest(int templateId,
- camera_metadata** request) const {
- ALOGV("%s: templateId = %d", __FUNCTION__, templateId);
-
- sp <IProCameraUser> c = mCamera;
- if (c == 0) return NO_INIT;
-
- return c->createDefaultRequest(templateId, request);
-}
-
-void ProCamera::onFrameAvailable(int streamId) {
- ALOGV("%s: streamId = %d", __FUNCTION__, streamId);
-
- sp<ProCameraListener> listener = mListener;
- StreamInfo& stream = getStreamInfo(streamId);
-
- if (listener.get() != NULL) {
- listener->onFrameAvailable(streamId, stream.cpuConsumer);
- }
-
- // Unblock waitForFrame(id) callers
- {
- Mutex::Autolock al(mWaitMutex);
- getStreamInfo(streamId).frameReady++;
- mWaitCondition.broadcast();
- }
-}
-
-int ProCamera::waitForFrameBuffer(int streamId) {
- status_t stat = BAD_VALUE;
- Mutex::Autolock al(mWaitMutex);
-
- StreamInfo& si = getStreamInfo(streamId);
-
- if (si.frameReady > 0) {
- int numFrames = si.frameReady;
- si.frameReady = 0;
- return numFrames;
- } else {
- while (true) {
- stat = mWaitCondition.waitRelative(mWaitMutex,
- mWaitTimeout);
- if (stat != OK) {
- ALOGE("%s: Error while waiting for frame buffer: %d",
- __FUNCTION__, stat);
- return stat;
- }
-
- if (si.frameReady > 0) {
- int numFrames = si.frameReady;
- si.frameReady = 0;
- return numFrames;
- }
- // else it was some other stream that got unblocked
- }
- }
-
- return stat;
-}
-
-int ProCamera::dropFrameBuffer(int streamId, int count) {
- StreamInfo& si = getStreamInfo(streamId);
-
- if (!si.cpuStream) {
- return BAD_VALUE;
- } else if (count < 0) {
- return BAD_VALUE;
- }
-
- if (!si.synchronousMode) {
- ALOGW("%s: No need to drop frames on asynchronous streams,"
- " as asynchronous mode only keeps 1 latest frame around.",
- __FUNCTION__);
- return BAD_VALUE;
- }
-
- int numDropped = 0;
- for (int i = 0; i < count; ++i) {
- CpuConsumer::LockedBuffer buffer;
- if (si.cpuConsumer->lockNextBuffer(&buffer) != OK) {
- break;
- }
-
- si.cpuConsumer->unlockBuffer(buffer);
- numDropped++;
- }
-
- return numDropped;
-}
-
-status_t ProCamera::waitForFrameMetadata() {
- status_t stat = BAD_VALUE;
- Mutex::Autolock al(mWaitMutex);
-
- if (mMetadataReady) {
- return OK;
- } else {
- while (true) {
- stat = mWaitCondition.waitRelative(mWaitMutex,
- mWaitTimeout);
-
- if (stat != OK) {
- ALOGE("%s: Error while waiting for metadata: %d",
- __FUNCTION__, stat);
- return stat;
- }
-
- if (mMetadataReady) {
- mMetadataReady = false;
- return OK;
- }
- // else it was some other stream or metadata
- }
- }
-
- return stat;
-}
-
-CameraMetadata ProCamera::consumeFrameMetadata() {
- Mutex::Autolock al(mWaitMutex);
-
- // Destructive: Subsequent calls return empty metadatas
- CameraMetadata tmp = mLatestMetadata;
- mLatestMetadata.clear();
-
- return tmp;
-}
-
-ProCamera::StreamInfo& ProCamera::getStreamInfo(int streamId) {
- return mStreams.editValueFor(streamId);
-}
-
-}; // namespace android
diff --git a/camera/camera2/ICameraDeviceUser.cpp b/camera/camera2/ICameraDeviceUser.cpp
index d1d63d5..89c6fb7 100644
--- a/camera/camera2/ICameraDeviceUser.cpp
+++ b/camera/camera2/ICameraDeviceUser.cpp
@@ -26,6 +26,7 @@
#include <gui/Surface.h>
#include <camera/CameraMetadata.h>
#include <camera/camera2/CaptureRequest.h>
+#include <camera/camera2/OutputConfiguration.h>
namespace android {
@@ -208,17 +209,16 @@
return reply.readInt32();
}
- virtual status_t createStream(
- const sp<IGraphicBufferProducer>& bufferProducer)
+ virtual status_t createStream(const OutputConfiguration& outputConfiguration)
{
Parcel data, reply;
data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
-
- data.writeInt32(1); // marker that bufferProducer is not null
- data.writeString16(String16("unknown_name")); // name of surface
- sp<IBinder> b(IInterface::asBinder(bufferProducer));
- data.writeStrongBinder(b);
-
+ if (outputConfiguration.getGraphicBufferProducer() != NULL) {
+ data.writeInt32(1); // marker that OutputConfiguration is not null. Mimic aidl behavior
+ outputConfiguration.writeToParcel(data);
+ } else {
+ data.writeInt32(0);
+ }
remote()->transact(CREATE_STREAM, data, &reply);
reply.readExceptionCode();
@@ -394,22 +394,14 @@
case CREATE_STREAM: {
CHECK_INTERFACE(ICameraDeviceUser, data, reply);
- sp<IGraphicBufferProducer> bp;
+ status_t ret = BAD_VALUE;
if (data.readInt32() != 0) {
- String16 name = readMaybeEmptyString16(data);
- bp = interface_cast<IGraphicBufferProducer>(
- data.readStrongBinder());
-
- ALOGV("%s: CREATE_STREAM: bp = %p, name = %s", __FUNCTION__,
- bp.get(), String8(name).string());
+ OutputConfiguration outputConfiguration(data);
+ ret = createStream(outputConfiguration);
} else {
- ALOGV("%s: CREATE_STREAM: bp = unset, name = unset",
- __FUNCTION__);
+ ALOGE("%s: cannot take an empty OutputConfiguration", __FUNCTION__);
}
- status_t ret;
- ret = createStream(bp);
-
reply->writeNoException();
ALOGV("%s: CREATE_STREAM: write noException", __FUNCTION__);
reply->writeInt32(ret);
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
new file mode 100644
index 0000000..24acaa0
--- /dev/null
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -0,0 +1,79 @@
+/*
+**
+** Copyright 2015, 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.
+*/
+
+#define LOG_TAG "OutputConfiguration"
+#include <utils/Log.h>
+
+#include <camera/camera2/OutputConfiguration.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+
+const int OutputConfiguration::INVALID_ROTATION = -1;
+
+// Read empty strings without printing a false error message.
+String16 OutputConfiguration::readMaybeEmptyString16(const Parcel& parcel) {
+ size_t len;
+ const char16_t* str = parcel.readString16Inplace(&len);
+ if (str != NULL) {
+ return String16(str, len);
+ } else {
+ return String16();
+ }
+}
+
+sp<IGraphicBufferProducer> OutputConfiguration::getGraphicBufferProducer() const {
+ return mGbp;
+}
+
+int OutputConfiguration::getRotation() const {
+ return mRotation;
+}
+
+OutputConfiguration::OutputConfiguration(const Parcel& parcel) {
+ status_t err;
+ int rotation = 0;
+ if ((err = parcel.readInt32(&rotation)) != OK) {
+ ALOGE("%s: Failed to read rotation from parcel", __FUNCTION__);
+ mGbp = NULL;
+ mRotation = INVALID_ROTATION;
+ return;
+ }
+
+ String16 name = readMaybeEmptyString16(parcel);
+ const sp<IGraphicBufferProducer>& gbp =
+ interface_cast<IGraphicBufferProducer>(parcel.readStrongBinder());
+ mGbp = gbp;
+ mRotation = rotation;
+
+ ALOGV("%s: OutputConfiguration: bp = %p, name = %s", __FUNCTION__,
+ gbp.get(), String8(name).string());
+}
+
+status_t OutputConfiguration::writeToParcel(Parcel& parcel) const {
+
+ parcel.writeInt32(mRotation);
+ parcel.writeString16(String16("unknown_name")); // name of surface
+ sp<IBinder> b(IInterface::asBinder(mGbp));
+ parcel.writeStrongBinder(b);
+
+ return OK;
+}
+
+}; // namespace android
+
diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk
index 2db4c14..5d37f9e 100644
--- a/camera/tests/Android.mk
+++ b/camera/tests/Android.mk
@@ -17,7 +17,6 @@
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_SRC_FILES:= \
- ProCameraTests.cpp \
VendorTagDescriptorTests.cpp
LOCAL_SHARED_LIBRARIES := \
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
deleted file mode 100644
index 24b2327..0000000
--- a/camera/tests/ProCameraTests.cpp
+++ /dev/null
@@ -1,1284 +0,0 @@
-/*
- * 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.
- */
-
-#include <gtest/gtest.h>
-#include <iostream>
-
-#include <binder/IPCThreadState.h>
-#include <utils/Thread.h>
-
-#include "Camera.h"
-#include "ProCamera.h"
-#include <utils/Vector.h>
-#include <utils/Mutex.h>
-#include <utils/Condition.h>
-
-#include <gui/SurfaceComposerClient.h>
-#include <gui/Surface.h>
-
-#include <system/camera_metadata.h>
-#include <hardware/camera2.h> // for CAMERA2_TEMPLATE_PREVIEW only
-#include <camera/CameraMetadata.h>
-
-#include <camera/ICameraServiceListener.h>
-
-namespace android {
-namespace camera2 {
-namespace tests {
-namespace client {
-
-#define CAMERA_ID 0
-#define TEST_DEBUGGING 0
-
-#define TEST_LISTENER_TIMEOUT 1000000000 // 1 second listener timeout
-#define TEST_FORMAT HAL_PIXEL_FORMAT_Y16 //TODO: YUY2 instead
-
-#define TEST_FORMAT_MAIN HAL_PIXEL_FORMAT_Y8
-#define TEST_FORMAT_DEPTH HAL_PIXEL_FORMAT_Y16
-
-// defaults for display "test"
-#define TEST_DISPLAY_FORMAT HAL_PIXEL_FORMAT_Y8
-#define TEST_DISPLAY_WIDTH 320
-#define TEST_DISPLAY_HEIGHT 240
-
-#define TEST_CPU_FRAME_COUNT 2
-#define TEST_CPU_HEAP_COUNT 5
-
-#define TEST_FRAME_PROCESSING_DELAY_US 200000 // 200 ms
-
-#if TEST_DEBUGGING
-#define dout std::cerr
-#else
-#define dout if (0) std::cerr
-#endif
-
-#define EXPECT_OK(x) EXPECT_EQ(OK, (x))
-#define ASSERT_OK(x) ASSERT_EQ(OK, (x))
-
-class ProCameraTest;
-
-struct ServiceListener : public BnCameraServiceListener {
-
- ServiceListener() :
- mLatestStatus(STATUS_UNKNOWN),
- mPrevStatus(STATUS_UNKNOWN)
- {
- }
-
- void onStatusChanged(Status status, int32_t cameraId) {
- dout << "On status changed: 0x" << std::hex
- << (unsigned int) status << " cameraId " << cameraId
- << std::endl;
-
- Mutex::Autolock al(mMutex);
-
- mLatestStatus = status;
- mCondition.broadcast();
- }
-
- void onTorchStatusChanged(TorchStatus status, const String16& cameraId) {
- dout << "On torch status changed: 0x" << std::hex
- << (unsigned int) status << " cameraId " << cameraId.string()
- << std::endl;
- }
-
- status_t waitForStatusChange(Status& newStatus) {
- Mutex::Autolock al(mMutex);
-
- if (mLatestStatus != mPrevStatus) {
- newStatus = mLatestStatus;
- mPrevStatus = mLatestStatus;
- return OK;
- }
-
- status_t stat = mCondition.waitRelative(mMutex,
- TEST_LISTENER_TIMEOUT);
-
- if (stat == OK) {
- newStatus = mLatestStatus;
- mPrevStatus = mLatestStatus;
- }
-
- return stat;
- }
-
- Condition mCondition;
- Mutex mMutex;
-
- Status mLatestStatus;
- Status mPrevStatus;
-};
-
-enum ProEvent {
- UNKNOWN,
- ACQUIRED,
- RELEASED,
- STOLEN,
- FRAME_RECEIVED,
- RESULT_RECEIVED,
-};
-
-inline int ProEvent_Mask(ProEvent e) {
- return (1 << static_cast<int>(e));
-}
-
-typedef Vector<ProEvent> EventList;
-
-class ProCameraTestThread : public Thread
-{
-public:
- ProCameraTestThread() {
- }
-
- virtual bool threadLoop() {
- mProc = ProcessState::self();
- mProc->startThreadPool();
-
- IPCThreadState *ptr = IPCThreadState::self();
-
- ptr->joinThreadPool();
-
- return false;
- }
-
- sp<ProcessState> mProc;
-};
-
-class ProCameraTestListener : public ProCameraListener {
-
-public:
- static const int EVENT_MASK_ALL = 0xFFFFFFFF;
-
- ProCameraTestListener() {
- mEventMask = EVENT_MASK_ALL;
- mDropFrames = false;
- }
-
- status_t WaitForEvent() {
- Mutex::Autolock cal(mConditionMutex);
-
- {
- Mutex::Autolock al(mListenerMutex);
-
- if (mProEventList.size() > 0) {
- return OK;
- }
- }
-
- return mListenerCondition.waitRelative(mConditionMutex,
- TEST_LISTENER_TIMEOUT);
- }
-
- /* Read events into out. Existing queue is flushed */
- void ReadEvents(EventList& out) {
- Mutex::Autolock al(mListenerMutex);
-
- for (size_t i = 0; i < mProEventList.size(); ++i) {
- out.push(mProEventList[i]);
- }
-
- mProEventList.clear();
- }
-
- /**
- * Dequeue 1 event from the event queue.
- * Returns UNKNOWN if queue is empty
- */
- ProEvent ReadEvent() {
- Mutex::Autolock al(mListenerMutex);
-
- if (mProEventList.size() == 0) {
- return UNKNOWN;
- }
-
- ProEvent ev = mProEventList[0];
- mProEventList.removeAt(0);
-
- return ev;
- }
-
- void SetEventMask(int eventMask) {
- Mutex::Autolock al(mListenerMutex);
- mEventMask = eventMask;
- }
-
- // Automatically acquire/release frames as they are available
- void SetDropFrames(bool dropFrames) {
- Mutex::Autolock al(mListenerMutex);
- mDropFrames = dropFrames;
- }
-
-private:
- void QueueEvent(ProEvent ev) {
- bool eventAdded = false;
- {
- Mutex::Autolock al(mListenerMutex);
-
- // Drop events not part of mask
- if (ProEvent_Mask(ev) & mEventMask) {
- mProEventList.push(ev);
- eventAdded = true;
- }
- }
-
- if (eventAdded) {
- mListenerCondition.broadcast();
- }
- }
-
-protected:
-
- //////////////////////////////////////////////////
- ///////// ProCameraListener //////////////////////
- //////////////////////////////////////////////////
-
-
- // Lock has been acquired. Write operations now available.
- virtual void onLockAcquired() {
- QueueEvent(ACQUIRED);
- }
- // Lock has been released with exclusiveUnlock
- virtual void onLockReleased() {
- QueueEvent(RELEASED);
- }
-
- // Lock has been stolen by another client.
- virtual void onLockStolen() {
- QueueEvent(STOLEN);
- }
-
- // Lock free.
- virtual void onTriggerNotify(int32_t ext1, int32_t ext2, int32_t ext3) {
-
- dout << "Trigger notify: " << ext1 << " " << ext2
- << " " << ext3 << std::endl;
- }
-
- virtual void onFrameAvailable(int streamId,
- const sp<CpuConsumer>& consumer) {
-
- QueueEvent(FRAME_RECEIVED);
-
- Mutex::Autolock al(mListenerMutex);
- if (mDropFrames) {
- CpuConsumer::LockedBuffer buf;
- status_t ret;
-
- if (OK == (ret = consumer->lockNextBuffer(&buf))) {
-
- dout << "Frame received on streamId = " << streamId <<
- ", dataPtr = " << (void*)buf.data <<
- ", timestamp = " << buf.timestamp << std::endl;
-
- EXPECT_OK(consumer->unlockBuffer(buf));
- }
- } else {
- dout << "Frame received on streamId = " << streamId << std::endl;
- }
- }
-
- virtual void onResultReceived(int32_t requestId,
- camera_metadata* request) {
- dout << "Result received requestId = " << requestId
- << ", requestPtr = " << (void*)request << std::endl;
- QueueEvent(RESULT_RECEIVED);
- free_camera_metadata(request);
- }
-
- virtual void notify(int32_t msg, int32_t ext1, int32_t ext2) {
- dout << "Notify received: msg " << std::hex << msg
- << ", ext1: " << std::hex << ext1 << ", ext2: " << std::hex << ext2
- << std::endl;
- }
-
- Vector<ProEvent> mProEventList;
- Mutex mListenerMutex;
- Mutex mConditionMutex;
- Condition mListenerCondition;
- int mEventMask;
- bool mDropFrames;
-};
-
-class ProCameraTest : public ::testing::Test {
-
-public:
- ProCameraTest() {
- char* displaySecsEnv = getenv("TEST_DISPLAY_SECS");
- if (displaySecsEnv != NULL) {
- mDisplaySecs = atoi(displaySecsEnv);
- if (mDisplaySecs < 0) {
- mDisplaySecs = 0;
- }
- } else {
- mDisplaySecs = 0;
- }
-
- char* displayFmtEnv = getenv("TEST_DISPLAY_FORMAT");
- if (displayFmtEnv != NULL) {
- mDisplayFmt = FormatFromString(displayFmtEnv);
- } else {
- mDisplayFmt = TEST_DISPLAY_FORMAT;
- }
-
- char* displayWidthEnv = getenv("TEST_DISPLAY_WIDTH");
- if (displayWidthEnv != NULL) {
- mDisplayW = atoi(displayWidthEnv);
- if (mDisplayW < 0) {
- mDisplayW = 0;
- }
- } else {
- mDisplayW = TEST_DISPLAY_WIDTH;
- }
-
- char* displayHeightEnv = getenv("TEST_DISPLAY_HEIGHT");
- if (displayHeightEnv != NULL) {
- mDisplayH = atoi(displayHeightEnv);
- if (mDisplayH < 0) {
- mDisplayH = 0;
- }
- } else {
- mDisplayH = TEST_DISPLAY_HEIGHT;
- }
- }
-
- static void SetUpTestCase() {
- // Binder Thread Pool Initialization
- mTestThread = new ProCameraTestThread();
- mTestThread->run("ProCameraTestThread");
- }
-
- virtual void SetUp() {
- mCamera = ProCamera::connect(CAMERA_ID);
- ASSERT_NE((void*)NULL, mCamera.get());
-
- mListener = new ProCameraTestListener();
- mCamera->setListener(mListener);
- }
-
- virtual void TearDown() {
- ASSERT_NE((void*)NULL, mCamera.get());
- mCamera->disconnect();
- }
-
-protected:
- sp<ProCamera> mCamera;
- sp<ProCameraTestListener> mListener;
-
- static sp<Thread> mTestThread;
-
- int mDisplaySecs;
- int mDisplayFmt;
- int mDisplayW;
- int mDisplayH;
-
- sp<SurfaceComposerClient> mComposerClient;
- sp<SurfaceControl> mSurfaceControl;
-
- sp<SurfaceComposerClient> mDepthComposerClient;
- sp<SurfaceControl> mDepthSurfaceControl;
-
- int getSurfaceWidth() {
- return 512;
- }
- int getSurfaceHeight() {
- return 512;
- }
-
- void createOnScreenSurface(sp<Surface>& surface) {
- mComposerClient = new SurfaceComposerClient;
- ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
- mSurfaceControl = mComposerClient->createSurface(
- String8("ProCameraTest StreamingImage Surface"),
- getSurfaceWidth(), getSurfaceHeight(),
- PIXEL_FORMAT_RGB_888, 0);
-
- mSurfaceControl->setPosition(0, 0);
-
- ASSERT_TRUE(mSurfaceControl != NULL);
- ASSERT_TRUE(mSurfaceControl->isValid());
-
- SurfaceComposerClient::openGlobalTransaction();
- ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF));
- ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
- SurfaceComposerClient::closeGlobalTransaction();
-
- sp<ANativeWindow> window = mSurfaceControl->getSurface();
- surface = mSurfaceControl->getSurface();
-
- ASSERT_NE((void*)NULL, surface.get());
- }
-
- void createDepthOnScreenSurface(sp<Surface>& surface) {
- mDepthComposerClient = new SurfaceComposerClient;
- ASSERT_EQ(NO_ERROR, mDepthComposerClient->initCheck());
-
- mDepthSurfaceControl = mDepthComposerClient->createSurface(
- String8("ProCameraTest StreamingImage Surface"),
- getSurfaceWidth(), getSurfaceHeight(),
- PIXEL_FORMAT_RGB_888, 0);
-
- mDepthSurfaceControl->setPosition(640, 0);
-
- ASSERT_TRUE(mDepthSurfaceControl != NULL);
- ASSERT_TRUE(mDepthSurfaceControl->isValid());
-
- SurfaceComposerClient::openGlobalTransaction();
- ASSERT_EQ(NO_ERROR, mDepthSurfaceControl->setLayer(0x7FFFFFFF));
- ASSERT_EQ(NO_ERROR, mDepthSurfaceControl->show());
- SurfaceComposerClient::closeGlobalTransaction();
-
- sp<ANativeWindow> window = mDepthSurfaceControl->getSurface();
- surface = mDepthSurfaceControl->getSurface();
-
- ASSERT_NE((void*)NULL, surface.get());
- }
-
- template <typename T>
- static bool ExistsItem(T needle, T* array, size_t count) {
- if (!array) {
- return false;
- }
-
- for (size_t i = 0; i < count; ++i) {
- if (array[i] == needle) {
- return true;
- }
- }
- return false;
- }
-
-
- static int FormatFromString(const char* str) {
- std::string s(str);
-
-#define CMP_STR(x, y) \
- if (s == #x) return HAL_PIXEL_FORMAT_ ## y;
-#define CMP_STR_SAME(x) CMP_STR(x, x)
-
- CMP_STR_SAME( Y16);
- CMP_STR_SAME( Y8);
- CMP_STR_SAME( YV12);
- CMP_STR(NV16, YCbCr_422_SP);
- CMP_STR(NV21, YCrCb_420_SP);
- CMP_STR(YUY2, YCbCr_422_I);
- CMP_STR(RAW, RAW16);
- CMP_STR(RGBA, RGBA_8888);
-
- std::cerr << "Unknown format string " << str << std::endl;
- return -1;
-
- }
-
- /**
- * Creating a streaming request for these output streams from a template,
- * and submit it
- */
- void createSubmitRequestForStreams(int32_t* streamIds, size_t count, int requestCount=-1) {
-
- ASSERT_NE((void*)NULL, streamIds);
- ASSERT_LT(0u, count);
-
- camera_metadata_t *requestTmp = NULL;
- EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
- /*out*/&requestTmp));
- ASSERT_NE((void*)NULL, requestTmp);
- CameraMetadata request(requestTmp);
-
- // set the output streams. default is empty
-
- uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
- request.update(tag, streamIds, count);
-
- requestTmp = request.release();
-
- if (requestCount < 0) {
- EXPECT_OK(mCamera->submitRequest(requestTmp, /*streaming*/true));
- } else {
- for (int i = 0; i < requestCount; ++i) {
- EXPECT_OK(mCamera->submitRequest(requestTmp,
- /*streaming*/false));
- }
- }
- request.acquire(requestTmp);
- }
-};
-
-sp<Thread> ProCameraTest::mTestThread;
-
-TEST_F(ProCameraTest, AvailableFormats) {
- if (HasFatalFailure()) {
- return;
- }
-
- CameraMetadata staticInfo = mCamera->getCameraInfo(CAMERA_ID);
- ASSERT_FALSE(staticInfo.isEmpty());
-
- uint32_t tag = static_cast<uint32_t>(ANDROID_SCALER_AVAILABLE_FORMATS);
- EXPECT_TRUE(staticInfo.exists(tag));
- camera_metadata_entry_t entry = staticInfo.find(tag);
-
- EXPECT_TRUE(ExistsItem<int32_t>(HAL_PIXEL_FORMAT_YV12,
- entry.data.i32, entry.count));
- EXPECT_TRUE(ExistsItem<int32_t>(HAL_PIXEL_FORMAT_YCrCb_420_SP,
- entry.data.i32, entry.count));
-}
-
-// test around exclusiveTryLock (immediate locking)
-TEST_F(ProCameraTest, LockingImmediate) {
-
- if (HasFatalFailure()) {
- return;
- }
-
- mListener->SetEventMask(ProEvent_Mask(ACQUIRED) |
- ProEvent_Mask(STOLEN) |
- ProEvent_Mask(RELEASED));
-
- EXPECT_FALSE(mCamera->hasExclusiveLock());
- EXPECT_EQ(OK, mCamera->exclusiveTryLock());
- // at this point we definitely have the lock
-
- EXPECT_EQ(OK, mListener->WaitForEvent());
- EXPECT_EQ(ACQUIRED, mListener->ReadEvent());
-
- EXPECT_TRUE(mCamera->hasExclusiveLock());
- EXPECT_EQ(OK, mCamera->exclusiveUnlock());
-
- EXPECT_EQ(OK, mListener->WaitForEvent());
- EXPECT_EQ(RELEASED, mListener->ReadEvent());
-
- EXPECT_FALSE(mCamera->hasExclusiveLock());
-}
-
-// test around exclusiveLock (locking at some future point in time)
-TEST_F(ProCameraTest, LockingAsynchronous) {
-
- if (HasFatalFailure()) {
- return;
- }
-
-
- mListener->SetEventMask(ProEvent_Mask(ACQUIRED) |
- ProEvent_Mask(STOLEN) |
- ProEvent_Mask(RELEASED));
-
- // TODO: Add another procamera that has a lock here.
- // then we can be test that the lock wont immediately be acquired
-
- EXPECT_FALSE(mCamera->hasExclusiveLock());
- EXPECT_EQ(OK, mCamera->exclusiveTryLock());
- // at this point we definitely have the lock
-
- EXPECT_EQ(OK, mListener->WaitForEvent());
- EXPECT_EQ(ACQUIRED, mListener->ReadEvent());
-
- EXPECT_TRUE(mCamera->hasExclusiveLock());
- EXPECT_EQ(OK, mCamera->exclusiveUnlock());
-
- EXPECT_EQ(OK, mListener->WaitForEvent());
- EXPECT_EQ(RELEASED, mListener->ReadEvent());
-
- EXPECT_FALSE(mCamera->hasExclusiveLock());
-}
-
-// Stream directly to the screen.
-TEST_F(ProCameraTest, DISABLED_StreamingImageSingle) {
- if (HasFatalFailure()) {
- return;
- }
-
- sp<Surface> surface;
- if (mDisplaySecs > 0) {
- createOnScreenSurface(/*out*/surface);
- }
- else {
- dout << "Skipping, will not render to screen" << std::endl;
- return;
- }
-
- int depthStreamId = -1;
-
- sp<ServiceListener> listener = new ServiceListener();
- EXPECT_OK(ProCamera::addServiceListener(listener));
-
- ServiceListener::Status currentStatus;
-
- // when subscribing a new listener,
- // we immediately get a callback to the current status
- while (listener->waitForStatusChange(/*out*/currentStatus) != OK);
- EXPECT_EQ(ServiceListener::STATUS_PRESENT, currentStatus);
-
- dout << "Will now stream and resume infinitely..." << std::endl;
- while (true) {
-
- if (currentStatus == ServiceListener::STATUS_PRESENT) {
-
- ASSERT_OK(mCamera->createStream(mDisplayW, mDisplayH, mDisplayFmt,
- surface,
- &depthStreamId));
- EXPECT_NE(-1, depthStreamId);
-
- EXPECT_OK(mCamera->exclusiveTryLock());
-
- int32_t streams[] = { depthStreamId };
- ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(
- streams,
- /*count*/1));
- }
-
- ServiceListener::Status stat = ServiceListener::STATUS_UNKNOWN;
-
- // TODO: maybe check for getch every once in a while?
- while (listener->waitForStatusChange(/*out*/stat) != OK);
-
- if (currentStatus != stat) {
- if (stat == ServiceListener::STATUS_PRESENT) {
- dout << "Reconnecting to camera" << std::endl;
- mCamera = ProCamera::connect(CAMERA_ID);
- } else if (stat == ServiceListener::STATUS_NOT_AVAILABLE) {
- dout << "Disconnecting from camera" << std::endl;
- mCamera->disconnect();
- } else if (stat == ServiceListener::STATUS_NOT_PRESENT) {
- dout << "Camera unplugged" << std::endl;
- mCamera = NULL;
- } else {
- dout << "Unknown status change "
- << std::hex << stat << std::endl;
- }
-
- currentStatus = stat;
- }
- }
-
- EXPECT_OK(ProCamera::removeServiceListener(listener));
- EXPECT_OK(mCamera->deleteStream(depthStreamId));
- EXPECT_OK(mCamera->exclusiveUnlock());
-}
-
-// Stream directly to the screen.
-TEST_F(ProCameraTest, DISABLED_StreamingImageDual) {
- if (HasFatalFailure()) {
- return;
- }
- sp<Surface> surface;
- sp<Surface> depthSurface;
- if (mDisplaySecs > 0) {
- createOnScreenSurface(/*out*/surface);
- createDepthOnScreenSurface(/*out*/depthSurface);
- }
-
- int streamId = -1;
- EXPECT_OK(mCamera->createStream(/*width*/1280, /*height*/960,
- TEST_FORMAT_MAIN, surface, &streamId));
- EXPECT_NE(-1, streamId);
-
- int depthStreamId = -1;
- EXPECT_OK(mCamera->createStream(/*width*/320, /*height*/240,
- TEST_FORMAT_DEPTH, depthSurface, &depthStreamId));
- EXPECT_NE(-1, depthStreamId);
-
- EXPECT_OK(mCamera->exclusiveTryLock());
- /*
- */
- /* iterate in a loop submitting requests every frame.
- * what kind of requests doesnt really matter, just whatever.
- */
-
- // it would probably be better to use CameraMetadata from camera service.
- camera_metadata_t *request = NULL;
- EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
- /*out*/&request));
- EXPECT_NE((void*)NULL, request);
-
- /*FIXME: dont need this later, at which point the above should become an
- ASSERT_NE*/
- if(request == NULL) request = allocate_camera_metadata(10, 100);
-
- // set the output streams to just this stream ID
-
- // wow what a verbose API.
- int32_t allStreams[] = { streamId, depthStreamId };
- // IMPORTANT. bad things will happen if its not a uint8.
- size_t streamCount = sizeof(allStreams) / sizeof(allStreams[0]);
- camera_metadata_entry_t entry;
- uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
- int find = find_camera_metadata_entry(request, tag, &entry);
- if (find == -ENOENT) {
- if (add_camera_metadata_entry(request, tag, &allStreams,
- /*data_count*/streamCount) != OK) {
- camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
- ASSERT_OK(append_camera_metadata(tmp, request));
- free_camera_metadata(request);
- request = tmp;
-
- ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
- /*data_count*/streamCount));
- }
- } else {
- ASSERT_OK(update_camera_metadata_entry(request, entry.index,
- &allStreams, /*data_count*/streamCount, &entry));
- }
-
- EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
-
- dout << "will sleep now for " << mDisplaySecs << std::endl;
- sleep(mDisplaySecs);
-
- free_camera_metadata(request);
-
- for (size_t i = 0; i < streamCount; ++i) {
- EXPECT_OK(mCamera->deleteStream(allStreams[i]));
- }
- EXPECT_OK(mCamera->exclusiveUnlock());
-}
-
-TEST_F(ProCameraTest, CpuConsumerSingle) {
- if (HasFatalFailure()) {
- return;
- }
-
- mListener->SetEventMask(ProEvent_Mask(ACQUIRED) |
- ProEvent_Mask(STOLEN) |
- ProEvent_Mask(RELEASED) |
- ProEvent_Mask(FRAME_RECEIVED));
- mListener->SetDropFrames(true);
-
- int streamId = -1;
- sp<CpuConsumer> consumer;
- EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
- TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
- EXPECT_NE(-1, streamId);
-
- EXPECT_OK(mCamera->exclusiveTryLock());
- EXPECT_EQ(OK, mListener->WaitForEvent());
- EXPECT_EQ(ACQUIRED, mListener->ReadEvent());
- /* iterate in a loop submitting requests every frame.
- * what kind of requests doesnt really matter, just whatever.
- */
-
- // it would probably be better to use CameraMetadata from camera service.
- camera_metadata_t *request = NULL;
- EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
- /*out*/&request));
- EXPECT_NE((void*)NULL, request);
-
- /*FIXME: dont need this later, at which point the above should become an
- ASSERT_NE*/
- if(request == NULL) request = allocate_camera_metadata(10, 100);
-
- // set the output streams to just this stream ID
-
- int32_t allStreams[] = { streamId };
- camera_metadata_entry_t entry;
- uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
- int find = find_camera_metadata_entry(request, tag, &entry);
- if (find == -ENOENT) {
- if (add_camera_metadata_entry(request, tag, &allStreams,
- /*data_count*/1) != OK) {
- camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
- ASSERT_OK(append_camera_metadata(tmp, request));
- free_camera_metadata(request);
- request = tmp;
-
- ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
- /*data_count*/1));
- }
- } else {
- ASSERT_OK(update_camera_metadata_entry(request, entry.index,
- &allStreams, /*data_count*/1, &entry));
- }
-
- EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
-
- // Consume a couple of frames
- for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
- EXPECT_EQ(OK, mListener->WaitForEvent());
- EXPECT_EQ(FRAME_RECEIVED, mListener->ReadEvent());
- }
-
- // Done: clean up
- free_camera_metadata(request);
- EXPECT_OK(mCamera->deleteStream(streamId));
- EXPECT_OK(mCamera->exclusiveUnlock());
-}
-
-TEST_F(ProCameraTest, CpuConsumerDual) {
- if (HasFatalFailure()) {
- return;
- }
-
- mListener->SetEventMask(ProEvent_Mask(FRAME_RECEIVED));
- mListener->SetDropFrames(true);
-
- int streamId = -1;
- sp<CpuConsumer> consumer;
- EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
- TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
- EXPECT_NE(-1, streamId);
-
- int depthStreamId = -1;
- EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
- TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &consumer, &depthStreamId));
- EXPECT_NE(-1, depthStreamId);
-
- EXPECT_OK(mCamera->exclusiveTryLock());
- /*
- */
- /* iterate in a loop submitting requests every frame.
- * what kind of requests doesnt really matter, just whatever.
- */
-
- // it would probably be better to use CameraMetadata from camera service.
- camera_metadata_t *request = NULL;
- EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
- /*out*/&request));
- EXPECT_NE((void*)NULL, request);
-
- if(request == NULL) request = allocate_camera_metadata(10, 100);
-
- // set the output streams to just this stream ID
-
- // wow what a verbose API.
- int32_t allStreams[] = { streamId, depthStreamId };
- size_t streamCount = 2;
- camera_metadata_entry_t entry;
- uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
- int find = find_camera_metadata_entry(request, tag, &entry);
- if (find == -ENOENT) {
- if (add_camera_metadata_entry(request, tag, &allStreams,
- /*data_count*/streamCount) != OK) {
- camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
- ASSERT_OK(append_camera_metadata(tmp, request));
- free_camera_metadata(request);
- request = tmp;
-
- ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
- /*data_count*/streamCount));
- }
- } else {
- ASSERT_OK(update_camera_metadata_entry(request, entry.index,
- &allStreams, /*data_count*/streamCount, &entry));
- }
-
- EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
-
- // Consume a couple of frames
- for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
- // stream id 1
- EXPECT_EQ(OK, mListener->WaitForEvent());
- EXPECT_EQ(FRAME_RECEIVED, mListener->ReadEvent());
-
- // stream id 2
- EXPECT_EQ(OK, mListener->WaitForEvent());
- EXPECT_EQ(FRAME_RECEIVED, mListener->ReadEvent());
-
- //TODO: events should be a struct with some data like the stream id
- }
-
- // Done: clean up
- free_camera_metadata(request);
- EXPECT_OK(mCamera->deleteStream(streamId));
- EXPECT_OK(mCamera->exclusiveUnlock());
-}
-
-TEST_F(ProCameraTest, ResultReceiver) {
- if (HasFatalFailure()) {
- return;
- }
-
- mListener->SetEventMask(ProEvent_Mask(RESULT_RECEIVED));
- mListener->SetDropFrames(true);
- //FIXME: if this is run right after the previous test we get FRAME_RECEIVED
- // need to filter out events at read time
-
- int streamId = -1;
- sp<CpuConsumer> consumer;
- EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
- TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
- EXPECT_NE(-1, streamId);
-
- EXPECT_OK(mCamera->exclusiveTryLock());
- /*
- */
- /* iterate in a loop submitting requests every frame.
- * what kind of requests doesnt really matter, just whatever.
- */
-
- camera_metadata_t *request = NULL;
- EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
- /*out*/&request));
- EXPECT_NE((void*)NULL, request);
-
- /*FIXME*/
- if(request == NULL) request = allocate_camera_metadata(10, 100);
-
- // set the output streams to just this stream ID
-
- int32_t allStreams[] = { streamId };
- size_t streamCount = 1;
- camera_metadata_entry_t entry;
- uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
- int find = find_camera_metadata_entry(request, tag, &entry);
- if (find == -ENOENT) {
- if (add_camera_metadata_entry(request, tag, &allStreams,
- /*data_count*/streamCount) != OK) {
- camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
- ASSERT_OK(append_camera_metadata(tmp, request));
- free_camera_metadata(request);
- request = tmp;
-
- ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
- /*data_count*/streamCount));
- }
- } else {
- ASSERT_OK(update_camera_metadata_entry(request, entry.index,
- &allStreams, /*data_count*/streamCount, &entry));
- }
-
- EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
-
- // Consume a couple of results
- for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
- EXPECT_EQ(OK, mListener->WaitForEvent());
- EXPECT_EQ(RESULT_RECEIVED, mListener->ReadEvent());
- }
-
- // Done: clean up
- free_camera_metadata(request);
- EXPECT_OK(mCamera->deleteStream(streamId));
- EXPECT_OK(mCamera->exclusiveUnlock());
-}
-
-// FIXME: This is racy and sometimes fails on waitForFrameMetadata
-TEST_F(ProCameraTest, DISABLED_WaitForResult) {
- if (HasFatalFailure()) {
- return;
- }
-
- mListener->SetDropFrames(true);
-
- int streamId = -1;
- sp<CpuConsumer> consumer;
- EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
- TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
- EXPECT_NE(-1, streamId);
-
- EXPECT_OK(mCamera->exclusiveTryLock());
-
- int32_t streams[] = { streamId };
- ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1));
-
- // Consume a couple of results
- for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
- EXPECT_OK(mCamera->waitForFrameMetadata());
- CameraMetadata meta = mCamera->consumeFrameMetadata();
- EXPECT_FALSE(meta.isEmpty());
- }
-
- // Done: clean up
- EXPECT_OK(mCamera->deleteStream(streamId));
- EXPECT_OK(mCamera->exclusiveUnlock());
-}
-
-TEST_F(ProCameraTest, WaitForSingleStreamBuffer) {
- if (HasFatalFailure()) {
- return;
- }
-
- int streamId = -1;
- sp<CpuConsumer> consumer;
- EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
- TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
- EXPECT_NE(-1, streamId);
-
- EXPECT_OK(mCamera->exclusiveTryLock());
-
- int32_t streams[] = { streamId };
- ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
- /*requests*/TEST_CPU_FRAME_COUNT));
-
- // Consume a couple of results
- for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
- EXPECT_EQ(1, mCamera->waitForFrameBuffer(streamId));
-
- CpuConsumer::LockedBuffer buf;
- EXPECT_OK(consumer->lockNextBuffer(&buf));
-
- dout << "Buffer synchronously received on streamId = " << streamId <<
- ", dataPtr = " << (void*)buf.data <<
- ", timestamp = " << buf.timestamp << std::endl;
-
- EXPECT_OK(consumer->unlockBuffer(buf));
- }
-
- // Done: clean up
- EXPECT_OK(mCamera->deleteStream(streamId));
- EXPECT_OK(mCamera->exclusiveUnlock());
-}
-
-// FIXME: This is racy and sometimes fails on waitForFrameMetadata
-TEST_F(ProCameraTest, DISABLED_WaitForDualStreamBuffer) {
- if (HasFatalFailure()) {
- return;
- }
-
- const int REQUEST_COUNT = TEST_CPU_FRAME_COUNT * 10;
-
- // 15 fps
- int streamId = -1;
- sp<CpuConsumer> consumer;
- EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
- TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
- EXPECT_NE(-1, streamId);
-
- // 30 fps
- int depthStreamId = -1;
- sp<CpuConsumer> depthConsumer;
- EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
- TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &depthConsumer, &depthStreamId));
- EXPECT_NE(-1, depthStreamId);
-
- EXPECT_OK(mCamera->exclusiveTryLock());
-
- int32_t streams[] = { streamId, depthStreamId };
- ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/2,
- /*requests*/REQUEST_COUNT));
-
- int depthFrames = 0;
- int greyFrames = 0;
-
- // Consume two frames simultaneously. Unsynchronized by timestamps.
- for (int i = 0; i < REQUEST_COUNT; ++i) {
-
- // Exhaust event queue so it doesn't keep growing
- while (mListener->ReadEvent() != UNKNOWN);
-
- // Get the metadata
- EXPECT_OK(mCamera->waitForFrameMetadata());
- CameraMetadata meta = mCamera->consumeFrameMetadata();
- EXPECT_FALSE(meta.isEmpty());
-
- // Get the buffers
-
- EXPECT_EQ(1, mCamera->waitForFrameBuffer(depthStreamId));
-
- /**
- * Guaranteed to be able to consume the depth frame,
- * since we waited on it.
- */
- CpuConsumer::LockedBuffer depthBuffer;
- EXPECT_OK(depthConsumer->lockNextBuffer(&depthBuffer));
-
- dout << "Depth Buffer synchronously received on streamId = " <<
- streamId <<
- ", dataPtr = " << (void*)depthBuffer.data <<
- ", timestamp = " << depthBuffer.timestamp << std::endl;
-
- EXPECT_OK(depthConsumer->unlockBuffer(depthBuffer));
-
- depthFrames++;
-
-
- /** Consume Greyscale frames if there are any.
- * There may not be since it runs at half FPS */
- CpuConsumer::LockedBuffer greyBuffer;
- while (consumer->lockNextBuffer(&greyBuffer) == OK) {
-
- dout << "GRAY Buffer synchronously received on streamId = " <<
- streamId <<
- ", dataPtr = " << (void*)greyBuffer.data <<
- ", timestamp = " << greyBuffer.timestamp << std::endl;
-
- EXPECT_OK(consumer->unlockBuffer(greyBuffer));
-
- greyFrames++;
- }
- }
-
- dout << "Done, summary: depth frames " << std::dec << depthFrames
- << ", grey frames " << std::dec << greyFrames << std::endl;
-
- // Done: clean up
- EXPECT_OK(mCamera->deleteStream(streamId));
- EXPECT_OK(mCamera->exclusiveUnlock());
-}
-
-TEST_F(ProCameraTest, WaitForSingleStreamBufferAndDropFramesSync) {
- if (HasFatalFailure()) {
- return;
- }
-
- const int NUM_REQUESTS = 20 * TEST_CPU_FRAME_COUNT;
-
- int streamId = -1;
- sp<CpuConsumer> consumer;
- EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
- TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT,
- /*synchronousMode*/true, &consumer, &streamId));
- EXPECT_NE(-1, streamId);
-
- EXPECT_OK(mCamera->exclusiveTryLock());
-
- int32_t streams[] = { streamId };
- ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
- /*requests*/NUM_REQUESTS));
-
- // Consume a couple of results
- for (int i = 0; i < NUM_REQUESTS; ++i) {
- int numFrames;
- EXPECT_TRUE((numFrames = mCamera->waitForFrameBuffer(streamId)) > 0);
-
- // Drop all but the newest framebuffer
- EXPECT_EQ(numFrames-1, mCamera->dropFrameBuffer(streamId, numFrames-1));
-
- dout << "Dropped " << (numFrames - 1) << " frames" << std::endl;
-
- // Skip the counter ahead, don't try to consume these frames again
- i += numFrames-1;
-
- // "Consume" the buffer
- CpuConsumer::LockedBuffer buf;
- EXPECT_OK(consumer->lockNextBuffer(&buf));
-
- dout << "Buffer synchronously received on streamId = " << streamId <<
- ", dataPtr = " << (void*)buf.data <<
- ", timestamp = " << buf.timestamp << std::endl;
-
- // Process at 10fps, stream is at 15fps.
- // This means we will definitely fill up the buffer queue with
- // extra buffers and need to drop them.
- usleep(TEST_FRAME_PROCESSING_DELAY_US);
-
- EXPECT_OK(consumer->unlockBuffer(buf));
- }
-
- // Done: clean up
- EXPECT_OK(mCamera->deleteStream(streamId));
- EXPECT_OK(mCamera->exclusiveUnlock());
-}
-
-TEST_F(ProCameraTest, WaitForSingleStreamBufferAndDropFramesAsync) {
- if (HasFatalFailure()) {
- return;
- }
-
- const int NUM_REQUESTS = 20 * TEST_CPU_FRAME_COUNT;
-
- int streamId = -1;
- sp<CpuConsumer> consumer;
- EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
- TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT,
- /*synchronousMode*/false, &consumer, &streamId));
- EXPECT_NE(-1, streamId);
-
- EXPECT_OK(mCamera->exclusiveTryLock());
-
- int32_t streams[] = { streamId };
- ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
- /*requests*/NUM_REQUESTS));
-
- uint64_t lastFrameNumber = 0;
- int numFrames;
-
- // Consume a couple of results
- int i;
- for (i = 0; i < NUM_REQUESTS && lastFrameNumber < NUM_REQUESTS; ++i) {
- EXPECT_LT(0, (numFrames = mCamera->waitForFrameBuffer(streamId)));
-
- dout << "Dropped " << (numFrames - 1) << " frames" << std::endl;
-
- // Skip the counter ahead, don't try to consume these frames again
- i += numFrames-1;
-
- // "Consume" the buffer
- CpuConsumer::LockedBuffer buf;
-
- EXPECT_EQ(OK, consumer->lockNextBuffer(&buf));
-
- lastFrameNumber = buf.frameNumber;
-
- dout << "Buffer asynchronously received on streamId = " << streamId <<
- ", dataPtr = " << (void*)buf.data <<
- ", timestamp = " << buf.timestamp <<
- ", framenumber = " << buf.frameNumber << std::endl;
-
- // Process at 10fps, stream is at 15fps.
- // This means we will definitely fill up the buffer queue with
- // extra buffers and need to drop them.
- usleep(TEST_FRAME_PROCESSING_DELAY_US);
-
- EXPECT_OK(consumer->unlockBuffer(buf));
- }
-
- dout << "Done after " << i << " iterations " << std::endl;
-
- // Done: clean up
- EXPECT_OK(mCamera->deleteStream(streamId));
- EXPECT_OK(mCamera->exclusiveUnlock());
-}
-
-
-
-//TODO: refactor into separate file
-TEST_F(ProCameraTest, ServiceListenersSubscribe) {
-
- ASSERT_EQ(4u, sizeof(ServiceListener::Status));
-
- sp<ServiceListener> listener = new ServiceListener();
-
- EXPECT_EQ(BAD_VALUE, ProCamera::removeServiceListener(listener));
- EXPECT_OK(ProCamera::addServiceListener(listener));
-
- EXPECT_EQ(ALREADY_EXISTS, ProCamera::addServiceListener(listener));
- EXPECT_OK(ProCamera::removeServiceListener(listener));
-
- EXPECT_EQ(BAD_VALUE, ProCamera::removeServiceListener(listener));
-}
-
-//TODO: refactor into separate file
-TEST_F(ProCameraTest, ServiceListenersFunctional) {
-
- sp<ServiceListener> listener = new ServiceListener();
-
- EXPECT_OK(ProCamera::addServiceListener(listener));
-
- sp<Camera> cam = Camera::connect(CAMERA_ID,
- /*clientPackageName*/String16(),
- -1);
- EXPECT_NE((void*)NULL, cam.get());
-
- ServiceListener::Status stat = ServiceListener::STATUS_UNKNOWN;
- EXPECT_OK(listener->waitForStatusChange(/*out*/stat));
-
- EXPECT_EQ(ServiceListener::STATUS_NOT_AVAILABLE, stat);
-
- if (cam.get()) {
- cam->disconnect();
- }
-
- EXPECT_OK(listener->waitForStatusChange(/*out*/stat));
- EXPECT_EQ(ServiceListener::STATUS_PRESENT, stat);
-
- EXPECT_OK(ProCamera::removeServiceListener(listener));
-}
-
-
-
-}
-}
-}
-}
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index c6074fc..ba33ffe 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -108,6 +108,9 @@
*/
void getSupportedPreviewFormats(Vector<int>& formats) const;
+ // Returns true if no keys are present
+ bool isEmpty() const;
+
// Parameter keys to communicate between camera application and driver.
// The access (read/write, read only, or write only) is viewed from the
// perspective of applications, not driver.
diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h
index 194a646..c8d3d19 100644
--- a/include/camera/ICameraService.h
+++ b/include/camera/ICameraService.h
@@ -25,8 +25,6 @@
class ICamera;
class ICameraClient;
-class IProCameraUser;
-class IProCameraCallbacks;
class ICameraServiceListener;
class ICameraDeviceUser;
class ICameraDeviceCallbacks;
@@ -44,7 +42,6 @@
GET_NUMBER_OF_CAMERAS = IBinder::FIRST_CALL_TRANSACTION,
GET_CAMERA_INFO,
CONNECT,
- CONNECT_PRO,
CONNECT_DEVICE,
ADD_LISTENER,
REMOVE_LISTENER,
@@ -105,13 +102,6 @@
/*out*/
sp<ICamera>& device) = 0;
- virtual status_t connectPro(const sp<IProCameraCallbacks>& cameraCb,
- int cameraId,
- const String16& clientPackageName,
- int clientUid,
- /*out*/
- sp<IProCameraUser>& device) = 0;
-
virtual status_t connectDevice(
const sp<ICameraDeviceCallbacks>& cameraCb,
int cameraId,
diff --git a/include/camera/IProCameraCallbacks.h b/include/camera/IProCameraCallbacks.h
deleted file mode 100644
index e8abb89..0000000
--- a/include/camera/IProCameraCallbacks.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_HARDWARE_IPROCAMERA_CALLBACKS_H
-#define ANDROID_HARDWARE_IPROCAMERA_CALLBACKS_H
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-#include <binder/IMemory.h>
-#include <utils/Timers.h>
-#include <system/camera.h>
-
-struct camera_metadata;
-
-namespace android {
-
-class IProCameraCallbacks : public IInterface
-{
- /**
- * Keep up-to-date with IProCameraCallbacks.aidl in frameworks/base
- */
-public:
- DECLARE_META_INTERFACE(ProCameraCallbacks);
-
- virtual void notifyCallback(int32_t msgType,
- int32_t ext1,
- int32_t ext2) = 0;
-
- enum LockStatus {
- LOCK_ACQUIRED,
- LOCK_RELEASED,
- LOCK_STOLEN,
- };
-
- virtual void onLockStatusChanged(LockStatus newLockStatus) = 0;
-
- /** Missing by design: implementation is client-side in ProCamera.cpp **/
- // virtual void onBufferReceived(int streamId,
- // const CpuConsumer::LockedBufer& buf);
- virtual void onResultReceived(int32_t requestId,
- camera_metadata* result) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnProCameraCallbacks : public BnInterface<IProCameraCallbacks>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-}; // namespace android
-
-#endif
diff --git a/include/camera/IProCameraUser.h b/include/camera/IProCameraUser.h
deleted file mode 100644
index 2ccc4d2..0000000
--- a/include/camera/IProCameraUser.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_HARDWARE_IPROCAMERAUSER_H
-#define ANDROID_HARDWARE_IPROCAMERAUSER_H
-
-#include <utils/RefBase.h>
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-#include <binder/IMemory.h>
-#include <utils/String8.h>
-#include <camera/IProCameraCallbacks.h>
-
-struct camera_metadata;
-
-namespace android {
-
-class IProCameraUserClient;
-class IGraphicBufferProducer;
-class Surface;
-
-class IProCameraUser: public IInterface
-{
- /**
- * Keep up-to-date with IProCameraUser.aidl in frameworks/base
- */
-public:
- DECLARE_META_INTERFACE(ProCameraUser);
-
- virtual void disconnect() = 0;
-
- // connect to the service, given a callbacks listener
- virtual status_t connect(const sp<IProCameraCallbacks>& callbacks)
- = 0;
-
- /**
- * Locking
- **/
- virtual status_t exclusiveTryLock() = 0;
- virtual status_t exclusiveLock() = 0;
- virtual status_t exclusiveUnlock() = 0;
-
- virtual bool hasExclusiveLock() = 0;
-
- /**
- * Request Handling
- **/
-
- // Note that the callee gets a copy of the metadata.
- virtual int submitRequest(struct camera_metadata* metadata,
- bool streaming = false) = 0;
- virtual status_t cancelRequest(int requestId) = 0;
-
- virtual status_t deleteStream(int streamId) = 0;
- virtual status_t createStream(
- int width, int height, int format,
- const sp<IGraphicBufferProducer>& bufferProducer,
- /*out*/
- int* streamId) = 0;
-
- // Create a request object from a template.
- virtual status_t createDefaultRequest(int templateId,
- /*out*/
- camera_metadata** request)
- = 0;
-
- // Get static camera metadata
- virtual status_t getCameraInfo(int cameraId,
- /*out*/
- camera_metadata** info) = 0;
-
-};
-
-// ----------------------------------------------------------------------------
-
-class BnProCameraUser: public BnInterface<IProCameraUser>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-}; // namespace android
-
-#endif
diff --git a/include/camera/ProCamera.h b/include/camera/ProCamera.h
deleted file mode 100644
index e9b687a..0000000
--- a/include/camera/ProCamera.h
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_HARDWARE_PRO_CAMERA_H
-#define ANDROID_HARDWARE_PRO_CAMERA_H
-
-#include <utils/Timers.h>
-#include <utils/KeyedVector.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <system/camera.h>
-#include <camera/IProCameraCallbacks.h>
-#include <camera/IProCameraUser.h>
-#include <camera/Camera.h>
-#include <camera/CameraMetadata.h>
-#include <camera/ICameraService.h>
-#include <gui/CpuConsumer.h>
-
-#include <gui/Surface.h>
-
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
-
-#include <camera/CameraBase.h>
-
-struct camera_metadata;
-
-namespace android {
-
-// All callbacks on this class are concurrent
-// (they come from separate threads)
-class ProCameraListener : virtual public RefBase
-{
-public:
- virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0;
-
- // Lock has been acquired. Write operations now available.
- virtual void onLockAcquired() = 0;
- // Lock has been released with exclusiveUnlock.
- virtual void onLockReleased() = 0;
- // Lock has been stolen by another client.
- virtual void onLockStolen() = 0;
-
- // Lock free.
- virtual void onTriggerNotify(int32_t msgType, int32_t ext1, int32_t ext2)
- = 0;
- // onFrameAvailable and OnResultReceived can come in with any order,
- // use android.sensor.timestamp and LockedBuffer.timestamp to correlate them
-
- /**
- * A new metadata buffer has been received.
- * -- Ownership of request passes on to the callee, free with
- * free_camera_metadata.
- */
- virtual void onResultReceived(int32_t frameId, camera_metadata* result) = 0;
-
- // TODO: make onFrameAvailable pure virtual
-
- // A new frame buffer has been received for this stream.
- // -- This callback only fires for createStreamCpu streams
- // -- A buffer may be obtained by calling cpuConsumer->lockNextBuffer
- // -- Use buf.timestamp to correlate with result's android.sensor.timestamp
- // -- The buffer should be accessed with CpuConsumer::lockNextBuffer
- // and CpuConsumer::unlockBuffer
- virtual void onFrameAvailable(int /*streamId*/,
- const sp<CpuConsumer>& /*cpuConsumer*/) {
- }
-
-};
-
-class ProCamera;
-
-template <>
-struct CameraTraits<ProCamera>
-{
- typedef ProCameraListener TCamListener;
- typedef IProCameraUser TCamUser;
- typedef IProCameraCallbacks TCamCallbacks;
- typedef status_t (ICameraService::*TCamConnectService)(const sp<IProCameraCallbacks>&,
- int, const String16&, int,
- /*out*/
- sp<IProCameraUser>&);
- static TCamConnectService fnConnectService;
-};
-
-
-class ProCamera :
- public CameraBase<ProCamera>,
- public BnProCameraCallbacks
-{
-public:
- /**
- * Connect a shared camera. By default access is restricted to read only
- * (Lock free) operations. To be able to submit custom requests a lock needs
- * to be acquired with exclusive[Try]Lock.
- */
- static sp<ProCamera> connect(int cameraId);
- virtual ~ProCamera();
-
- /**
- * Exclusive Locks:
- * - We may request exclusive access to a camera if no other
- * clients are using the camera. This works as a traditional
- * client, writing/reading any camera state.
- * - An application opening the camera (a regular 'Camera') will
- * always steal away the exclusive lock from a ProCamera,
- * this will call onLockReleased.
- * - onLockAcquired will be called again once it is possible
- * to again exclusively lock the camera.
- *
- */
-
- /**
- * All exclusiveLock/unlock functions are asynchronous. The remote endpoint
- * shall not block while waiting to acquire the lock. Instead the lock
- * notifications will come in asynchronously on the listener.
- */
-
- /**
- * Attempt to acquire the lock instantly (non-blocking)
- * - If this succeeds, you do not need to wait for onLockAcquired
- * but the event will still be fired
- *
- * Returns -EBUSY if already locked. 0 on success.
- */
- status_t exclusiveTryLock();
- // always returns 0. wait for onLockAcquired before lock is acquired.
- status_t exclusiveLock();
- // release a lock if we have one, or cancel the lock request.
- status_t exclusiveUnlock();
-
- // exclusive lock = do whatever we want. no lock = read only.
- bool hasExclusiveLock();
-
- /**
- * < 0 error, >= 0 the request ID. streaming to have the request repeat
- * until cancelled.
- * The request queue is flushed when a lock is released or stolen
- * if not locked will return PERMISSION_DENIED
- */
- int submitRequest(const struct camera_metadata* metadata,
- bool streaming = false);
- // if not locked will return PERMISSION_DENIED, BAD_VALUE if requestId bad
- status_t cancelRequest(int requestId);
-
- /**
- * Ask for a stream to be enabled.
- * Lock free. Service maintains counter of streams.
- */
- status_t requestStream(int streamId);
-// TODO: remove requestStream, its useless.
-
- /**
- * Delete a stream.
- * Lock free.
- *
- * NOTE: As a side effect this cancels ALL streaming requests.
- *
- * Errors: BAD_VALUE if unknown stream ID.
- * PERMISSION_DENIED if the stream wasn't yours
- */
- status_t deleteStream(int streamId);
-
- /**
- * Create a new HW stream, whose sink will be the window.
- * Lock free. Service maintains counter of streams.
- * Errors: -EBUSY if too many streams created
- */
- status_t createStream(int width, int height, int format,
- const sp<Surface>& surface,
- /*out*/
- int* streamId);
-
- /**
- * Create a new HW stream, whose sink will be the SurfaceTexture.
- * Lock free. Service maintains counter of streams.
- * Errors: -EBUSY if too many streams created
- */
- status_t createStream(int width, int height, int format,
- const sp<IGraphicBufferProducer>& bufferProducer,
- /*out*/
- int* streamId);
- status_t createStreamCpu(int width, int height, int format,
- int heapCount,
- /*out*/
- sp<CpuConsumer>* cpuConsumer,
- int* streamId);
- status_t createStreamCpu(int width, int height, int format,
- int heapCount,
- bool synchronousMode,
- /*out*/
- sp<CpuConsumer>* cpuConsumer,
- int* streamId);
-
- // Create a request object from a template.
- status_t createDefaultRequest(int templateId,
- /*out*/
- camera_metadata** request) const;
-
- // Get static camera metadata
- camera_metadata* getCameraInfo(int cameraId);
-
- // Blocks until a frame is available (CPU streams only)
- // - Obtain the frame data by calling CpuConsumer::lockNextBuffer
- // - Release the frame data after use with CpuConsumer::unlockBuffer
- // Return value:
- // - >0 - number of frames available to be locked
- // - <0 - error (refer to error codes)
- // Error codes:
- // -ETIMEDOUT if it took too long to get a frame
- int waitForFrameBuffer(int streamId);
-
- // Blocks until a metadata result is available
- // - Obtain the metadata by calling consumeFrameMetadata()
- // Error codes:
- // -ETIMEDOUT if it took too long to get a frame
- status_t waitForFrameMetadata();
-
- // Get the latest metadata. This is destructive.
- // - Calling this repeatedly will produce empty metadata objects.
- // - Use waitForFrameMetadata to sync until new data is available.
- CameraMetadata consumeFrameMetadata();
-
- // Convenience method to drop frame buffers (CPU streams only)
- // Return values:
- // >=0 - number of frames dropped (up to count)
- // <0 - error code
- // Error codes:
- // BAD_VALUE - invalid streamId or count passed
- int dropFrameBuffer(int streamId, int count);
-
-protected:
- ////////////////////////////////////////////////////////
- // IProCameraCallbacks implementation
- ////////////////////////////////////////////////////////
- virtual void notifyCallback(int32_t msgType,
- int32_t ext,
- int32_t ext2);
-
- virtual void onLockStatusChanged(
- IProCameraCallbacks::LockStatus newLockStatus);
-
- virtual void onResultReceived(int32_t requestId,
- camera_metadata* result);
-private:
- ProCamera(int cameraId);
-
- class ProFrameListener : public CpuConsumer::FrameAvailableListener {
- public:
- ProFrameListener(wp<ProCamera> camera, int streamID) {
- mCamera = camera;
- mStreamId = streamID;
- }
-
- protected:
- virtual void onFrameAvailable(const BufferItem& /* item */) {
- sp<ProCamera> c = mCamera.promote();
- if (c.get() != NULL) {
- c->onFrameAvailable(mStreamId);
- }
- }
-
- private:
- wp<ProCamera> mCamera;
- int mStreamId;
- };
- friend class ProFrameListener;
-
- struct StreamInfo
- {
- StreamInfo(int streamId) {
- this->streamID = streamId;
- cpuStream = false;
- frameReady = 0;
- }
-
- StreamInfo() {
- streamID = -1;
- cpuStream = false;
- }
-
- int streamID;
- bool cpuStream;
- sp<CpuConsumer> cpuConsumer;
- bool synchronousMode;
- sp<ProFrameListener> frameAvailableListener;
- sp<Surface> stc;
- int frameReady;
- };
-
- Condition mWaitCondition;
- Mutex mWaitMutex;
- static const nsecs_t mWaitTimeout = 1000000000; // 1sec
- KeyedVector<int, StreamInfo> mStreams;
- bool mMetadataReady;
- CameraMetadata mLatestMetadata;
-
- void onFrameAvailable(int streamId);
-
- StreamInfo& getStreamInfo(int streamId);
-
- friend class CameraBase;
-};
-
-}; // namespace android
-
-#endif
diff --git a/include/camera/camera2/ICameraDeviceUser.h b/include/camera/camera2/ICameraDeviceUser.h
index bfc2aa0..e9f1f5a 100644
--- a/include/camera/camera2/ICameraDeviceUser.h
+++ b/include/camera/camera2/ICameraDeviceUser.h
@@ -27,9 +27,9 @@
class ICameraDeviceUserClient;
class IGraphicBufferProducer;
-class Surface;
class CaptureRequest;
class CameraMetadata;
+class OutputConfiguration;
enum {
NO_IN_FLIGHT_REPEATING_FRAMES = -1,
@@ -100,8 +100,8 @@
virtual status_t endConfigure() = 0;
virtual status_t deleteStream(int streamId) = 0;
- virtual status_t createStream(
- const sp<IGraphicBufferProducer>& bufferProducer) = 0;
+
+ virtual status_t createStream(const OutputConfiguration& outputConfiguration) = 0;
// Create a request object from a template.
virtual status_t createDefaultRequest(int templateId,
diff --git a/include/camera/camera2/OutputConfiguration.h b/include/camera/camera2/OutputConfiguration.h
new file mode 100644
index 0000000..e6b679f
--- /dev/null
+++ b/include/camera/camera2/OutputConfiguration.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAMERA2_OUTPUTCONFIGURATION_H
+#define ANDROID_HARDWARE_CAMERA2_OUTPUTCONFIGURATION_H
+
+#include <utils/RefBase.h>
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+class Surface;
+
+class OutputConfiguration : public virtual RefBase {
+public:
+
+ static const int INVALID_ROTATION;
+ sp<IGraphicBufferProducer> getGraphicBufferProducer() const;
+ int getRotation() const;
+
+ /**
+ * Keep impl up-to-date with OutputConfiguration.java in frameworks/base
+ */
+ status_t writeToParcel(Parcel& parcel) const;
+ // getGraphicBufferProducer will be NULL if error occurred
+ // getRotation will be INVALID_ROTATION if error occurred
+ OutputConfiguration(const Parcel& parcel);
+
+private:
+ sp<IGraphicBufferProducer> mGbp;
+ int mRotation;
+
+ // helper function
+ static String16 readMaybeEmptyString16(const Parcel& parcel);
+};
+}; // namespace android
+
+#endif
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index f70d981..c503f25 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -42,8 +42,7 @@
EVENT_MORE_DATA = 0, // Request to read available data from buffer.
// If this event is delivered but the callback handler
// does not want to read the available data, the handler must
- // explicitly
- // ignore the event by setting frameCount to zero.
+ // explicitly ignore the event by setting frameCount to zero.
EVENT_OVERRUN = 1, // Buffer overrun occurred.
EVENT_MARKER = 2, // Record head is at the specified marker position
// (See setMarkerPosition()).
@@ -53,7 +52,7 @@
// voluntary invalidation by mediaserver, or mediaserver crash.
};
- /* Client should declare Buffer on the stack and pass address to obtainBuffer()
+ /* Client should declare a Buffer and pass address to obtainBuffer()
* and releaseBuffer(). See also callback_t for EVENT_MORE_DATA.
*/
@@ -62,20 +61,25 @@
public:
// FIXME use m prefix
size_t frameCount; // number of sample frames corresponding to size;
- // on input it is the number of frames available,
- // on output is the number of frames actually drained
- // (currently ignored but will make the primary field in future)
+ // on input to obtainBuffer() it is the number of frames desired
+ // on output from obtainBuffer() it is the number of available
+ // frames to be read
+ // on input to releaseBuffer() it is currently ignored
size_t size; // input/output in bytes == frameCount * frameSize
- // on output is the number of bytes actually drained
- // FIXME this is redundant with respect to frameCount,
- // and TRANSFER_OBTAIN mode is broken for 8-bit data
- // since we don't define the frame format
+ // on input to obtainBuffer() it is ignored
+ // on output from obtainBuffer() it is the number of available
+ // bytes to be read, which is frameCount * frameSize
+ // on input to releaseBuffer() it is the number of bytes to
+ // release
+ // FIXME This is redundant with respect to frameCount. Consider
+ // removing size and making frameCount the primary field.
union {
void* raw;
short* i16; // signed 16-bit
int8_t* i8; // unsigned 8-bit, offset by 0x80
+ // input to obtainBuffer(): unused, output: pointer to buffer
};
};
@@ -88,8 +92,8 @@
* user: Pointer to context for use by the callback receiver.
* info: Pointer to optional parameter according to event type:
* - EVENT_MORE_DATA: pointer to AudioRecord::Buffer struct. The callback must not read
- * more bytes than indicated by 'size' field and update 'size' if fewer bytes are
- * consumed.
+ * more bytes than indicated by 'size' field and update 'size' if
+ * fewer bytes are consumed.
* - EVENT_OVERRUN: unused.
* - EVENT_MARKER: pointer to const uint32_t containing the marker position in frames.
* - EVENT_NEW_POS: pointer to const uint32_t containing the new position in frames.
@@ -106,6 +110,7 @@
* - BAD_VALUE: unsupported configuration
* frameCount is guaranteed to be non-zero if status is NO_ERROR,
* and is undefined otherwise.
+ * FIXME This API assumes a route, and so should be deprecated.
*/
static status_t getMinFrameCount(size_t* frameCount,
@@ -144,15 +149,16 @@
* be larger if the requested size is not compatible with current audio HAL
* latency. Zero means to use a default value.
* cbf: Callback function. If not null, this function is called periodically
- * to consume new data and inform of marker, position updates, etc.
+ * to consume new data in TRANSFER_CALLBACK mode
+ * and inform of marker, position updates, etc.
* user: Context for use by the callback receiver.
* notificationFrames: The callback function is called each time notificationFrames PCM
* frames are ready in record track output buffer.
* sessionId: Not yet supported.
* transferType: How data is transferred from AudioRecord.
* flags: See comments on audio_input_flags_t in <system/audio.h>
+ * pAttributes: If not NULL, supersedes inputSource for use case selection.
* threadCanCallJava: Not present in parameter list, and so is fixed at false.
- * pAttributes: if not NULL, supersedes inputSource for use case selection
*/
AudioRecord(audio_source_t inputSource,
@@ -211,7 +217,7 @@
status_t initCheck() const { return mStatus; }
/* Returns this track's estimated latency in milliseconds.
- * This includes the latency due to AudioRecord buffer size,
+ * This includes the latency due to AudioRecord buffer size, resampling if applicable,
* and audio hardware driver.
*/
uint32_t latency() const { return mLatency; }
@@ -309,7 +315,12 @@
* Returned value:
* handle on audio hardware input
*/
- audio_io_handle_t getInput() const;
+// FIXME The only known public caller is frameworks/opt/net/voip/src/jni/rtp/AudioGroup.cpp
+ audio_io_handle_t getInput() const __attribute__((__deprecated__))
+ { return getInputPrivate(); }
+private:
+ audio_io_handle_t getInputPrivate() const;
+public:
/* Returns the audio session ID associated with this AudioRecord.
*
@@ -323,7 +334,8 @@
*/
int getSessionId() const { return mSessionId; }
- /* Obtains a buffer of up to "audioBuffer->frameCount" full frames.
+ /* Public API for TRANSFER_OBTAIN mode.
+ * Obtains a buffer of up to "audioBuffer->frameCount" full frames.
* After draining these frames of data, the caller should release them with releaseBuffer().
* If the track buffer is not empty, obtainBuffer() returns as many contiguous
* full frames as are available immediately.
@@ -347,6 +359,8 @@
* Buffer fields
* On entry:
* frameCount number of frames requested
+ * size ignored
+ * raw ignored
* After error return:
* frameCount 0
* size 0
@@ -372,9 +386,15 @@
struct timespec *elapsed = NULL, size_t *nonContig = NULL);
public:
- /* Release an emptied buffer of "audioBuffer->frameCount" frames for AudioFlinger to re-fill. */
- // FIXME make private when obtainBuffer() for TRANSFER_OBTAIN is removed
- void releaseBuffer(Buffer* audioBuffer);
+ /* Public API for TRANSFER_OBTAIN mode.
+ * Release an emptied buffer of "audioBuffer->frameCount" frames for AudioFlinger to re-fill.
+ *
+ * Buffer fields:
+ * frameCount currently ignored but recommend to set to actual number of frames consumed
+ * size actual number of bytes consumed, must be multiple of frameSize
+ * raw ignored
+ */
+ void releaseBuffer(const Buffer* audioBuffer);
/* As a convenience we provide a read() interface to the audio buffer.
* Input parameter 'size' is in byte units.
@@ -416,6 +436,7 @@
void pause(); // suspend thread from execution at next loop boundary
void resume(); // allow thread to execute, if not requested to exit
+ void wake(); // wake to handle changed notification conditions.
private:
void pauseInternal(nsecs_t ns = 0LL);
@@ -430,7 +451,9 @@
bool mPaused; // whether thread is requested to pause at next loop entry
bool mPausedInt; // whether thread internally requests pause
nsecs_t mPausedNs; // if mPausedInt then associated timeout, otherwise ignored
- bool mIgnoreNextPausedInt; // whether to ignore next mPausedInt request
+ bool mIgnoreNextPausedInt; // skip any internal pause and go immediately
+ // to processAudioBuffer() as state may have changed
+ // since pause time calculated.
};
// body of AudioRecordThread::threadLoop()
@@ -458,7 +481,7 @@
bool mActive;
// for client callback handler
- callback_t mCbf; // callback handler for events, or NULL
+ callback_t mCbf; // callback handler for events, or NULL
void* mUserData;
// for notification APIs
@@ -475,10 +498,10 @@
bool mRetryOnPartialBuffer; // sleep and retry after partial obtainBuffer()
uint32_t mObservedSequence; // last observed value of mSequence
- uint32_t mMarkerPosition; // in wrapping (overflow) frame units
+ uint32_t mMarkerPosition; // in wrapping (overflow) frame units
bool mMarkerReached;
- uint32_t mNewPosition; // in frames
- uint32_t mUpdatePeriod; // in frames, zero means no EVENT_NEW_POS
+ uint32_t mNewPosition; // in frames
+ uint32_t mUpdatePeriod; // in frames, zero means no EVENT_NEW_POS
status_t mStatus;
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 2ab3dd6..ad5d6ed 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -98,10 +98,13 @@
// Returned samplingRate and frameCount output values are guaranteed
// to be non-zero if status == NO_ERROR
+ // FIXME This API assumes a route, and so should be deprecated.
static status_t getOutputSamplingRate(uint32_t* samplingRate,
audio_stream_type_t stream);
+ // FIXME This API assumes a route, and so should be deprecated.
static status_t getOutputFrameCount(size_t* frameCount,
audio_stream_type_t stream);
+ // FIXME This API assumes a route, and so should be deprecated.
static status_t getOutputLatency(uint32_t* latency,
audio_stream_type_t stream);
static status_t getSamplingRate(audio_io_handle_t output,
@@ -110,19 +113,20 @@
// audio_stream->get_buffer_size()/audio_stream_out_frame_size()
static status_t getFrameCount(audio_io_handle_t output,
size_t* frameCount);
- // returns the audio output stream latency in ms. Corresponds to
+ // returns the audio output latency in ms. Corresponds to
// audio_stream_out->get_latency()
static status_t getLatency(audio_io_handle_t output,
uint32_t* latency);
// return status NO_ERROR implies *buffSize > 0
+ // FIXME This API assumes a route, and so should deprecated.
static status_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask, size_t* buffSize);
static status_t setVoiceVolume(float volume);
// return the number of audio frames written by AudioFlinger to audio HAL and
- // audio dsp to DAC since the specified output I/O handle has exited standby.
+ // audio dsp to DAC since the specified output has exited standby.
// returned status (from utils/Errors.h) can be:
// - NO_ERROR: successful operation, halFrames and dspFrames point to valid data
// - INVALID_OPERATION: Not supported on current hardware platform
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 3de0774..81b1181 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -91,7 +91,7 @@
void* raw;
short* i16; // signed 16-bit
int8_t* i8; // unsigned 8-bit, offset by 0x80
- }; // input: unused, output: pointer to buffer
+ }; // input to obtainBuffer(): unused, output: pointer to buffer
};
/* As a convenience, if a callback is supplied, a handler thread
@@ -125,6 +125,7 @@
* - BAD_VALUE: unsupported configuration
* frameCount is guaranteed to be non-zero if status is NO_ERROR,
* and is undefined otherwise.
+ * FIXME This API assumes a route, and so should be deprecated.
*/
static status_t getMinFrameCount(size_t* frameCount,
@@ -149,9 +150,6 @@
/* Creates an AudioTrack object and registers it with AudioFlinger.
* Once created, the track needs to be started before it can be used.
* Unspecified values are set to appropriate default values.
- * With this constructor, the track is configured for streaming mode.
- * Data to be rendered is supplied by write() or by the callback EVENT_MORE_DATA.
- * Intermixing a combination of write() and non-ignored EVENT_MORE_DATA is not allowed.
*
* Parameters:
*
@@ -169,20 +167,28 @@
* configuration. Zero means to use a default value.
* flags: See comments on audio_output_flags_t in <system/audio.h>.
* cbf: Callback function. If not null, this function is called periodically
- * to provide new data and inform of marker, position updates, etc.
+ * to provide new data in TRANSFER_CALLBACK mode
+ * and inform of marker, position updates, etc.
* user: Context for use by the callback receiver.
* notificationFrames: The callback function is called each time notificationFrames PCM
* frames have been consumed from track input buffer.
* This is expressed in units of frames at the initial source sample rate.
* sessionId: Specific session ID, or zero to use default.
* transferType: How data is transferred to AudioTrack.
+ * offloadInfo: If not NULL, provides offload parameters for
+ * AudioSystem::getOutputForAttr().
+ * uid: User ID of the app which initially requested this AudioTrack
+ * for power management tracking, or -1 for current user ID.
+ * pid: Process ID of the app which initially requested this AudioTrack
+ * for power management tracking, or -1 for current process ID.
+ * pAttributes: If not NULL, supersedes streamType for use case selection.
* threadCanCallJava: Not present in parameter list, and so is fixed at false.
*/
AudioTrack( audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
- audio_channel_mask_t,
+ audio_channel_mask_t channelMask,
size_t frameCount = 0,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
callback_t cbf = NULL,
@@ -198,7 +204,9 @@
/* Creates an audio track and registers it with AudioFlinger.
* With this constructor, the track is configured for static buffer mode.
* Data to be rendered is passed in a shared memory buffer
- * identified by the argument sharedBuffer, which must be non-0.
+ * identified by the argument sharedBuffer, which should be non-0.
+ * If sharedBuffer is zero, this constructor is equivalent to the previous constructor
+ * but without the ability to specify a non-zero value for the frameCount parameter.
* The memory should be initialized to the desired data before calling start().
* The write() method is not supported in this case.
* It is recommended to pass a callback function to be notified of playback end by an
@@ -464,7 +472,9 @@
* handle on audio hardware output, or AUDIO_IO_HANDLE_NONE if the
* track needed to be re-created but that failed
*/
+private:
audio_io_handle_t getOutput() const;
+public:
/* Returns the unique session ID associated with this track.
*
@@ -559,7 +569,6 @@
* frameCount currently ignored but recommend to set to actual number of frames filled
* size actual number of bytes filled, must be multiple of frameSize
* raw ignored
- *
*/
// FIXME make private when obtainBuffer() for TRANSFER_OBTAIN is removed
void releaseBuffer(const Buffer* audioBuffer);
@@ -574,7 +583,7 @@
* WOULD_BLOCK when obtainBuffer() returns same, or
* AudioTrack was stopped during the write
* or any other error code returned by IAudioTrack::start() or restoreTrack_l().
- * Default behavior is to only return until all data has been transferred. Set 'blocking' to
+ * Default behavior is to only return when all data has been transferred. Set 'blocking' to
* false for the method to return immediately without waiting to try multiple times to write
* the full content of the buffer.
*/
@@ -582,6 +591,7 @@
/*
* Dumps the state of an audio track.
+ * Not a general-purpose API; intended only for use by media player service to dump its tracks.
*/
status_t dump(int fd, const Vector<String16>& args) const;
@@ -623,8 +633,6 @@
AudioTrack(const AudioTrack& other);
AudioTrack& operator = (const AudioTrack& other);
- void setAttributesFromStreamType(audio_stream_type_t streamType);
-
/* a small internal class to handle the callback */
class AudioTrackThread : public Thread
{
@@ -667,10 +675,6 @@
static const nsecs_t NS_WHENEVER = -1, NS_INACTIVE = -2, NS_NEVER = -3;
nsecs_t processAudioBuffer();
- bool isOffloaded() const;
- bool isDirect() const;
- bool isOffloadedOrDirect() const;
-
// caller must hold lock on mLock for all _l methods
status_t createTrack_l();
@@ -683,6 +687,10 @@
// FIXME enum is faster than strcmp() for parameter 'from'
status_t restoreTrack_l(const char *from);
+ bool isOffloaded() const;
+ bool isDirect() const;
+ bool isOffloadedOrDirect() const;
+
bool isOffloaded_l() const
{ return (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; }
@@ -773,6 +781,7 @@
bool mMarkerReached;
uint32_t mNewPosition; // in frames
uint32_t mUpdatePeriod; // in frames, zero means no EVENT_NEW_POS
+
uint32_t mServer; // in frames, last known mProxy->getPosition()
// which is count of frames consumed by server,
// reset by new IAudioTrack,
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 31a14f0..f927a80 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -94,6 +94,8 @@
sp<IMemory>& buffers, // return value 0 means it follows cblk
status_t *status) = 0;
+ // FIXME Surprisingly, sampleRate/format/frameCount/latency don't work for input handles
+
/* query the audio hardware state. This state never changes,
* and therefore can be cached.
*/
@@ -142,6 +144,7 @@
virtual void registerClient(const sp<IAudioFlingerClient>& client) = 0;
// retrieve the audio recording buffer size
+ // FIXME This API assumes a route, and so should be deprecated.
virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask) const = 0;
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index aa91485..c1483f3 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -291,7 +291,7 @@
OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat);
status_t setupAMRCodec(bool encoder, bool isWAMR, int32_t bitRate);
- status_t setupG711Codec(bool encoder, int32_t numChannels);
+ status_t setupG711Codec(bool encoder, int32_t sampleRate, int32_t numChannels);
status_t setupFlacCodec(
bool encoder, int32_t numChannels, int32_t sampleRate, int32_t compressionLevel);
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index e341160..84b1b1a 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -250,7 +250,7 @@
status_t setAC3Format(int32_t numChannels, int32_t sampleRate);
- void setG711Format(int32_t numChannels);
+ void setG711Format(int32_t sampleRate, int32_t numChannels);
status_t setVideoPortFormatType(
OMX_U32 portIndex,
diff --git a/include/media/stagefright/ProcessInfo.h b/include/media/stagefright/ProcessInfo.h
new file mode 100644
index 0000000..ec0cdff
--- /dev/null
+++ b/include/media/stagefright/ProcessInfo.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef PROCESS_INFO_H_
+
+#define PROCESS_INFO_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/ProcessInfoInterface.h>
+
+namespace android {
+
+struct ProcessInfo : public ProcessInfoInterface {
+ ProcessInfo();
+
+ virtual bool getPriority(int pid, int* priority);
+
+protected:
+ virtual ~ProcessInfo();
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(ProcessInfo);
+};
+
+} // namespace android
+
+#endif // PROCESS_INFO_H_
diff --git a/media/libmediaplayerservice/ProcessInfoInterface.h b/include/media/stagefright/ProcessInfoInterface.h
similarity index 100%
rename from media/libmediaplayerservice/ProcessInfoInterface.h
rename to include/media/stagefright/ProcessInfoInterface.h
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 7143f1a..5644428 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -53,8 +53,8 @@
struct AudioTrackSharedStreaming {
// similar to NBAIO MonoPipe
// in continuously incrementing frame units, take modulo buffer size, which must be a power of 2
- volatile int32_t mFront; // read by server
- volatile int32_t mRear; // write by client
+ volatile int32_t mFront; // read by consumer (output: server, input: client)
+ volatile int32_t mRear; // written by producer (output: client, input: server)
volatile int32_t mFlush; // incremented by client to indicate a request to flush;
// server notices and discards all data between mFront and mRear
volatile uint32_t mUnderrunFrames; // server increments for each unavailable but desired frame
diff --git a/include/radio/IRadio.h b/include/radio/IRadio.h
new file mode 100644
index 0000000..1877f8f
--- /dev/null
+++ b/include/radio/IRadio.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IRADIO_H
+#define ANDROID_HARDWARE_IRADIO_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <system/radio.h>
+
+namespace android {
+
+class IRadio : public IInterface
+{
+public:
+
+ DECLARE_META_INTERFACE(Radio);
+
+ virtual void detach() = 0;
+
+ virtual status_t setConfiguration(const struct radio_band_config *config) = 0;
+
+ virtual status_t getConfiguration(struct radio_band_config *config) = 0;
+
+ virtual status_t setMute(bool mute) = 0;
+
+ virtual status_t getMute(bool *mute) = 0;
+
+ virtual status_t step(radio_direction_t direction, bool skipSubChannel) = 0;
+
+ virtual status_t scan(radio_direction_t direction, bool skipSubChannel) = 0;
+
+ virtual status_t tune(unsigned int channel, unsigned int subChannel) = 0;
+
+ virtual status_t cancel() = 0;
+
+ virtual status_t getProgramInformation(struct radio_program_info *info) = 0;
+
+ virtual status_t hasControl(bool *hasControl) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnRadio: public BnInterface<IRadio>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_IRADIO_H
diff --git a/include/radio/IRadioClient.h b/include/radio/IRadioClient.h
new file mode 100644
index 0000000..9062ad6
--- /dev/null
+++ b/include/radio/IRadioClient.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IRADIO_CLIENT_H
+#define ANDROID_HARDWARE_IRADIO_CLIENT_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class IRadioClient : public IInterface
+{
+public:
+
+ DECLARE_META_INTERFACE(RadioClient);
+
+ virtual void onEvent(const sp<IMemory>& eventMemory) = 0;
+
+};
+
+// ----------------------------------------------------------------------------
+
+class BnRadioClient : public BnInterface<IRadioClient>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_IRADIO_CLIENT_H
diff --git a/include/radio/IRadioService.h b/include/radio/IRadioService.h
new file mode 100644
index 0000000..a946dd5
--- /dev/null
+++ b/include/radio/IRadioService.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IRADIO_SERVICE_H
+#define ANDROID_HARDWARE_IRADIO_SERVICE_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <system/radio.h>
+
+namespace android {
+
+class IRadio;
+class IRadioClient;
+
+class IRadioService : public IInterface
+{
+public:
+
+ DECLARE_META_INTERFACE(RadioService);
+
+ virtual status_t listModules(struct radio_properties *properties,
+ uint32_t *numModules) = 0;
+
+ virtual status_t attach(const radio_handle_t handle,
+ const sp<IRadioClient>& client,
+ const struct radio_band_config *config,
+ bool withAudio,
+ sp<IRadio>& radio) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnRadioService: public BnInterface<IRadioService>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_IRADIO_SERVICE_H
diff --git a/include/radio/Radio.h b/include/radio/Radio.h
new file mode 100644
index 0000000..302bf16
--- /dev/null
+++ b/include/radio/Radio.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_HARDWARE_RADIO_H
+#define ANDROID_HARDWARE_RADIO_H
+
+#include <binder/IBinder.h>
+#include <utils/threads.h>
+#include <radio/RadioCallback.h>
+#include <radio/IRadio.h>
+#include <radio/IRadioService.h>
+#include <radio/IRadioClient.h>
+#include <system/radio.h>
+
+namespace android {
+
+class MemoryDealer;
+
+class Radio : public BnRadioClient,
+ public IBinder::DeathRecipient
+{
+public:
+
+ virtual ~Radio();
+
+ static status_t listModules(struct radio_properties *properties,
+ uint32_t *numModules);
+ static sp<Radio> attach(radio_handle_t handle,
+ const struct radio_band_config *config,
+ bool withAudio,
+ const sp<RadioCallback>& callback);
+
+
+ void detach();
+
+ status_t setConfiguration(const struct radio_band_config *config);
+
+ status_t getConfiguration(struct radio_band_config *config);
+
+ status_t setMute(bool mute);
+
+ status_t getMute(bool *mute);
+
+ status_t step(radio_direction_t direction, bool skipSubChannel);
+
+ status_t scan(radio_direction_t direction, bool skipSubChannel);
+
+ status_t tune(unsigned int channel, unsigned int subChannel);
+
+ status_t cancel();
+
+ status_t getProgramInformation(struct radio_program_info *info);
+
+ status_t hasControl(bool *hasControl);
+
+ // BpRadioClient
+ virtual void onEvent(const sp<IMemory>& eventMemory);
+
+ //IBinder::DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+
+private:
+ Radio(radio_handle_t handle,
+ const sp<RadioCallback>&);
+ static const sp<IRadioService>& getRadioService();
+
+ Mutex mLock;
+ sp<IRadio> mIRadio;
+ const radio_handle_t mHandle;
+ sp<RadioCallback> mCallback;
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_RADIO_H
diff --git a/include/radio/RadioCallback.h b/include/radio/RadioCallback.h
new file mode 100644
index 0000000..4a7f1a6
--- /dev/null
+++ b/include/radio/RadioCallback.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_HARDWARE_RADIO_CALLBACK_H
+#define ANDROID_HARDWARE_RADIO_CALLBACK_H
+
+#include <utils/RefBase.h>
+#include <system/radio.h>
+
+namespace android {
+
+class RadioCallback : public RefBase
+{
+public:
+
+ RadioCallback() {}
+ virtual ~RadioCallback() {}
+
+ virtual void onEvent(struct radio_event *event) = 0;
+
+};
+
+}; // namespace android
+
+#endif //ANDROID_HARDWARE_RADIO_CALLBACK_H
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index af103c1..7d8222f 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -486,4 +486,4 @@
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp
index 33dbf0b..8c8cf45 100644
--- a/media/libmedia/AudioParameter.cpp
+++ b/media/libmedia/AudioParameter.cpp
@@ -180,4 +180,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/AudioPolicy.cpp b/media/libmedia/AudioPolicy.cpp
index d2d0971..c7dafcb 100644
--- a/media/libmedia/AudioPolicy.cpp
+++ b/media/libmedia/AudioPolicy.cpp
@@ -112,4 +112,4 @@
return NO_ERROR;
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 07ca14f..1a65ee8 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -112,7 +112,9 @@
mCblkMemory.clear();
mBufferMemory.clear();
IPCThreadState::self()->flushCommands();
- AudioSystem::releaseAudioSessionId(mSessionId, -1);
+ ALOGV("~AudioRecord, releasing session id %d",
+ mSessionId);
+ AudioSystem::releaseAudioSessionId(mSessionId, -1 /*pid*/);
}
}
@@ -286,7 +288,6 @@
status_t status = NO_ERROR;
if (!(flags & CBLK_INVALID)) {
- ALOGV("mAudioRecord->start()");
status = mAudioRecord->start(event, triggerSession);
if (status == DEAD_OBJECT) {
flags |= CBLK_INVALID;
@@ -352,6 +353,10 @@
mMarkerPosition = marker;
mMarkerReached = false;
+ sp<AudioRecordThread> t = mAudioRecordThread;
+ if (t != 0) {
+ t->wake();
+ }
return NO_ERROR;
}
@@ -378,6 +383,10 @@
mNewPosition = mProxy->getPosition() + updatePeriod;
mUpdatePeriod = updatePeriod;
+ sp<AudioRecordThread> t = mAudioRecordThread;
+ if (t != 0) {
+ t->wake();
+ }
return NO_ERROR;
}
@@ -408,7 +417,7 @@
uint32_t AudioRecord::getInputFramesLost() const
{
// no need to check mActive, because if inactive this will return 0, which is what we want
- return AudioSystem::getInputFramesLost(getInput());
+ return AudioSystem::getInputFramesLost(getInputPrivate());
}
// -------------------------------------------------------------------------
@@ -416,7 +425,6 @@
// must be called with mLock held
status_t AudioRecord::openRecord_l(size_t epoch)
{
- status_t status;
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
if (audioFlinger == 0) {
ALOGE("Could not get audioflinger");
@@ -436,7 +444,8 @@
(mTransfer == TRANSFER_CALLBACK) &&
// matching sample rate
(mSampleRate == afSampleRate))) {
- ALOGW("AUDIO_INPUT_FLAG_FAST denied by client");
+ ALOGW("AUDIO_INPUT_FLAG_FAST denied by client; transfer %d, track %u Hz, primary %u Hz",
+ mTransfer, mSampleRate, afSampleRate);
// once denied, do not request again if IAudioRecord is re-created
mFlags = (audio_input_flags_t) (mFlags & ~AUDIO_INPUT_FLAG_FAST);
}
@@ -452,7 +461,8 @@
}
audio_io_handle_t input;
- status = AudioSystem::getInputForAttr(&mAttributes, &input, (audio_session_t)mSessionId,
+ status_t status = AudioSystem::getInputForAttr(&mAttributes, &input,
+ (audio_session_t)mSessionId,
mSampleRate, mFormat, mChannelMask, mFlags);
if (status != NO_ERROR) {
@@ -684,9 +694,9 @@
return status;
}
-void AudioRecord::releaseBuffer(Buffer* audioBuffer)
+void AudioRecord::releaseBuffer(const Buffer* audioBuffer)
{
- // all TRANSFER_* are valid
+ // FIXME add error checking on mode, by adding an internal version
size_t stepCount = audioBuffer->size / mFrameSize;
if (stepCount == 0) {
@@ -704,7 +714,7 @@
// the server does not automatically disable recorder on overrun, so no need to restart
}
-audio_io_handle_t AudioRecord::getInput() const
+audio_io_handle_t AudioRecord::getInputPrivate() const
{
AutoMutex lock(mLock);
return mInput;
@@ -863,8 +873,11 @@
if (!markerReached && position < markerPosition) {
minFrames = markerPosition - position;
}
- if (updatePeriod > 0 && updatePeriod < minFrames) {
- minFrames = updatePeriod;
+ if (updatePeriod > 0) {
+ uint32_t remaining = newPosition - position;
+ if (remaining < minFrames) {
+ minFrames = remaining;
+ }
}
// If > 0, poll periodically to recover from a stuck server. A good value is 2.
@@ -990,14 +1003,13 @@
{
ALOGW("dead IAudioRecord, creating a new one from %s()", from);
++mSequence;
- status_t result;
// if the new IAudioRecord is created, openRecord_l() will modify the
// following member variables: mAudioRecord, mCblkMemory, mCblk, mBufferMemory.
// It will also delete the strong references on previous IAudioRecord and IMemory
size_t position = mProxy->getPosition();
mNewPosition = position + mUpdatePeriod;
- result = openRecord_l(position);
+ status_t result = openRecord_l(position);
if (result == NO_ERROR) {
if (mActive) {
// callback thread or sync event hasn't changed
@@ -1069,8 +1081,8 @@
case NS_NEVER:
return false;
case NS_WHENEVER:
- // FIXME increase poll interval, or make event-driven
- ns = 1000000000LL;
+ // Event driven: call wake() when callback notifications conditions change.
+ ns = INT64_MAX;
// fall through
default:
LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %" PRId64, ns);
@@ -1103,6 +1115,17 @@
}
}
+void AudioRecord::AudioRecordThread::wake()
+{
+ AutoMutex _l(mMyLock);
+ if (!mPaused && mPausedInt && mPausedNs > 0) {
+ // audio record is active and internally paused with timeout.
+ mIgnoreNextPausedInt = true;
+ mPausedInt = false;
+ mMyCond.signal();
+ }
+}
+
void AudioRecord::AudioRecordThread::pauseInternal(nsecs_t ns)
{
AutoMutex _l(mMyLock);
@@ -1112,4 +1135,4 @@
// -------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index f5a5712..c6b34a7 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -1003,4 +1003,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index c775e7b..8fd5278 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -203,8 +203,8 @@
mCblkMemory.clear();
mSharedBuffer.clear();
IPCThreadState::self()->flushCommands();
- ALOGV("~AudioTrack, releasing session id from %d on behalf of %d",
- IPCThreadState::self()->getCallingPid(), mClientPid);
+ ALOGV("~AudioTrack, releasing session id %d from %d on behalf of %d",
+ mSessionId, IPCThreadState::self()->getCallingPid(), mClientPid);
AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
}
}
@@ -229,9 +229,9 @@
const audio_attributes_t* pAttributes)
{
ALOGV("set(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
- "flags #%x, notificationFrames %u, sessionId %d, transferType %d",
+ "flags #%x, notificationFrames %u, sessionId %d, transferType %d, uid %d, pid %d",
streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames,
- sessionId, transferType);
+ sessionId, transferType, uid, pid);
switch (transferType) {
case TRANSFER_DEFAULT:
@@ -964,9 +964,9 @@
if (status != NO_ERROR || output == AUDIO_IO_HANDLE_NONE) {
- ALOGE("Could not get audio output for stream type %d, usage %d, sample rate %u, format %#x,"
+ ALOGE("Could not get audio output for session %d, stream type %d, usage %d, sample rate %u, format %#x,"
" channel mask %#x, flags %#x",
- streamType, mAttributes.usage, mSampleRate, mFormat, mChannelMask, mFlags);
+ mSessionId, streamType, mAttributes.usage, mSampleRate, mFormat, mChannelMask, mFlags);
return BAD_VALUE;
}
{
@@ -981,6 +981,7 @@
ALOGE("getLatency(%d) failed status %d", output, status);
goto release;
}
+ ALOGV("createTrack_l() output %d afLatency %u", output, afLatency);
size_t afFrameCount;
status = AudioSystem::getFrameCount(output, &afFrameCount);
@@ -1010,11 +1011,11 @@
(mTransfer == TRANSFER_OBTAIN)) &&
// matching sample rate
(mSampleRate == afSampleRate))) {
- ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by client");
+ ALOGW("AUDIO_OUTPUT_FLAG_FAST denied by client; transfer %d, track %u Hz, output %u Hz",
+ mTransfer, mSampleRate, afSampleRate);
// once denied, do not request again if IAudioTrack is re-created
mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_FAST);
}
- ALOGV("createTrack_l() output %d afLatency %d", output, afLatency);
// The client's AudioTrack buffer is divided into n parts for purpose of wakeup by server, where
// n = 1 fast track with single buffering; nBuffering is ignored
@@ -1090,6 +1091,7 @@
size_t temp = frameCount; // temp may be replaced by a revised value of frameCount,
// but we will still need the original value also
+ int originalSessionId = mSessionId;
sp<IAudioTrack> track = audioFlinger->createTrack(streamType,
mSampleRate,
mFormat,
@@ -1102,6 +1104,8 @@
&mSessionId,
mClientUid,
&status);
+ ALOGE_IF(originalSessionId != AUDIO_SESSION_ALLOCATE && mSessionId != originalSessionId,
+ "session ID changed from %d to %d", originalSessionId, mSessionId);
if (status != NO_ERROR) {
ALOGE("AudioFlinger could not create track, status: %d", status);
@@ -1194,9 +1198,13 @@
// address space. AudioFlinger::TrackBase::mBuffer is for the server address space.
void* buffers;
if (mSharedBuffer == 0) {
- buffers = (char*)cblk + sizeof(audio_track_cblk_t);
+ buffers = cblk + 1;
} else {
buffers = mSharedBuffer->pointer();
+ if (buffers == NULL) {
+ ALOGE("Could not get buffer pointer");
+ return NO_INIT;
+ }
}
mAudioTrack->attachAuxEffect(mAuxEffectId);
@@ -1415,8 +1423,7 @@
return ssize_t(err);
}
- size_t toWrite;
- toWrite = audioBuffer.size;
+ size_t toWrite = audioBuffer.size;
memcpy(audioBuffer.i8, buffer, toWrite);
buffer = ((const char *) buffer) + toWrite;
userSize -= toWrite;
@@ -1784,7 +1791,7 @@
return WAIT_PERIOD_MS * 1000000LL;
}
- size_t releasedFrames = audioBuffer.size / mFrameSize;
+ size_t releasedFrames = writtenSize / mFrameSize;
audioBuffer.frameCount = releasedFrames;
mRemainingFrames -= releasedFrames;
if (misalignment >= releasedFrames) {
@@ -1829,7 +1836,6 @@
ALOGW("dead IAudioTrack, %s, creating a new one from %s()",
isOffloadedOrDirect_l() ? "Offloaded or Direct" : "PCM", from);
++mSequence;
- status_t result;
// refresh the audio configuration cache in this process to make sure we get new
// output parameters and new IAudioFlinger in createTrack_l()
@@ -1851,13 +1857,13 @@
// following member variables: mAudioTrack, mCblkMemory and mCblk.
// It will also delete the strong references on previous IAudioTrack and IMemory.
// If a new IAudioTrack cannot be created, the previous (dead) instance will be left intact.
- result = createTrack_l();
+ status_t result = createTrack_l();
// take the frames that will be lost by track recreation into account in saved position
// For streaming tracks, this is the amount we obtained from the user/client
// (not the number actually consumed at the server - those are already lost).
(void) updateAndGetPosition_l();
- if (mStaticProxy != 0) {
+ if (mStaticProxy == 0) {
mPosition = mReleased;
}
@@ -2185,4 +2191,4 @@
mPausedNs = ns;
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 08241e2..6d5f1af 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -423,7 +423,6 @@
goto end;
}
// check for obtainBuffer interrupted by client
- // check for obtainBuffer interrupted by client
if (flags & CBLK_INTERRUPT) {
ALOGV("waitStreamEndDone() interrupted by client");
status = -EINTR;
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 8e3b633..6f038ea 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -1369,4 +1369,4 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp
index 1c299f7..641e6c1 100644
--- a/media/libmedia/IAudioFlingerClient.cpp
+++ b/media/libmedia/IAudioFlingerClient.cpp
@@ -99,4 +99,4 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index f2ff27b..39374d8 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -1228,4 +1228,4 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IAudioPolicyServiceClient.cpp b/media/libmedia/IAudioPolicyServiceClient.cpp
index e802277..7c65878 100644
--- a/media/libmedia/IAudioPolicyServiceClient.cpp
+++ b/media/libmedia/IAudioPolicyServiceClient.cpp
@@ -80,4 +80,4 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp
index 8a4a383..9d80753 100644
--- a/media/libmedia/IAudioRecord.cpp
+++ b/media/libmedia/IAudioRecord.cpp
@@ -91,4 +91,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp
index df209fd..651cb61 100644
--- a/media/libmedia/IAudioTrack.cpp
+++ b/media/libmedia/IAudioTrack.cpp
@@ -292,4 +292,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IDrmClient.cpp b/media/libmedia/IDrmClient.cpp
index f50715e..490c6ed 100644
--- a/media/libmedia/IDrmClient.cpp
+++ b/media/libmedia/IDrmClient.cpp
@@ -78,4 +78,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IEffect.cpp b/media/libmedia/IEffect.cpp
index c2fff78..eb4b098 100644
--- a/media/libmedia/IEffect.cpp
+++ b/media/libmedia/IEffect.cpp
@@ -201,4 +201,4 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IEffectClient.cpp b/media/libmedia/IEffectClient.cpp
index aef4371..1322e72 100644
--- a/media/libmedia/IEffectClient.cpp
+++ b/media/libmedia/IEffectClient.cpp
@@ -141,4 +141,4 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IMediaCodecList.cpp b/media/libmedia/IMediaCodecList.cpp
index bf7c5ca..80020db 100644
--- a/media/libmedia/IMediaCodecList.cpp
+++ b/media/libmedia/IMediaCodecList.cpp
@@ -160,4 +160,4 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IMediaDeathNotifier.cpp b/media/libmedia/IMediaDeathNotifier.cpp
index 38e9ca0..d4360ea 100644
--- a/media/libmedia/IMediaDeathNotifier.cpp
+++ b/media/libmedia/IMediaDeathNotifier.cpp
@@ -108,4 +108,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IMediaHTTPConnection.cpp b/media/libmedia/IMediaHTTPConnection.cpp
index 7e26ee6..2ff7658 100644
--- a/media/libmedia/IMediaHTTPConnection.cpp
+++ b/media/libmedia/IMediaHTTPConnection.cpp
@@ -178,5 +178,4 @@
IMPLEMENT_META_INTERFACE(
MediaHTTPConnection, "android.media.IMediaHTTPConnection");
-} // namespace android
-
+} // namespace android
diff --git a/media/libmedia/IMediaHTTPService.cpp b/media/libmedia/IMediaHTTPService.cpp
index 1260582..f30d0f3 100644
--- a/media/libmedia/IMediaHTTPService.cpp
+++ b/media/libmedia/IMediaHTTPService.cpp
@@ -54,5 +54,4 @@
IMPLEMENT_META_INTERFACE(
MediaHTTPService, "android.media.IMediaHTTPService");
-} // namespace android
-
+} // namespace android
diff --git a/media/libmedia/IMediaLogService.cpp b/media/libmedia/IMediaLogService.cpp
index a4af7b7..230749e 100644
--- a/media/libmedia/IMediaLogService.cpp
+++ b/media/libmedia/IMediaLogService.cpp
@@ -91,4 +91,4 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index aa2665a..551cffe 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -297,4 +297,4 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index dcd5670..ce3009a 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -574,4 +574,4 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IMediaPlayerClient.cpp b/media/libmedia/IMediaPlayerClient.cpp
index a670c96..d608386 100644
--- a/media/libmedia/IMediaPlayerClient.cpp
+++ b/media/libmedia/IMediaPlayerClient.cpp
@@ -75,4 +75,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index feea267..aa7b2e1 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -234,4 +234,4 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 9181f86..8ca256c 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -463,4 +463,4 @@
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IMediaRecorderClient.cpp b/media/libmedia/IMediaRecorderClient.cpp
index e7907e3..6795d23 100644
--- a/media/libmedia/IMediaRecorderClient.cpp
+++ b/media/libmedia/IMediaRecorderClient.cpp
@@ -67,4 +67,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IRemoteDisplay.cpp b/media/libmedia/IRemoteDisplay.cpp
index 1e15434..869d11a 100644
--- a/media/libmedia/IRemoteDisplay.cpp
+++ b/media/libmedia/IRemoteDisplay.cpp
@@ -91,4 +91,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/IRemoteDisplayClient.cpp b/media/libmedia/IRemoteDisplayClient.cpp
index 9d63bc9..bedeb6c 100644
--- a/media/libmedia/IRemoteDisplayClient.cpp
+++ b/media/libmedia/IRemoteDisplayClient.cpp
@@ -101,4 +101,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/StringArray.cpp b/media/libmedia/StringArray.cpp
index 477e3fd..b2e5907 100644
--- a/media/libmedia/StringArray.cpp
+++ b/media/libmedia/StringArray.cpp
@@ -110,4 +110,4 @@
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index f91e3e4..9d69b6a 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -429,4 +429,4 @@
return false;
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index 8e8a1ed..873808a 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -176,4 +176,4 @@
}
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index d1d51cc..5dd8c02 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -877,4 +877,4 @@
return mPlayer->setNextPlayer(next == NULL ? NULL : next->mPlayer);
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 973e156..a2d6e53 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -680,4 +680,4 @@
notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_ERROR_SERVER_DIED, 0);
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmediaplayerservice/DrmSessionManager.cpp b/media/libmediaplayerservice/DrmSessionManager.cpp
index 6e17eb1..641f881 100644
--- a/media/libmediaplayerservice/DrmSessionManager.cpp
+++ b/media/libmediaplayerservice/DrmSessionManager.cpp
@@ -21,10 +21,10 @@
#include "DrmSessionManager.h"
#include "DrmSessionClientInterface.h"
-#include "ProcessInfoInterface.h"
#include <binder/IPCThreadState.h>
#include <binder/IProcessInfoService.h>
#include <binder/IServiceManager.h>
+#include <media/stagefright/ProcessInfo.h>
#include <unistd.h>
#include <utils/String8.h>
@@ -38,37 +38,6 @@
return sessionIdStr;
}
-struct ProcessInfo : public ProcessInfoInterface {
- ProcessInfo() {}
-
- virtual bool getPriority(int pid, int* priority) {
- sp<IBinder> binder = defaultServiceManager()->getService(String16("processinfo"));
- sp<IProcessInfoService> service = interface_cast<IProcessInfoService>(binder);
-
- size_t length = 1;
- int32_t states;
- status_t err = service->getProcessStatesFromPids(length, &pid, &states);
- if (err != OK) {
- ALOGE("getProcessStatesFromPids failed");
- return false;
- }
- ALOGV("pid %d states %d", pid, states);
- if (states < 0) {
- return false;
- }
-
- // Use process state as the priority. Lower the value, higher the priority.
- *priority = states;
- return true;
- }
-
-protected:
- virtual ~ProcessInfo() {}
-
-private:
- DISALLOW_EVIL_CONSTRUCTORS(ProcessInfo);
-};
-
bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2) {
if (sessionId1.size() != sessionId2.size()) {
return false;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 4bccfa8..a2ec51c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -566,12 +566,22 @@
}
bool NuPlayer::Renderer::onDrainAudioQueue() {
-#if 0
+ // TODO: This call to getPosition checks if AudioTrack has been created
+ // in AudioSink before draining audio. If AudioTrack doesn't exist, then
+ // CHECKs on getPosition will fail.
+ // We still need to figure out why AudioTrack is not created when
+ // this function is called. One possible reason could be leftover
+ // audio. Another possible place is to check whether decoder
+ // has received INFO_FORMAT_CHANGED as the first buffer since
+ // AudioSink is opened there, and possible interactions with flush
+ // immediately after start. Investigate error message
+ // "vorbis_dsp_synthesis returned -135", along with RTSP.
uint32_t numFramesPlayed;
if (mAudioSink->getPosition(&numFramesPlayed) != OK) {
return false;
}
+#if 0
ssize_t numFramesAvailableToWrite =
mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
index 27b482b..d3e760b 100644
--- a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -23,8 +23,8 @@
#include "Drm.h"
#include "DrmSessionClientInterface.h"
#include "DrmSessionManager.h"
-#include "ProcessInfoInterface.h"
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/ProcessInfoInterface.h>
namespace android {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 31e10ce..97f3e20 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1591,7 +1591,11 @@
if (!msg->findInt32("channel-count", &numChannels)) {
err = INVALID_OPERATION;
} else {
- err = setupG711Codec(encoder, numChannels);
+ int32_t sampleRate;
+ if (!msg->findInt32("sample-rate", &sampleRate)) {
+ sampleRate = 8000;
+ }
+ err = setupG711Codec(encoder, sampleRate, numChannels);
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) {
int32_t numChannels, sampleRate, compressionLevel = -1;
@@ -2066,11 +2070,11 @@
1 /* numChannels */);
}
-status_t ACodec::setupG711Codec(bool encoder, int32_t numChannels) {
+status_t ACodec::setupG711Codec(bool encoder, int32_t sampleRate, int32_t numChannels) {
CHECK(!encoder); // XXX TODO
return setupRawAudioFormat(
- kPortIndexInput, 8000 /* sampleRate */, numChannels);
+ kPortIndexInput, sampleRate, numChannels);
}
status_t ACodec::setupFlacCodec(
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 38f2e34..177293d 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -47,6 +47,7 @@
OMXClient.cpp \
OMXCodec.cpp \
OggExtractor.cpp \
+ ProcessInfo.cpp \
SampleIterator.cpp \
SampleTable.cpp \
SkipCutBuffer.cpp \
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index ea19ab2..4d30069 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -629,10 +629,14 @@
// These are PCM-like formats with a fixed sample rate but
// a variable number of channels.
+ int32_t sampleRate;
int32_t numChannels;
CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+ if (!meta->findInt32(kKeySampleRate, &sampleRate)) {
+ sampleRate = 8000;
+ }
- setG711Format(numChannels);
+ setG711Format(sampleRate, numChannels);
} else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mMIME)) {
CHECK(!mIsEncoder);
@@ -3616,9 +3620,9 @@
sizeof(def));
}
-void OMXCodec::setG711Format(int32_t numChannels) {
+void OMXCodec::setG711Format(int32_t sampleRate, int32_t numChannels) {
CHECK(!mIsEncoder);
- setRawAudioFormat(kPortIndexInput, 8000, numChannels);
+ setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
}
void OMXCodec::setImageOutputFormat(
diff --git a/media/libstagefright/ProcessInfo.cpp b/media/libstagefright/ProcessInfo.cpp
new file mode 100644
index 0000000..b4172b3
--- /dev/null
+++ b/media/libstagefright/ProcessInfo.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ProcessInfo"
+#include <utils/Log.h>
+
+#include <media/stagefright/ProcessInfo.h>
+
+#include <binder/IProcessInfoService.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+ProcessInfo::ProcessInfo() {}
+
+bool ProcessInfo::getPriority(int pid, int* priority) {
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("processinfo"));
+ sp<IProcessInfoService> service = interface_cast<IProcessInfoService>(binder);
+
+ size_t length = 1;
+ int32_t states;
+ status_t err = service->getProcessStatesFromPids(length, &pid, &states);
+ if (err != OK) {
+ ALOGE("getProcessStatesFromPids failed");
+ return false;
+ }
+ ALOGV("pid %d states %d", pid, states);
+ if (states < 0) {
+ return false;
+ }
+
+ // Use process state as the priority. Lower the value, higher the priority.
+ *priority = states;
+ return true;
+}
+
+ProcessInfo::~ProcessInfo() {}
+
+} // namespace android
diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.cpp b/media/libstagefright/codecs/g711/dec/SoftG711.cpp
index 3a69095..015515e 100644
--- a/media/libstagefright/codecs/g711/dec/SoftG711.cpp
+++ b/media/libstagefright/codecs/g711/dec/SoftG711.cpp
@@ -41,8 +41,9 @@
OMX_COMPONENTTYPE **component)
: SimpleSoftOMXComponent(name, callbacks, appData, component),
mIsMLaw(true),
+ mSignalledError(false),
mNumChannels(1),
- mSignalledError(false) {
+ mSamplingRate(8000) {
if (!strcmp(name, "OMX.google.g711.alaw.decoder")) {
mIsMLaw = false;
} else {
@@ -129,7 +130,7 @@
pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
pcmParams->nChannels = mNumChannels;
- pcmParams->nSamplingRate = 8000;
+ pcmParams->nSamplingRate = mSamplingRate;
return OMX_ErrorNone;
}
@@ -159,6 +160,8 @@
mNumChannels = pcmParams->nChannels;
}
+ mSamplingRate = pcmParams->nSamplingRate;
+
return OMX_ErrorNone;
}
diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.h b/media/libstagefright/codecs/g711/dec/SoftG711.h
index bff0c68..16b6340 100644
--- a/media/libstagefright/codecs/g711/dec/SoftG711.h
+++ b/media/libstagefright/codecs/g711/dec/SoftG711.h
@@ -46,8 +46,9 @@
};
bool mIsMLaw;
- OMX_U32 mNumChannels;
bool mSignalledError;
+ OMX_U32 mNumChannels;
+ int32_t mSamplingRate;
void initPorts();
diff --git a/media/libstagefright/data/media_codecs_google_audio.xml b/media/libstagefright/data/media_codecs_google_audio.xml
index a06684b..b957b0c 100644
--- a/media/libstagefright/data/media_codecs_google_audio.xml
+++ b/media/libstagefright/data/media_codecs_google_audio.xml
@@ -38,12 +38,12 @@
</MediaCodec>
<MediaCodec name="OMX.google.g711.alaw.decoder" type="audio/g711-alaw">
<Limit name="channel-count" max="1" />
- <Limit name="sample-rate" ranges="8000" />
+ <Limit name="sample-rate" ranges="8000-48000" />
<Limit name="bitrate" range="64000" />
</MediaCodec>
<MediaCodec name="OMX.google.g711.mlaw.decoder" type="audio/g711-mlaw">
<Limit name="channel-count" max="1" />
- <Limit name="sample-rate" ranges="8000" />
+ <Limit name="sample-rate" ranges="8000-48000" />
<Limit name="bitrate" range="64000" />
</MediaCodec>
<MediaCodec name="OMX.google.vorbis.decoder" type="audio/vorbis">
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index a8f60a8..738f8b6 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -50,13 +50,75 @@
namespace android {
// static
-// Number of recently-read bytes to use for bandwidth estimation
-const size_t LiveSession::kBandwidthHistoryBytes = 200 * 1024;
// High water mark to start up switch or report prepared)
const int64_t LiveSession::kHighWaterMark = 8000000ll;
const int64_t LiveSession::kMidWaterMark = 5000000ll;
const int64_t LiveSession::kLowWaterMark = 3000000ll;
+struct LiveSession::BandwidthEstimator : public RefBase {
+ BandwidthEstimator();
+
+ void addBandwidthMeasurement(size_t numBytes, int64_t delayUs);
+ bool estimateBandwidth(int32_t *bandwidth);
+
+private:
+ // Bandwidth estimation parameters
+ static const int32_t kMaxBandwidthHistoryItems = 20;
+ static const int64_t kMaxBandwidthHistoryWindowUs = 3000000ll; // 3 sec
+
+ struct BandwidthEntry {
+ int64_t mDelayUs;
+ size_t mNumBytes;
+ };
+
+ Mutex mLock;
+ List<BandwidthEntry> mBandwidthHistory;
+ int64_t mTotalTransferTimeUs;
+ size_t mTotalTransferBytes;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BandwidthEstimator);
+};
+
+LiveSession::BandwidthEstimator::BandwidthEstimator() :
+ mTotalTransferTimeUs(0),
+ mTotalTransferBytes(0) {
+}
+
+void LiveSession::BandwidthEstimator::addBandwidthMeasurement(
+ size_t numBytes, int64_t delayUs) {
+ AutoMutex autoLock(mLock);
+
+ BandwidthEntry entry;
+ entry.mDelayUs = delayUs;
+ entry.mNumBytes = numBytes;
+ mTotalTransferTimeUs += delayUs;
+ mTotalTransferBytes += numBytes;
+ mBandwidthHistory.push_back(entry);
+
+ // trim old samples, keeping at least kMaxBandwidthHistoryItems samples,
+ // and total transfer time at least kMaxBandwidthHistoryWindowUs.
+ while (mBandwidthHistory.size() > kMaxBandwidthHistoryItems) {
+ List<BandwidthEntry>::iterator it = mBandwidthHistory.begin();
+ if (mTotalTransferTimeUs - it->mDelayUs < kMaxBandwidthHistoryWindowUs) {
+ break;
+ }
+ mTotalTransferTimeUs -= it->mDelayUs;
+ mTotalTransferBytes -= it->mNumBytes;
+ mBandwidthHistory.erase(mBandwidthHistory.begin());
+ }
+}
+
+bool LiveSession::BandwidthEstimator::estimateBandwidth(int32_t *bandwidthBps) {
+ AutoMutex autoLock(mLock);
+
+ if (mBandwidthHistory.size() < 2) {
+ return false;
+ }
+
+ *bandwidthBps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs);
+ return true;
+}
+
LiveSession::LiveSession(
const sp<AMessage> ¬ify, uint32_t flags,
const sp<IMediaHTTPService> &httpService)
@@ -66,18 +128,17 @@
mInPreparationPhase(true),
mHTTPDataSource(new MediaHTTP(mHTTPService->makeHTTPConnection())),
mCurBandwidthIndex(-1),
+ mLastBandwidthBps(-1ll),
+ mBandwidthEstimator(new BandwidthEstimator()),
mStreamMask(0),
mNewStreamMask(0),
mSwapMask(0),
- mCheckBandwidthGeneration(0),
mSwitchGeneration(0),
mSubtitleGeneration(0),
mLastDequeuedTimeUs(0ll),
mRealTimeBaseUs(0ll),
mReconfigurationInProgress(false),
mSwitchInProgress(false),
- mDisconnectReplyID(0),
- mSeekReplyID(0),
mFirstTimeUsValid(false),
mFirstTimeUs(0),
mLastSeekTimeUs(0),
@@ -90,15 +151,7 @@
for (size_t i = 0; i < kMaxStreams; ++i) {
mPacketSources.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
mPacketSources2.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
- mBuffering[i] = false;
}
-
- size_t numHistoryItems = kBandwidthHistoryBytes /
- PlaylistFetcher::kDownloadBlockSize + 1;
- if (numHistoryItems < 5) {
- numHistoryItems = 5;
- }
- mHTTPDataSource->setBandwidthHistorySize(numHistoryItems);
}
LiveSession::~LiveSession() {
@@ -107,24 +160,6 @@
}
}
-sp<ABuffer> LiveSession::createFormatChangeBuffer(bool swap) {
- ABuffer *discontinuity = new ABuffer(0);
- discontinuity->meta()->setInt32("discontinuity", ATSParser::DISCONTINUITY_FORMATCHANGE);
- discontinuity->meta()->setInt32("swapPacketSource", swap);
- discontinuity->meta()->setInt32("switchGeneration", mSwitchGeneration);
- discontinuity->meta()->setInt64("timeUs", -1);
- return discontinuity;
-}
-
-void LiveSession::swapPacketSource(StreamType stream) {
- sp<AnotherPacketSource> &aps = mPacketSources.editValueFor(stream);
- sp<AnotherPacketSource> &aps2 = mPacketSources2.editValueFor(stream);
- sp<AnotherPacketSource> tmp = aps;
- aps = aps2;
- aps2 = tmp;
- aps2->clear();
-}
-
status_t LiveSession::dequeueAccessUnit(
StreamType stream, sp<ABuffer> *accessUnit) {
if (!(mStreamMask & stream)) {
@@ -137,58 +172,22 @@
sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream);
ssize_t idx = typeToIndex(stream);
- if (!packetSource->hasBufferAvailable(&finalResult)) {
+ // Do not let client pull data if we don't have data packets yet.
+ // We might only have a format discontinuity queued without data.
+ // When NuPlayerDecoder dequeues the format discontinuity, it will
+ // immediately try to getFormat. If we return NULL, NuPlayerDecoder
+ // thinks it can do seamless change, so will not shutdown decoder.
+ // When the actual format arrives, it can't handle it and get stuck.
+ if (!packetSource->hasDataBufferAvailable(&finalResult)) {
if (finalResult == OK) {
- mBuffering[idx] = true;
return -EAGAIN;
} else {
return finalResult;
}
}
- int32_t targetDuration = 0;
- sp<AMessage> meta = packetSource->getLatestEnqueuedMeta();
- if (meta != NULL) {
- meta->findInt32("targetDuration", &targetDuration);
- }
-
- int64_t targetDurationUs = targetDuration * 1000000ll;
- if (targetDurationUs == 0 ||
- targetDurationUs > PlaylistFetcher::kMinBufferedDurationUs) {
- // Fetchers limit buffering to
- // min(3 * targetDuration, kMinBufferedDurationUs)
- targetDurationUs = PlaylistFetcher::kMinBufferedDurationUs;
- }
-
- if (mBuffering[idx]) {
- if (mSwitchInProgress
- || packetSource->isFinished(0)
- || packetSource->hasBufferAvailable(&finalResult)) {
- mBuffering[idx] = false;
- }
- }
-
- if (mBuffering[idx]) {
- return -EAGAIN;
- }
-
- // wait for counterpart
- sp<AnotherPacketSource> otherSource;
- uint32_t mask = mNewStreamMask & mStreamMask;
- uint32_t fetchersMask = 0;
- for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
- uint32_t fetcherMask = mFetcherInfos.valueAt(i).mFetcher->getStreamTypeMask();
- fetchersMask |= fetcherMask;
- }
- mask &= fetchersMask;
- if (stream == STREAMTYPE_AUDIO && (mask & STREAMTYPE_VIDEO)) {
- otherSource = mPacketSources.valueFor(STREAMTYPE_VIDEO);
- } else if (stream == STREAMTYPE_VIDEO && (mask & STREAMTYPE_AUDIO)) {
- otherSource = mPacketSources.valueFor(STREAMTYPE_AUDIO);
- }
- if (otherSource != NULL && !otherSource->hasBufferAvailable(&finalResult)) {
- return finalResult == OK ? -EAGAIN : finalResult;
- }
+ // Let the client dequeue as long as we have buffers available
+ // Do not make pause/resume decisions here.
status_t err = packetSource->dequeueAccessUnit(accessUnit);
@@ -227,41 +226,25 @@
type,
extra == NULL ? "NULL" : extra->debugString().c_str());
- int32_t swap;
- if ((*accessUnit)->meta()->findInt32("swapPacketSource", &swap) && swap) {
- int32_t switchGeneration;
- CHECK((*accessUnit)->meta()->findInt32("switchGeneration", &switchGeneration));
- {
- Mutex::Autolock lock(mSwapMutex);
- if (switchGeneration == mSwitchGeneration) {
- swapPacketSource(stream);
- sp<AMessage> msg = new AMessage(kWhatSwapped, this);
- msg->setInt32("stream", stream);
- msg->setInt32("switchGeneration", switchGeneration);
- msg->post();
- }
- }
+ size_t seq = strm.mCurDiscontinuitySeq;
+ int64_t offsetTimeUs;
+ if (mDiscontinuityOffsetTimesUs.indexOfKey(seq) >= 0) {
+ offsetTimeUs = mDiscontinuityOffsetTimesUs.valueFor(seq);
} else {
- size_t seq = strm.mCurDiscontinuitySeq;
- int64_t offsetTimeUs;
- if (mDiscontinuityOffsetTimesUs.indexOfKey(seq) >= 0) {
- offsetTimeUs = mDiscontinuityOffsetTimesUs.valueFor(seq);
- } else {
- offsetTimeUs = 0;
- }
-
- seq += 1;
- if (mDiscontinuityAbsStartTimesUs.indexOfKey(strm.mCurDiscontinuitySeq) >= 0) {
- int64_t firstTimeUs;
- firstTimeUs = mDiscontinuityAbsStartTimesUs.valueFor(strm.mCurDiscontinuitySeq);
- offsetTimeUs += strm.mLastDequeuedTimeUs - firstTimeUs;
- offsetTimeUs += strm.mLastSampleDurationUs;
- } else {
- offsetTimeUs += strm.mLastSampleDurationUs;
- }
-
- mDiscontinuityOffsetTimesUs.add(seq, offsetTimeUs);
+ offsetTimeUs = 0;
}
+
+ seq += 1;
+ if (mDiscontinuityAbsStartTimesUs.indexOfKey(strm.mCurDiscontinuitySeq) >= 0) {
+ int64_t firstTimeUs;
+ firstTimeUs = mDiscontinuityAbsStartTimesUs.valueFor(strm.mCurDiscontinuitySeq);
+ offsetTimeUs += strm.mLastDequeuedTimeUs - firstTimeUs;
+ offsetTimeUs += strm.mLastSampleDurationUs;
+ } else {
+ offsetTimeUs += strm.mLastSampleDurationUs;
+ }
+
+ mDiscontinuityOffsetTimesUs.add(seq, offsetTimeUs);
} else if (err == OK) {
if (stream == STREAMTYPE_AUDIO || stream == STREAMTYPE_VIDEO) {
@@ -322,7 +305,6 @@
}
status_t LiveSession::getStreamFormat(StreamType stream, sp<AMessage> *format) {
- // No swapPacketSource race condition; called from the same thread as dequeueAccessUnit.
if (!(mStreamMask & stream)) {
return UNKNOWN_ERROR;
}
@@ -338,6 +320,10 @@
return convertMetaDataToMessage(meta, format);
}
+sp<HTTPBase> LiveSession::getHTTPDataSource() {
+ return new MediaHTTP(mHTTPService->makeHTTPConnection());
+}
+
void LiveSession::connectAsync(
const char *url, const KeyedVector<String8, String8> *headers) {
sp<AMessage> msg = new AMessage(kWhatConnect, this);
@@ -417,21 +403,27 @@
case PlaylistFetcher::kWhatPaused:
case PlaylistFetcher::kWhatStopped:
{
+ AString uri;
+ CHECK(msg->findString("uri", &uri));
+ ssize_t index = mFetcherInfos.indexOfKey(uri);
+ if (index < 0) {
+ // ignore msgs from fetchers that's already gone
+ break;
+ }
+
if (what == PlaylistFetcher::kWhatStopped) {
- AString uri;
- CHECK(msg->findString("uri", &uri));
- ssize_t index = mFetcherInfos.indexOfKey(uri);
- if (index < 0) {
- // ignore duplicated kWhatStopped messages.
- break;
- }
+ tryToFinishBandwidthSwitch(uri);
mFetcherLooper->unregisterHandler(
mFetcherInfos[index].mFetcher->id());
mFetcherInfos.removeItemsAt(index);
-
- if (mSwitchInProgress) {
- tryToFinishBandwidthSwitch();
+ } else if (what == PlaylistFetcher::kWhatPaused) {
+ int32_t seekMode;
+ CHECK(msg->findInt32("seekMode", &seekMode));
+ for (size_t i = 0; i < kMaxStreams; ++i) {
+ if (mStreams[i].mUri == uri) {
+ mStreams[i].mSeekMode = (SeekMode) seekMode;
+ }
}
}
@@ -513,6 +505,13 @@
break;
}
+ AString uri;
+ CHECK(msg->findString("uri", &uri));
+ ssize_t index = mFetcherInfos.indexOfKey(uri);
+ if (index >= 0) {
+ mFetcherInfos.editValueAt(index).mToBeResumed = true;
+ }
+
// Resume fetcher for the original variant; the resumed fetcher should
// continue until the timestamps found in msg, which is stored by the
// new fetcher to indicate where the new variant has started buffering.
@@ -556,12 +555,6 @@
break;
}
- case kWhatSwapped:
- {
- onSwapped(msg);
- break;
- }
-
case kWhatPollBuffering:
{
int32_t generation;
@@ -700,11 +693,10 @@
}
void LiveSession::finishDisconnect() {
+ ALOGV("finishDisconnect");
+
// No reconfiguration is currently pending, make sure none will trigger
// during disconnection either.
-
- // Protect mPacketSources from a swapPacketSource race condition through disconnect.
- // (finishDisconnect, onFinishDisconnect2)
cancelBandwidthSwitch();
// cancel buffer polling
@@ -737,7 +729,7 @@
response->setInt32("err", OK);
response->postReply(mDisconnectReplyID);
- mDisconnectReplyID = 0;
+ mDisconnectReplyID.clear();
}
sp<PlaylistFetcher> LiveSession::addFetcher(const char *uri) {
@@ -754,8 +746,8 @@
FetcherInfo info;
info.mFetcher = new PlaylistFetcher(notify, this, uri, mSubtitleGeneration);
info.mDurationUs = -1ll;
- info.mIsPrepared = false;
info.mToBeRemoved = false;
+ info.mToBeResumed = false;
mFetcherLooper->registerHandler(info.mFetcher);
mFetcherInfos.add(uri, info);
@@ -784,14 +776,15 @@
int64_t range_offset, int64_t range_length,
uint32_t block_size, /* download block size */
sp<DataSource> *source, /* to return and reuse source */
- String8 *actualUrl) {
+ String8 *actualUrl,
+ bool forceConnectHTTP /* force connect HTTP when resuing source */) {
off64_t size;
sp<DataSource> temp_source;
if (source == NULL) {
source = &temp_source;
}
- if (*source == NULL) {
+ if (*source == NULL || forceConnectHTTP) {
if (!strncasecmp(url, "file://", 7)) {
*source = new FileSource(url + 7);
} else if (strncasecmp(url, "http://", 7)
@@ -810,13 +803,18 @@
? "" : AStringPrintf("%lld",
range_offset + range_length - 1).c_str()).c_str()));
}
- status_t err = mHTTPDataSource->connect(url, &headers);
+
+ HTTPBase* httpDataSource =
+ (*source == NULL) ? mHTTPDataSource.get() : (HTTPBase*)source->get();
+ status_t err = httpDataSource->connect(url, &headers);
if (err != OK) {
return err;
}
- *source = mHTTPDataSource;
+ if (*source == NULL) {
+ *source = mHTTPDataSource;
+ }
}
}
@@ -952,8 +950,66 @@
}
#endif
-size_t LiveSession::getBandwidthIndex() {
- if (mBandwidthItems.size() == 0) {
+float LiveSession::getAbortThreshold(
+ ssize_t currentBWIndex, ssize_t targetBWIndex) const {
+ float abortThreshold = -1.0f;
+ if (currentBWIndex > 0 && targetBWIndex < currentBWIndex) {
+ /*
+ If we're switching down, we need to decide whether to
+
+ 1) finish last segment of high-bandwidth variant, or
+ 2) abort last segment of high-bandwidth variant, and fetch an
+ overlapping portion from low-bandwidth variant.
+
+ Here we try to maximize the amount of buffer left when the
+ switch point is met. Given the following parameters:
+
+ B: our current buffering level in seconds
+ T: target duration in seconds
+ X: sample duration in seconds remain to fetch in last segment
+ bw0: bandwidth of old variant (as specified in playlist)
+ bw1: bandwidth of new variant (as specified in playlist)
+ bw: measured bandwidth available
+
+ If we choose 1), when switch happens at the end of current
+ segment, our buffering will be
+ B + X - X * bw0 / bw
+
+ If we choose 2), when switch happens where we aborted current
+ segment, our buffering will be
+ B - (T - X) * bw1 / bw
+
+ We should only choose 1) if
+ X/T < bw1 / (bw1 + bw0 - bw)
+ */
+
+ CHECK(mLastBandwidthBps >= 0);
+ abortThreshold =
+ (float)mBandwidthItems.itemAt(targetBWIndex).mBandwidth
+ / ((float)mBandwidthItems.itemAt(targetBWIndex).mBandwidth
+ + (float)mBandwidthItems.itemAt(currentBWIndex).mBandwidth
+ - (float)mLastBandwidthBps * 0.7f);
+ if (abortThreshold < 0.0f) {
+ abortThreshold = -1.0f; // do not abort
+ }
+ ALOGV("Switching Down: bps %ld => %ld, measured %d, abort ratio %.2f",
+ mBandwidthItems.itemAt(currentBWIndex).mBandwidth,
+ mBandwidthItems.itemAt(targetBWIndex).mBandwidth,
+ mLastBandwidthBps,
+ abortThreshold);
+ }
+ return abortThreshold;
+}
+
+void LiveSession::addBandwidthMeasurement(size_t numBytes, int64_t delayUs) {
+ mBandwidthEstimator->addBandwidthMeasurement(numBytes, delayUs);
+}
+
+size_t LiveSession::getBandwidthIndex(int32_t bandwidthBps) {
+ if (mBandwidthItems.size() < 2) {
+ // shouldn't be here if we only have 1 bandwidth, check
+ // logic to get rid of redundant bandwidth polling
+ ALOGW("getBandwidthIndex() called for single bandwidth playlist!");
return 0;
}
@@ -971,15 +1027,6 @@
}
if (index < 0) {
- int32_t bandwidthBps;
- if (mHTTPDataSource != NULL
- && mHTTPDataSource->estimateBandwidth(&bandwidthBps)) {
- ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
- } else {
- ALOGV("no bandwidth estimate.");
- return 0; // Pick the lowest bandwidth stream by default.
- }
-
char value[PROPERTY_VALUE_MAX];
if (property_get("media.httplive.max-bw", value, NULL)) {
char *end;
@@ -996,15 +1043,9 @@
index = mBandwidthItems.size() - 1;
while (index > 0) {
- // consider only 80% of the available bandwidth, but if we are switching up,
- // be even more conservative (70%) to avoid overestimating and immediately
- // switching back.
- size_t adjustedBandwidthBps = bandwidthBps;
- if (index > mCurBandwidthIndex) {
- adjustedBandwidthBps = adjustedBandwidthBps * 7 / 10;
- } else {
- adjustedBandwidthBps = adjustedBandwidthBps * 8 / 10;
- }
+ // be conservative (70%) to avoid overestimating and immediately
+ // switching down again.
+ size_t adjustedBandwidthBps = bandwidthBps * 7 / 10;
if (mBandwidthItems.itemAt(index).mBandwidth <= adjustedBandwidthBps) {
break;
}
@@ -1167,8 +1208,6 @@
CHECK(!mReconfigurationInProgress);
mReconfigurationInProgress = true;
- mCurBandwidthIndex = bandwidthIndex;
-
ALOGV("changeConfiguration => timeUs:%" PRId64 " us, bwIndex:%zu, pickTrack:%d",
timeUs, bandwidthIndex, pickTrack);
@@ -1192,28 +1231,41 @@
bool discardFetcher = true;
- // If we're seeking all current fetchers are discarded.
if (timeUs < 0ll) {
// delay fetcher removal if not picking tracks
discardFetcher = pickTrack;
+ }
- for (size_t j = 0; j < kMaxStreams; ++j) {
- StreamType type = indexToType(j);
- if ((streamMask & type) && uri == URIs[j]) {
- resumeMask |= type;
- streamMask &= ~type;
- discardFetcher = false;
- }
+ for (size_t j = 0; j < kMaxStreams; ++j) {
+ StreamType type = indexToType(j);
+ if ((streamMask & type) && uri == URIs[j]) {
+ resumeMask |= type;
+ streamMask &= ~type;
+ discardFetcher = false;
}
}
if (discardFetcher) {
mFetcherInfos.valueAt(i).mFetcher->stopAsync();
} else {
- mFetcherInfos.valueAt(i).mFetcher->pauseAsync();
+ float threshold = -1.0f; // always finish fetching by default
+ if (timeUs >= 0ll) {
+ // seeking, no need to finish fetching
+ threshold = 0.0f;
+ } else if (!pickTrack) {
+ // adapting, abort if remaining of current segment is over threshold
+ threshold = getAbortThreshold(
+ mCurBandwidthIndex, bandwidthIndex);
+ }
+
+ ALOGV("Pausing with threshold %.3f", threshold);
+
+ mFetcherInfos.valueAt(i).mFetcher->pauseAsync(threshold);
}
}
+ mCurBandwidthIndex = bandwidthIndex;
+
sp<AMessage> msg;
if (timeUs < 0ll) {
// skip onChangeConfiguration2 (decoder destruction) if not seeking.
@@ -1274,11 +1326,11 @@
mDiscontinuityOffsetTimesUs.clear();
mDiscontinuityAbsStartTimesUs.clear();
- if (mSeekReplyID != 0) {
+ if (mSeekReplyID != NULL) {
CHECK(mSeekReply != NULL);
mSeekReply->setInt32("err", OK);
mSeekReply->postReply(mSeekReplyID);
- mSeekReplyID = 0;
+ mSeekReplyID.clear();
mSeekReply.clear();
}
}
@@ -1287,9 +1339,6 @@
CHECK(msg->findInt32("streamMask", (int32_t *)&streamMask));
CHECK(msg->findInt32("resumeMask", (int32_t *)&resumeMask));
- // currently onChangeConfiguration2 is only called for seeking;
- // remove the following CHECK if using it else where.
- CHECK_EQ(resumeMask, 0);
streamMask |= resumeMask;
AString URIs[kMaxStreams];
@@ -1301,17 +1350,25 @@
}
}
- // Determine which decoders to shutdown on the player side,
- // a decoder has to be shutdown if either
- // 1) its streamtype was active before but now longer isn't.
- // or
- // 2) its streamtype was already active and still is but the URI
- // has changed.
uint32_t changedMask = 0;
for (size_t i = 0; i < kMaxStreams && i != kSubtitleIndex; ++i) {
- if (((mStreamMask & streamMask & indexToType(i))
- && !(URIs[i] == mStreams[i].mUri))
- || (mStreamMask & ~streamMask & indexToType(i))) {
+ // stream URI could change even if onChangeConfiguration2 is only
+ // used for seek. Seek could happen during a bw switch, in this
+ // case bw switch will be cancelled, but the seekTo position will
+ // fetch from the new URI.
+ if ((mStreamMask & streamMask & indexToType(i))
+ && !mStreams[i].mUri.empty()
+ && !(URIs[i] == mStreams[i].mUri)) {
+ ALOGV("stream %zu changed: oldURI %s, newURI %s", i,
+ mStreams[i].mUri.c_str(), URIs[i].c_str());
+ sp<AnotherPacketSource> source = mPacketSources.valueFor(indexToType(i));
+ source->queueDiscontinuity(
+ ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
+ }
+ // Determine which decoders to shutdown on the player side,
+ // a decoder has to be shutdown if its streamtype was active
+ // before but now longer isn't.
+ if ((mStreamMask & ~streamMask & indexToType(i))) {
changedMask |= indexToType(i);
}
}
@@ -1394,14 +1451,13 @@
if (sources[kAudioIndex] != NULL || sources[kVideoIndex] != NULL
|| sources[kSubtitleIndex] != NULL) {
info.mFetcher->startAsync(
- sources[kAudioIndex], sources[kVideoIndex], sources[kSubtitleIndex]);
+ sources[kAudioIndex], sources[kVideoIndex], sources[kSubtitleIndex], timeUs);
} else {
info.mToBeRemoved = true;
}
}
// streamMask now only contains the types that need a new fetcher created.
-
if (streamMask != 0) {
ALOGV("creating new fetchers for mask 0x%08x", streamMask);
}
@@ -1422,6 +1478,7 @@
int64_t startTimeUs = -1;
int64_t segmentStartTimeUs = -1ll;
int32_t discontinuitySeq = -1;
+ SeekMode seekMode = kSeekModeExactPosition;
sp<AnotherPacketSource> sources[kMaxStreams];
if (i == kSubtitleIndex) {
@@ -1441,6 +1498,16 @@
sp<AMessage> meta;
if (pickTrack) {
// selecting
+
+ // FIXME:
+ // This should only apply to the track that's being picked, we
+ // need a bitmask to indicate that.
+ //
+ // It's possible that selectTrack() gets called during a bandwidth
+ // switch, and we needed to fetch a new variant. The new fetcher
+ // should start from where old fetcher left off, not where decoder
+ // is dequeueing at.
+
meta = sources[j]->getLatestDequeuedMeta();
} else {
// adapting
@@ -1474,17 +1541,25 @@
break;
}
- ALOGV("stream[%d]: queue format change", j);
+ ALOGV("stream[%zu]: queue format change", j);
sources[j]->queueDiscontinuity(
- ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
+ ATSParser::DISCONTINUITY_FORMAT_ONLY, NULL, true);
} else {
// adapting, queue discontinuities after resume
sources[j] = mPacketSources2.valueFor(indexToType(j));
sources[j]->clear();
uint32_t extraStreams = mNewStreamMask & (~mStreamMask);
if (extraStreams & indexToType(j)) {
- sources[j]->queueAccessUnit(createFormatChangeBuffer(/*swap*/ false));
+ sources[j]->queueDiscontinuity(
+ ATSParser::DISCONTINUITY_FORMAT_ONLY, NULL, true);
+ }
+ // the new fetcher might be providing streams that used to be
+ // provided by two different fetchers, if one of the fetcher
+ // paused in the middle while the other somehow paused in next
+ // seg, we have to start from next seg.
+ if (seekMode < mStreams[j].mSeekMode) {
+ seekMode = mStreams[j].mSeekMode;
}
}
}
@@ -1500,7 +1575,7 @@
startTimeUs < 0 ? mLastSeekTimeUs : startTimeUs,
segmentStartTimeUs,
discontinuitySeq,
- switching);
+ seekMode);
}
// All fetchers have now been started, the configuration change
@@ -1514,30 +1589,62 @@
mStreamMask = mNewStreamMask;
}
- if (mDisconnectReplyID != 0) {
+ if (mDisconnectReplyID != NULL) {
finishDisconnect();
}
}
-void LiveSession::onSwapped(const sp<AMessage> &msg) {
- int32_t switchGeneration;
- CHECK(msg->findInt32("switchGeneration", &switchGeneration));
- if (switchGeneration != mSwitchGeneration) {
+void LiveSession::swapPacketSource(StreamType stream) {
+ ALOGV("swapPacketSource: stream = %d", stream);
+
+ // transfer packets from source2 to source
+ sp<AnotherPacketSource> &aps = mPacketSources.editValueFor(stream);
+ sp<AnotherPacketSource> &aps2 = mPacketSources2.editValueFor(stream);
+
+ // queue discontinuity in mPacketSource
+ aps->queueDiscontinuity(ATSParser::DISCONTINUITY_FORMAT_ONLY, NULL, false);
+
+ // queue packets in mPacketSource2 to mPacketSource
+ status_t finalResult = OK;
+ sp<ABuffer> accessUnit;
+ while (aps2->hasBufferAvailable(&finalResult) && finalResult == OK &&
+ OK == aps2->dequeueAccessUnit(&accessUnit)) {
+ aps->queueAccessUnit(accessUnit);
+ }
+ aps2->clear();
+}
+
+void LiveSession::tryToFinishBandwidthSwitch(const AString &uri) {
+ if (!mSwitchInProgress) {
return;
}
- int32_t stream;
- CHECK(msg->findInt32("stream", &stream));
-
- ssize_t idx = typeToIndex(stream);
- CHECK(idx >= 0);
- if ((mNewStreamMask & stream) && mStreams[idx].mNewUri.empty()) {
- ALOGW("swapping stream type %d %s to empty stream", stream, mStreams[idx].mUri.c_str());
+ ssize_t index = mFetcherInfos.indexOfKey(uri);
+ if (index < 0 || !mFetcherInfos[index].mToBeRemoved) {
+ return;
}
- mStreams[idx].mUri = mStreams[idx].mNewUri;
- mStreams[idx].mNewUri.clear();
- mSwapMask &= ~stream;
+ // Swap packet source of streams provided by old variant
+ for (size_t idx = 0; idx < kMaxStreams; idx++) {
+ if (uri == mStreams[idx].mUri) {
+ StreamType stream = indexToType(idx);
+
+ swapPacketSource(stream);
+
+ if ((mNewStreamMask & stream) && mStreams[idx].mNewUri.empty()) {
+ ALOGW("swapping stream type %d %s to empty stream",
+ stream, mStreams[idx].mUri.c_str());
+ }
+ mStreams[idx].mUri = mStreams[idx].mNewUri;
+ mStreams[idx].mNewUri.clear();
+
+ mSwapMask &= ~stream;
+ }
+ }
+
+ mFetcherInfos.editValueAt(index).mToBeRemoved = false;
+
+ ALOGV("tryToFinishBandwidthSwitch: mSwapMask=%x", mSwapMask);
if (mSwapMask != 0) {
return;
}
@@ -1545,21 +1652,50 @@
// Check if new variant contains extra streams.
uint32_t extraStreams = mNewStreamMask & (~mStreamMask);
while (extraStreams) {
- StreamType extraStream = (StreamType) (extraStreams & ~(extraStreams - 1));
- swapPacketSource(extraStream);
- extraStreams &= ~extraStream;
+ StreamType stream = (StreamType) (extraStreams & ~(extraStreams - 1));
+ extraStreams &= ~stream;
- idx = typeToIndex(extraStream);
+ swapPacketSource(stream);
+
+ ssize_t idx = typeToIndex(stream);
CHECK(idx >= 0);
if (mStreams[idx].mNewUri.empty()) {
ALOGW("swapping extra stream type %d %s to empty stream",
- extraStream, mStreams[idx].mUri.c_str());
+ stream, mStreams[idx].mUri.c_str());
}
mStreams[idx].mUri = mStreams[idx].mNewUri;
mStreams[idx].mNewUri.clear();
}
- tryToFinishBandwidthSwitch();
+ // Restart new fetcher (it was paused after the first 47k block)
+ // and let it fetch into mPacketSources (not mPacketSources2)
+ for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
+ FetcherInfo &info = mFetcherInfos.editValueAt(i);
+ if (info.mToBeResumed) {
+ const AString &uri = mFetcherInfos.keyAt(i);
+ sp<AnotherPacketSource> sources[kMaxStreams];
+ for (size_t j = 0; j < kMaxStreams; ++j) {
+ if (uri == mStreams[j].mUri) {
+ sources[j] = mPacketSources.valueFor(indexToType(j));
+ }
+ }
+ if (sources[kAudioIndex] != NULL
+ || sources[kVideoIndex] != NULL
+ || sources[kSubtitleIndex] != NULL) {
+ ALOGV("resuming fetcher %s", uri.c_str());
+ info.mFetcher->startAsync(
+ sources[kAudioIndex],
+ sources[kVideoIndex],
+ sources[kSubtitleIndex]);
+ }
+ info.mToBeResumed = false;
+ }
+ }
+
+ mStreamMask = mNewStreamMask;
+ mSwitchInProgress = false;
+
+ ALOGI("#### Finished Bandwidth Switch");
}
void LiveSession::schedulePollBuffering() {
@@ -1574,9 +1710,9 @@
void LiveSession::onPollBuffering() {
ALOGV("onPollBuffering: mSwitchInProgress %d, mReconfigurationInProgress %d, "
- "mInPreparationPhase %d, mStreamMask 0x%x",
+ "mInPreparationPhase %d, mCurBandwidthIndex %zd, mStreamMask 0x%x",
mSwitchInProgress, mReconfigurationInProgress,
- mInPreparationPhase, mStreamMask);
+ mInPreparationPhase, mCurBandwidthIndex, mStreamMask);
bool low, mid, high;
if (checkBuffering(low, mid, high)) {
@@ -1585,38 +1721,17 @@
}
// don't switch before we report prepared
- if (!mInPreparationPhase && (low || high)) {
- switchBandwidthIfNeeded(high);
+ if (!mInPreparationPhase) {
+ switchBandwidthIfNeeded(high, !mid);
}
}
schedulePollBuffering();
}
-// Mark switch done when:
-// 1. all old buffers are swapped out
-void LiveSession::tryToFinishBandwidthSwitch() {
- if (!mSwitchInProgress) {
- return;
- }
-
- bool needToRemoveFetchers = false;
- for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
- if (mFetcherInfos.valueAt(i).mToBeRemoved) {
- needToRemoveFetchers = true;
- break;
- }
- }
-
- if (!needToRemoveFetchers && mSwapMask == 0) {
- ALOGI("mSwitchInProgress = false");
- mStreamMask = mNewStreamMask;
- mSwitchInProgress = false;
- }
-}
-
void LiveSession::cancelBandwidthSwitch() {
- Mutex::Autolock lock(mSwapMutex);
+ ALOGV("cancelBandwidthSwitch: mSwitchGen(%d)++", mSwitchGeneration);
+
mSwitchGeneration++;
mSwitchInProgress = false;
mSwapMask = 0;
@@ -1679,7 +1794,7 @@
++activeCount;
int64_t bufferedDurationUs =
mPacketSources[i]->getEstimatedDurationUs();
- ALOGV("source[%d]: buffered %lld us", i, bufferedDurationUs);
+ ALOGV("source[%zu]: buffered %lld us", i, (long long)bufferedDurationUs);
if (bufferedDurationUs < kLowWaterMark) {
++lowCount;
break;
@@ -1701,11 +1816,36 @@
return false;
}
-void LiveSession::switchBandwidthIfNeeded(bool canSwitchUp) {
- ssize_t bandwidthIndex = getBandwidthIndex();
+void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
+ // no need to check bandwidth if we only have 1 bandwidth settings
+ if (mBandwidthItems.size() < 2) {
+ return;
+ }
- if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex)
- || (!canSwitchUp && bandwidthIndex < mCurBandwidthIndex)) {
+ int32_t bandwidthBps;
+ if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps)) {
+ ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
+ mLastBandwidthBps = bandwidthBps;
+ } else {
+ ALOGV("no bandwidth estimate.");
+ return;
+ }
+
+ int32_t curBandwidth = mBandwidthItems.itemAt(mCurBandwidthIndex).mBandwidth;
+ bool bandwidthLow = bandwidthBps < (int32_t)curBandwidth * 8 / 10;
+ bool bandwidthHigh = bandwidthBps > (int32_t)curBandwidth * 12 / 10;
+
+ if ((bufferHigh && bandwidthHigh) || (bufferLow && bandwidthLow)) {
+ ssize_t bandwidthIndex = getBandwidthIndex(bandwidthBps);
+
+ if (bandwidthIndex == mCurBandwidthIndex
+ || (bufferHigh && bandwidthIndex < mCurBandwidthIndex)
+ || (bufferLow && bandwidthIndex > mCurBandwidthIndex)) {
+ return;
+ }
+
+ ALOGI("#### Starting Bandwidth Switch: %zd => %zd",
+ mCurBandwidthIndex, bandwidthIndex);
changeConfiguration(-1, bandwidthIndex, false);
}
}
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 3b0a9a4..cbf988e 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -54,6 +54,12 @@
STREAMTYPE_SUBTITLES = 1 << kSubtitleIndex,
};
+ enum SeekMode {
+ kSeekModeExactPosition = 0, // used for seeking
+ kSeekModeNextSample = 1, // used for seamless switching
+ kSeekModeNextSegment = 2, // used for seamless switching
+ };
+
LiveSession(
const sp<AMessage> ¬ify,
uint32_t flags,
@@ -63,6 +69,8 @@
status_t getStreamFormat(StreamType stream, sp<AMessage> *format);
+ sp<HTTPBase> getHTTPDataSource();
+
void connectAsync(
const char *url,
const KeyedVector<String8, String8> *headers = NULL);
@@ -88,11 +96,6 @@
kWhatPreparationFailed,
};
- // create a format-change discontinuity
- //
- // swap:
- // whether is format-change discontinuity should trigger a buffer swap
- sp<ABuffer> createFormatChangeBuffer(bool swap = true);
protected:
virtual ~LiveSession();
@@ -106,20 +109,18 @@
kWhatDisconnect = 'disc',
kWhatSeek = 'seek',
kWhatFetcherNotify = 'notf',
- kWhatCheckBandwidth = 'bndw',
kWhatChangeConfiguration = 'chC0',
kWhatChangeConfiguration2 = 'chC2',
kWhatChangeConfiguration3 = 'chC3',
kWhatFinishDisconnect2 = 'fin2',
- kWhatSwapped = 'swap',
kWhatPollBuffering = 'poll',
};
- static const size_t kBandwidthHistoryBytes;
static const int64_t kHighWaterMark;
static const int64_t kMidWaterMark;
static const int64_t kLowWaterMark;
+ struct BandwidthEstimator;
struct BandwidthItem {
size_t mPlaylistIndex;
unsigned long mBandwidth;
@@ -128,23 +129,22 @@
struct FetcherInfo {
sp<PlaylistFetcher> mFetcher;
int64_t mDurationUs;
- bool mIsPrepared;
bool mToBeRemoved;
+ bool mToBeResumed;
};
struct StreamItem {
const char *mType;
AString mUri, mNewUri;
+ SeekMode mSeekMode;
size_t mCurDiscontinuitySeq;
int64_t mLastDequeuedTimeUs;
int64_t mLastSampleDurationUs;
StreamItem()
- : mType(""),
- mCurDiscontinuitySeq(0),
- mLastDequeuedTimeUs(0),
- mLastSampleDurationUs(0) {}
+ : StreamItem("") {}
StreamItem(const char *type)
: mType(type),
+ mSeekMode(kSeekModeExactPosition),
mCurDiscontinuitySeq(0),
mLastDequeuedTimeUs(0),
mLastSampleDurationUs(0) {}
@@ -170,6 +170,8 @@
Vector<BandwidthItem> mBandwidthItems;
ssize_t mCurBandwidthIndex;
+ int32_t mLastBandwidthBps;
+ sp<BandwidthEstimator> mBandwidthEstimator;
sp<M3UParser> mPlaylist;
@@ -190,12 +192,6 @@
// A second set of packet sources that buffer content for the variant we're switching to.
KeyedVector<StreamType, sp<AnotherPacketSource> > mPacketSources2;
- // A mutex used to serialize two sets of events:
- // * the swapping of packet sources in dequeueAccessUnit on the player thread, AND
- // * a forced bandwidth switch termination in cancelSwitch on the live looper.
- Mutex mSwapMutex;
-
- int32_t mCheckBandwidthGeneration;
int32_t mSwitchGeneration;
int32_t mSubtitleGeneration;
@@ -244,12 +240,17 @@
uint32_t block_size = 0,
/* reuse DataSource if doing partial fetch */
sp<DataSource> *source = NULL,
- String8 *actualUrl = NULL);
+ String8 *actualUrl = NULL,
+ /* force connect http even when resuing DataSource */
+ bool forceConnectHTTP = false);
sp<M3UParser> fetchPlaylist(
const char *url, uint8_t *curPlaylistHash, bool *unchanged);
- size_t getBandwidthIndex();
+ float getAbortThreshold(
+ ssize_t currentBWIndex, ssize_t targetBWIndex) const;
+ void addBandwidthMeasurement(size_t numBytes, int64_t delayUs);
+ size_t getBandwidthIndex(int32_t bandwidthBps);
int64_t latestMediaSegmentStartTimeUs();
static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);
@@ -261,26 +262,21 @@
void onChangeConfiguration(const sp<AMessage> &msg);
void onChangeConfiguration2(const sp<AMessage> &msg);
void onChangeConfiguration3(const sp<AMessage> &msg);
- void onSwapped(const sp<AMessage> &msg);
- void tryToFinishBandwidthSwitch();
+ void swapPacketSource(StreamType stream);
+ void tryToFinishBandwidthSwitch(const AString &uri);
- // cancelBandwidthSwitch is atomic wrt swapPacketSource; call it to prevent packet sources
- // from being swapped out on stale discontinuities while manipulating
- // mPacketSources/mPacketSources2.
void cancelBandwidthSwitch();
void schedulePollBuffering();
void cancelPollBuffering();
void onPollBuffering();
bool checkBuffering(bool &low, bool &mid, bool &high);
- void switchBandwidthIfNeeded(bool canSwitchUp);
+ void switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow);
void finishDisconnect();
void postPrepared(status_t err);
- void swapPacketSource(StreamType stream);
-
DISALLOW_EVIL_CONSTRUCTORS(LiveSession);
};
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 997b694..3c5d7cf 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -251,6 +251,7 @@
mIsComplete(false),
mIsEvent(false),
mDiscontinuitySeq(0),
+ mDiscontinuityCount(0),
mSelectedIndex(-1) {
mInitCheck = parse(data, size);
}
@@ -582,6 +583,7 @@
itemMeta = new AMessage;
}
itemMeta->setInt32("discontinuity", true);
+ ++mDiscontinuityCount;
} else if (line.startsWith("#EXT-X-STREAM-INF")) {
if (mMeta != NULL) {
return ERROR_MALFORMED;
@@ -609,6 +611,9 @@
} else if (line.startsWith("#EXT-X-MEDIA")) {
err = parseMedia(line);
} else if (line.startsWith("#EXT-X-DISCONTINUITY-SEQUENCE")) {
+ if (mIsVariantPlaylist) {
+ return ERROR_MALFORMED;
+ }
size_t seq;
err = parseDiscontinuitySequence(line, &seq);
if (err == OK) {
@@ -628,6 +633,7 @@
|| !itemMeta->findInt64("durationUs", &durationUs)) {
return ERROR_MALFORMED;
}
+ itemMeta->setInt32("discontinuity-sequence", mDiscontinuitySeq + mDiscontinuityCount);
}
mItems.push();
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index 1cad060..d475683 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -70,6 +70,7 @@
bool mIsComplete;
bool mIsEvent;
size_t mDiscontinuitySeq;
+ int32_t mDiscontinuityCount;
sp<AMessage> mMeta;
Vector<Item> mItems;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 3710686..68f0357 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -54,13 +54,98 @@
const int32_t PlaylistFetcher::kDownloadBlockSize = 47 * 1024;
const int32_t PlaylistFetcher::kNumSkipFrames = 5;
+struct PlaylistFetcher::DownloadState : public RefBase {
+ DownloadState();
+ void resetState();
+ bool hasSavedState() const;
+ void restoreState(
+ AString &uri,
+ sp<AMessage> &itemMeta,
+ sp<ABuffer> &buffer,
+ sp<ABuffer> &tsBuffer,
+ int32_t &firstSeqNumberInPlaylist,
+ int32_t &lastSeqNumberInPlaylist);
+ void saveState(
+ AString &uri,
+ sp<AMessage> &itemMeta,
+ sp<ABuffer> &buffer,
+ sp<ABuffer> &tsBuffer,
+ int32_t &firstSeqNumberInPlaylist,
+ int32_t &lastSeqNumberInPlaylist);
+
+private:
+ bool mHasSavedState;
+ AString mUri;
+ sp<AMessage> mItemMeta;
+ sp<ABuffer> mBuffer;
+ sp<ABuffer> mTsBuffer;
+ int32_t mFirstSeqNumberInPlaylist;
+ int32_t mLastSeqNumberInPlaylist;
+};
+
+PlaylistFetcher::DownloadState::DownloadState() {
+ resetState();
+}
+
+bool PlaylistFetcher::DownloadState::hasSavedState() const {
+ return mHasSavedState;
+}
+
+void PlaylistFetcher::DownloadState::resetState() {
+ mHasSavedState = false;
+
+ mUri.clear();
+ mItemMeta = NULL;
+ mBuffer = NULL;
+ mTsBuffer = NULL;
+ mFirstSeqNumberInPlaylist = 0;
+ mLastSeqNumberInPlaylist = 0;
+}
+
+void PlaylistFetcher::DownloadState::restoreState(
+ AString &uri,
+ sp<AMessage> &itemMeta,
+ sp<ABuffer> &buffer,
+ sp<ABuffer> &tsBuffer,
+ int32_t &firstSeqNumberInPlaylist,
+ int32_t &lastSeqNumberInPlaylist) {
+ if (!mHasSavedState) {
+ return;
+ }
+
+ uri = mUri;
+ itemMeta = mItemMeta;
+ buffer = mBuffer;
+ tsBuffer = mTsBuffer;
+ firstSeqNumberInPlaylist = mFirstSeqNumberInPlaylist;
+ lastSeqNumberInPlaylist = mLastSeqNumberInPlaylist;
+
+ resetState();
+}
+
+void PlaylistFetcher::DownloadState::saveState(
+ AString &uri,
+ sp<AMessage> &itemMeta,
+ sp<ABuffer> &buffer,
+ sp<ABuffer> &tsBuffer,
+ int32_t &firstSeqNumberInPlaylist,
+ int32_t &lastSeqNumberInPlaylist) {
+ mHasSavedState = true;
+
+ mUri = uri;
+ mItemMeta = itemMeta;
+ mBuffer = buffer;
+ mTsBuffer = tsBuffer;
+ mFirstSeqNumberInPlaylist = firstSeqNumberInPlaylist;
+ mLastSeqNumberInPlaylist = lastSeqNumberInPlaylist;
+}
+
PlaylistFetcher::PlaylistFetcher(
const sp<AMessage> ¬ify,
const sp<LiveSession> &session,
const char *uri,
int32_t subtitleGeneration)
: mNotify(notify),
- mStartTimeUsNotify(notify->dup()),
mSession(session),
mURI(uri),
mStreamTypeMask(0),
@@ -72,18 +157,21 @@
mSeqNumber(-1),
mNumRetries(0),
mStartup(true),
- mAdaptive(false),
+ mSeekMode(LiveSession::kSeekModeExactPosition),
mPrepared(false),
+ mTimeChangeSignaled(false),
mNextPTSTimeUs(-1ll),
mMonitorQueueGeneration(0),
mSubtitleGeneration(subtitleGeneration),
+ mLastDiscontinuitySeq(-1ll),
mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY),
mFirstPTSValid(false),
- mAbsoluteTimeAnchorUs(0ll),
- mVideoBuffer(new AnotherPacketSource(NULL)) {
+ mFirstTimeUs(-1ll),
+ mVideoBuffer(new AnotherPacketSource(NULL)),
+ mThresholdRatio(-1.0f),
+ mDownloadState(new DownloadState()) {
memset(mPlaylistHash, 0, sizeof(mPlaylistHash));
- mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
- mStartTimeUsNotify->setInt32("streamMask", 0);
+ mHTTPDataSource = mSession->getHTTPDataSource();
}
PlaylistFetcher::~PlaylistFetcher() {
@@ -335,6 +423,14 @@
++mMonitorQueueGeneration;
}
+void PlaylistFetcher::setStoppingThreshold(float thresholdRatio) {
+ AutoMutex _l(mThresholdLock);
+ if (mStreamTypeMask == LiveSession::STREAMTYPE_SUBTITLES) {
+ return;
+ }
+ mThresholdRatio = thresholdRatio;
+}
+
void PlaylistFetcher::startAsync(
const sp<AnotherPacketSource> &audioSource,
const sp<AnotherPacketSource> &videoSource,
@@ -342,7 +438,7 @@
int64_t startTimeUs,
int64_t segmentStartTimeUs,
int32_t startDiscontinuitySeq,
- bool adaptive) {
+ LiveSession::SeekMode seekMode) {
sp<AMessage> msg = new AMessage(kWhatStart, this);
uint32_t streamTypeMask = 0ul;
@@ -366,15 +462,20 @@
msg->setInt64("startTimeUs", startTimeUs);
msg->setInt64("segmentStartTimeUs", segmentStartTimeUs);
msg->setInt32("startDiscontinuitySeq", startDiscontinuitySeq);
- msg->setInt32("adaptive", adaptive);
+ msg->setInt32("seekMode", seekMode);
msg->post();
}
-void PlaylistFetcher::pauseAsync() {
+void PlaylistFetcher::pauseAsync(float thresholdRatio) {
+ if (thresholdRatio >= 0.0f) {
+ setStoppingThreshold(thresholdRatio);
+ }
(new AMessage(kWhatPause, this))->post();
}
void PlaylistFetcher::stopAsync(bool clear) {
+ setStoppingThreshold(0.0f);
+
sp<AMessage> msg = new AMessage(kWhatStop, this);
msg->setInt32("clear", clear);
msg->post();
@@ -405,6 +506,10 @@
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatPaused);
+ notify->setInt32("seekMode",
+ mDownloadState->hasSavedState()
+ ? LiveSession::kSeekModeNextSample
+ : LiveSession::kSeekModeNextSegment);
notify->post();
break;
}
@@ -451,6 +556,11 @@
status_t PlaylistFetcher::onStart(const sp<AMessage> &msg) {
mPacketSources.clear();
+ mStopParams.clear();
+ mStartTimeUsNotify = mNotify->dup();
+ mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
+ mStartTimeUsNotify->setInt32("streamMask", 0);
+ mStartTimeUsNotify->setString("uri", mURI);
uint32_t streamTypeMask;
CHECK(msg->findInt32("streamTypeMask", (int32_t *)&streamTypeMask));
@@ -458,11 +568,11 @@
int64_t startTimeUs;
int64_t segmentStartTimeUs;
int32_t startDiscontinuitySeq;
- int32_t adaptive;
+ int32_t seekMode;
CHECK(msg->findInt64("startTimeUs", &startTimeUs));
CHECK(msg->findInt64("segmentStartTimeUs", &segmentStartTimeUs));
CHECK(msg->findInt32("startDiscontinuitySeq", &startDiscontinuitySeq));
- CHECK(msg->findInt32("adaptive", &adaptive));
+ CHECK(msg->findInt32("seekMode", &seekMode));
if (streamTypeMask & LiveSession::STREAMTYPE_AUDIO) {
void *ptr;
@@ -496,12 +606,19 @@
mSegmentStartTimeUs = segmentStartTimeUs;
mDiscontinuitySeq = startDiscontinuitySeq;
+ mRefreshState = INITIAL_MINIMUM_RELOAD_DELAY;
+ mSeekMode = (LiveSession::SeekMode) seekMode;
+
if (startTimeUs >= 0) {
mStartTimeUs = startTimeUs;
+ mFirstPTSValid = false;
mSeqNumber = -1;
mStartup = true;
mPrepared = false;
- mAdaptive = adaptive;
+ mIDRFound = false;
+ mTimeChangeSignaled = false;
+ mVideoBuffer->clear();
+ mDownloadState->resetState();
}
postMonitorQueue();
@@ -511,6 +628,9 @@
void PlaylistFetcher::onPause() {
cancelMonitorQueue();
+ mLastDiscontinuitySeq = mDiscontinuitySeq;
+
+ setStoppingThreshold(-1.0f);
}
void PlaylistFetcher::onStop(const sp<AMessage> &msg) {
@@ -525,8 +645,11 @@
}
}
+ mDownloadState->resetState();
mPacketSources.clear();
mStreamTypeMask = 0;
+
+ setStoppingThreshold(-1.0f);
}
// Resume until we have reached the boundary timestamps listed in `msg`; when
@@ -578,9 +701,6 @@
// Don't resume if all streams are within a resume threshold
if (stopCount == mPacketSources.size()) {
- for (size_t i = 0; i < mPacketSources.size(); i++) {
- mPacketSources.valueAt(i)->queueAccessUnit(mSession->createFormatChangeBuffer());
- }
stopAsync(/* clear = */ false);
return OK;
}
@@ -624,27 +744,23 @@
targetDurationUs = targetDurationSecs * 1000000ll;
}
- // buffer at least 3 times the target duration, or up to 10 seconds
- int64_t durationToBufferUs = targetDurationUs * 3;
- if (durationToBufferUs > kMinBufferedDurationUs) {
- durationToBufferUs = kMinBufferedDurationUs;
- }
-
int64_t bufferedDurationUs = 0ll;
- status_t finalResult = NOT_ENOUGH_DATA;
+ status_t finalResult = OK;
if (mStreamTypeMask == LiveSession::STREAMTYPE_SUBTITLES) {
sp<AnotherPacketSource> packetSource =
mPacketSources.valueFor(LiveSession::STREAMTYPE_SUBTITLES);
bufferedDurationUs =
packetSource->getBufferedDurationUs(&finalResult);
- finalResult = OK;
} else {
- // Use max stream duration to prevent us from waiting on a non-existent stream;
- // when we cannot make out from the manifest what streams are included in a playlist
- // we might assume extra streams.
+ // Use min stream duration, but ignore streams that never have any packet
+ // enqueued to prevent us from waiting on a non-existent stream;
+ // when we cannot make out from the manifest what streams are included in
+ // a playlist we might assume extra streams.
+ bufferedDurationUs = -1ll;
for (size_t i = 0; i < mPacketSources.size(); ++i) {
- if ((mStreamTypeMask & mPacketSources.keyAt(i)) == 0) {
+ if ((mStreamTypeMask & mPacketSources.keyAt(i)) == 0
+ || mPacketSources[i]->getLatestEnqueuedMeta() == NULL) {
continue;
}
@@ -652,24 +768,19 @@
mPacketSources.valueAt(i)->getBufferedDurationUs(&finalResult);
ALOGV("buffered %" PRId64 " for stream %d",
bufferedStreamDurationUs, mPacketSources.keyAt(i));
- if (bufferedStreamDurationUs > bufferedDurationUs) {
+ if (bufferedDurationUs == -1ll
+ || bufferedStreamDurationUs < bufferedDurationUs) {
bufferedDurationUs = bufferedStreamDurationUs;
}
}
- }
- downloadMore = (bufferedDurationUs < durationToBufferUs);
-
- // signal start if buffered up at least the target size
- if (!mPrepared && bufferedDurationUs > targetDurationUs && downloadMore) {
- mPrepared = true;
-
- ALOGV("prepared, buffered=%" PRId64 " > %" PRId64 "",
- bufferedDurationUs, targetDurationUs);
+ if (bufferedDurationUs == -1ll) {
+ bufferedDurationUs = 0ll;
+ }
}
- if (finalResult == OK && downloadMore) {
+ if (finalResult == OK && bufferedDurationUs < kMinBufferedDurationUs) {
ALOGV("monitoring, buffered=%" PRId64 " < %" PRId64 "",
- bufferedDurationUs, durationToBufferUs);
+ bufferedDurationUs, kMinBufferedDurationUs);
// delay the next download slightly; hopefully this gives other concurrent fetchers
// a better chance to run.
// onDownloadNext();
@@ -677,13 +788,16 @@
msg->setInt32("generation", mMonitorQueueGeneration);
msg->post(1000l);
} else {
- // Nothing to do yet, try again in a second.
- int64_t delayUs = mPrepared ? kMaxMonitorDelayUs : targetDurationUs / 2;
+ // We'd like to maintain buffering above durationToBufferUs, so try
+ // again when buffer just about to go below durationToBufferUs
+ // (or after targetDurationUs / 2, whichever is smaller).
+ int64_t delayUs = bufferedDurationUs - kMinBufferedDurationUs + 1000000ll;
+ if (delayUs > targetDurationUs / 2) {
+ delayUs = targetDurationUs / 2;
+ }
ALOGV("pausing for %" PRId64 ", buffered=%" PRId64 " > %" PRId64 "",
- delayUs, bufferedDurationUs, durationToBufferUs);
- // :TRICKY: need to enforce minimum delay because the delay to
- // refresh the playlist will become 0
- postMonitorQueue(delayUs, mPrepared ? targetDurationUs * 2 : 0);
+ delayUs, bufferedDurationUs, kMinBufferedDurationUs);
+ postMonitorQueue(delayUs);
}
}
@@ -724,10 +838,74 @@
return buffer->size() > 0 && buffer->data()[0] == 0x47;
}
-void PlaylistFetcher::onDownloadNext() {
+bool PlaylistFetcher::shouldPauseDownload(bool startFound) {
+ if (mStreamTypeMask == LiveSession::STREAMTYPE_SUBTITLES) {
+ // doesn't apply to subtitles
+ return false;
+ }
+
+ // If we're switching, save state and pause after start point is found
+ if (mSeekMode != LiveSession::kSeekModeExactPosition && startFound) {
+ return true;
+ }
+
+ // Calculate threshold to abort current download
+ int32_t targetDurationSecs;
+ CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
+ int64_t targetDurationUs = targetDurationSecs * 1000000ll;
+ int64_t thresholdUs = -1;
+ {
+ AutoMutex _l(mThresholdLock);
+ thresholdUs = (mThresholdRatio < 0.0f) ?
+ -1ll : mThresholdRatio * targetDurationUs;
+ }
+
+ if (thresholdUs < 0) {
+ // never abort
+ return false;
+ } else if (thresholdUs == 0) {
+ // immediately abort
+ return true;
+ }
+
+ // now we have a positive thresholdUs, abort if remaining
+ // portion to download is over that threshold.
+ if (mSegmentFirstPTS < 0) {
+ // this means we haven't even find the first access unit,
+ // abort now as we must be very far away from the end.
+ return true;
+ }
+ int64_t lastEnqueueUs = mSegmentFirstPTS;
+ for (size_t i = 0; i < mPacketSources.size(); ++i) {
+ if ((mStreamTypeMask & mPacketSources.keyAt(i)) == 0) {
+ continue;
+ }
+ sp<AMessage> meta = mPacketSources[i]->getLatestEnqueuedMeta();
+ int32_t type;
+ if (meta == NULL || meta->findInt32("discontinuity", &type)) {
+ continue;
+ }
+ int64_t tmpUs;
+ CHECK(meta->findInt64("timeUs", &tmpUs));
+ if (tmpUs > lastEnqueueUs) {
+ lastEnqueueUs = tmpUs;
+ }
+ }
+ lastEnqueueUs -= mSegmentFirstPTS;
+ if (targetDurationUs - lastEnqueueUs > thresholdUs) {
+ return true;
+ }
+ return false;
+}
+
+bool PlaylistFetcher::initDownloadState(
+ AString &uri,
+ sp<AMessage> &itemMeta,
+ int32_t &firstSeqNumberInPlaylist,
+ int32_t &lastSeqNumberInPlaylist) {
status_t err = refreshPlaylist();
- int32_t firstSeqNumberInPlaylist = 0;
- int32_t lastSeqNumberInPlaylist = 0;
+ firstSeqNumberInPlaylist = 0;
+ lastSeqNumberInPlaylist = 0;
bool discontinuity = false;
if (mPlaylist != NULL) {
@@ -743,6 +921,8 @@
}
}
+ mSegmentFirstPTS = -1ll;
+
if (mPlaylist != NULL && mSeqNumber < 0) {
CHECK_GE(mStartTimeUs, 0ll);
@@ -770,7 +950,7 @@
// timestamps coming from the media container) is used to determine the position
// inside a segments.
mSeqNumber = getSeqNumberForTime(mSegmentStartTimeUs);
- if (mAdaptive) {
+ if (mSeekMode == LiveSession::kSeekModeNextSegment) {
// avoid double fetch/decode
mSeqNumber += 1;
}
@@ -820,12 +1000,12 @@
mSeqNumber, firstSeqNumberInPlaylist,
lastSeqNumberInPlaylist, delayUs, mNumRetries);
postMonitorQueue(delayUs);
- return;
+ return false;
}
if (err != OK) {
notifyError(err);
- return;
+ return false;
}
// we've missed the boat, let's start 3 segments prior to the latest sequence
@@ -840,12 +1020,8 @@
// but since the segments we are supposed to fetch have already rolled off
// the playlist, i.e. we have already missed the boat, we inevitably have to
// skip.
- for (size_t i = 0; i < mPacketSources.size(); i++) {
- sp<ABuffer> formatChange = mSession->createFormatChangeBuffer();
- mPacketSources.valueAt(i)->queueAccessUnit(formatChange);
- }
stopAsync(/* clear = */ false);
- return;
+ return false;
}
mSeqNumber = lastSeqNumberInPlaylist - 3;
if (mSeqNumber < firstSeqNumberInPlaylist) {
@@ -861,39 +1037,34 @@
firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1);
notifyError(ERROR_END_OF_STREAM);
- return;
+ return false;
}
}
mNumRetries = 0;
- AString uri;
- sp<AMessage> itemMeta;
CHECK(mPlaylist->itemAt(
mSeqNumber - firstSeqNumberInPlaylist,
&uri,
&itemMeta));
+ CHECK(itemMeta->findInt32("discontinuity-sequence", &mDiscontinuitySeq));
+
int32_t val;
if (itemMeta->findInt32("discontinuity", &val) && val != 0) {
- mDiscontinuitySeq++;
+ discontinuity = true;
+ } else if (mLastDiscontinuitySeq >= 0
+ && mDiscontinuitySeq != mLastDiscontinuitySeq) {
+ // Seek jumped to a new discontinuity sequence. We need to signal
+ // a format change to decoder. Decoder needs to shutdown and be
+ // created again if seamless format change is unsupported.
+ ALOGV("saw discontinuity: mStartup %d, mLastDiscontinuitySeq %d, "
+ "mDiscontinuitySeq %d, mStartTimeUs %lld",
+ mStartup, mLastDiscontinuitySeq, mDiscontinuitySeq, (long long)mStartTimeUs);
discontinuity = true;
}
+ mLastDiscontinuitySeq = -1;
- int64_t range_offset, range_length;
- if (!itemMeta->findInt64("range-offset", &range_offset)
- || !itemMeta->findInt64("range-length", &range_length)) {
- range_offset = 0;
- range_length = -1;
- }
-
- ALOGV("fetching segment %d from (%d .. %d)",
- mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist);
-
- ALOGV("fetching '%s'", uri.c_str());
-
- sp<DataSource> source;
- sp<ABuffer> buffer, tsBuffer;
// decrypt a junk buffer to prefetch key; since a session uses only one http connection,
// this avoids interleaved connections to the key and segment file.
{
@@ -903,16 +1074,117 @@
true /* first */);
if (err != OK) {
notifyError(err);
- return;
+ return false;
}
}
+ if ((mStartup && !mTimeChangeSignaled) || discontinuity) {
+ // We need to signal a time discontinuity to ATSParser on the
+ // first segment after start, or on a discontinuity segment.
+ // Setting mNextPTSTimeUs informs extractAndQueueAccessUnitsXX()
+ // to send the time discontinuity.
+ if (mPlaylist->isComplete() || mPlaylist->isEvent()) {
+ // If this was a live event this made no sense since
+ // we don't have access to all the segment before the current
+ // one.
+ mNextPTSTimeUs = getSegmentStartTimeUs(mSeqNumber);
+ }
+
+ // Setting mTimeChangeSignaled to true, so that if start time
+ // searching goes into 2nd segment (without a discontinuity),
+ // we don't reset time again. It causes corruption when pending
+ // data in ATSParser is cleared.
+ mTimeChangeSignaled = true;
+ }
+
+ if (discontinuity) {
+ ALOGI("queueing discontinuity (explicit=%d)", discontinuity);
+
+ // Signal a format discontinuity to ATSParser to clear partial data
+ // from previous streams. Not doing this causes bitstream corruption.
+ mTSParser->signalDiscontinuity(
+ ATSParser::DISCONTINUITY_FORMATCHANGE, NULL /* extra */);
+
+ queueDiscontinuity(
+ ATSParser::DISCONTINUITY_FORMATCHANGE,
+ NULL /* extra */);
+
+ if (mStartup && mStartTimeUsRelative && mFirstPTSValid) {
+ // This means we guessed mStartTimeUs to be in the previous
+ // segment (likely very close to the end), but either video or
+ // audio has not found start by the end of that segment.
+ //
+ // If this new segment is not a discontinuity, keep searching.
+ //
+ // If this new segment even got a discontinuity marker, just
+ // set mStartTimeUs=0, and take all samples from now on.
+ mStartTimeUs = 0;
+ mFirstPTSValid = false;
+ }
+ }
+
+ ALOGV("fetching segment %d from (%d .. %d)",
+ mSeqNumber, firstSeqNumberInPlaylist, lastSeqNumberInPlaylist);
+ return true;
+}
+
+void PlaylistFetcher::onDownloadNext() {
+ AString uri;
+ sp<AMessage> itemMeta;
+ sp<ABuffer> buffer;
+ sp<ABuffer> tsBuffer;
+ int32_t firstSeqNumberInPlaylist = 0;
+ int32_t lastSeqNumberInPlaylist = 0;
+ bool connectHTTP = true;
+
+ if (mDownloadState->hasSavedState()) {
+ mDownloadState->restoreState(
+ uri,
+ itemMeta,
+ buffer,
+ tsBuffer,
+ firstSeqNumberInPlaylist,
+ lastSeqNumberInPlaylist);
+ connectHTTP = false;
+ ALOGV("resuming: '%s'", uri.c_str());
+ } else {
+ if (!initDownloadState(
+ uri,
+ itemMeta,
+ firstSeqNumberInPlaylist,
+ lastSeqNumberInPlaylist)) {
+ return;
+ }
+ ALOGV("fetching: '%s'", uri.c_str());
+ }
+
+ int64_t range_offset, range_length;
+ if (!itemMeta->findInt64("range-offset", &range_offset)
+ || !itemMeta->findInt64("range-length", &range_length)) {
+ range_offset = 0;
+ range_length = -1;
+ }
+
// block-wise download
- bool startup = mStartup;
ssize_t bytesRead;
do {
+ sp<DataSource> source = mHTTPDataSource;
+
+ int64_t startUs = ALooper::GetNowUs();
bytesRead = mSession->fetchFile(
- uri.c_str(), &buffer, range_offset, range_length, kDownloadBlockSize, &source);
+ uri.c_str(), &buffer, range_offset, range_length, kDownloadBlockSize,
+ &source, NULL, connectHTTP);
+
+ // add sample for bandwidth estimation (excluding subtitles)
+ if (bytesRead > 0
+ && (mStreamTypeMask
+ & (LiveSession::STREAMTYPE_AUDIO
+ | LiveSession::STREAMTYPE_VIDEO))) {
+ int64_t delayUs = ALooper::GetNowUs() - startUs;
+ mSession->addBandwidthMeasurement(bytesRead, delayUs);
+ }
+
+ connectHTTP = false;
if (bytesRead < 0) {
status_t err = bytesRead;
@@ -938,28 +1210,7 @@
return;
}
- if (startup || discontinuity) {
- // Signal discontinuity.
-
- if (mPlaylist->isComplete() || mPlaylist->isEvent()) {
- // If this was a live event this made no sense since
- // we don't have access to all the segment before the current
- // one.
- mNextPTSTimeUs = getSegmentStartTimeUs(mSeqNumber);
- }
-
- if (discontinuity) {
- ALOGI("queueing discontinuity (explicit=%d)", discontinuity);
-
- queueDiscontinuity(
- ATSParser::DISCONTINUITY_FORMATCHANGE,
- NULL /* extra */);
-
- discontinuity = false;
- }
-
- startup = false;
- }
+ bool startUp = mStartup; // save current start up state
err = OK;
if (bufferStartsWithTsSyncByte(buffer)) {
@@ -973,7 +1224,6 @@
tsBuffer->setRange(tsOff, tsSize);
}
tsBuffer->setRange(tsBuffer->offset(), tsBuffer->size() + bytesRead);
-
err = extractAndQueueAccessUnitsFromTs(tsBuffer);
}
@@ -993,8 +1243,17 @@
} else if (err != OK) {
notifyError(err);
return;
+ } else if (bytesRead != 0 &&
+ shouldPauseDownload(mStartup != startUp /* startFound */)) {
+ mDownloadState->saveState(
+ uri,
+ itemMeta,
+ buffer,
+ tsBuffer,
+ firstSeqNumberInPlaylist,
+ lastSeqNumberInPlaylist);
+ return;
}
-
} while (bytesRead != 0);
if (bufferStartsWithTsSyncByte(buffer)) {
@@ -1031,7 +1290,7 @@
return;
}
- err = OK;
+ status_t err = OK;
if (tsBuffer != NULL) {
AString method;
CHECK(buffer->meta()->findString("cipher-method", &method));
@@ -1064,11 +1323,11 @@
}
++mSeqNumber;
-
postMonitorQueue();
}
-int32_t PlaylistFetcher::getSeqNumberWithAnchorTime(int64_t anchorTimeUs) const {
+int32_t PlaylistFetcher::getSeqNumberWithAnchorTime(
+ int64_t anchorTimeUs, int64_t targetDurationUs) const {
int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist;
if (mPlaylist->meta() == NULL
|| !mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist)) {
@@ -1077,7 +1336,8 @@
lastSeqNumberInPlaylist = firstSeqNumberInPlaylist + mPlaylist->size() - 1;
int32_t index = mSeqNumber - firstSeqNumberInPlaylist - 1;
- while (index >= 0 && anchorTimeUs > mStartTimeUs) {
+ // adjust anchorTimeUs to within 1x targetDurationUs from mStartTimeUs
+ while (index >= 0 && anchorTimeUs - mStartTimeUs > targetDurationUs) {
sp<AMessage> itemMeta;
CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta));
@@ -1197,9 +1457,7 @@
mTSParser->signalDiscontinuity(
ATSParser::DISCONTINUITY_TIME, extra);
- mAbsoluteTimeAnchorUs = mNextPTSTimeUs;
mNextPTSTimeUs = -1ll;
- mFirstPTSValid = false;
}
size_t offset = 0;
@@ -1252,14 +1510,22 @@
continue;
}
- int64_t timeUs;
+ const char *mime;
+ sp<MetaData> format = source->getFormat();
+ bool isAvc = format != NULL && format->findCString(kKeyMIMEType, &mime)
+ && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
+
sp<ABuffer> accessUnit;
status_t finalResult;
while (source->hasBufferAvailable(&finalResult)
&& source->dequeueAccessUnit(&accessUnit) == OK) {
+ int64_t timeUs;
CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+ if (mSegmentFirstPTS < 0ll) {
+ mSegmentFirstPTS = timeUs;
+ }
if (mStartup) {
if (!mFirstPTSValid) {
mFirstTimeUs = timeUs;
@@ -1272,30 +1538,30 @@
}
}
- if (timeUs < mStartTimeUs) {
- // buffer up to the closest preceding IDR frame
- ALOGV("timeUs %" PRId64 " us < mStartTimeUs %" PRId64 " us",
- timeUs, mStartTimeUs);
- const char *mime;
- sp<MetaData> format = source->getFormat();
- bool isAvc = false;
- if (format != NULL && format->findCString(kKeyMIMEType, &mime)
- && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
- isAvc = true;
- }
- if (isAvc && IsIDR(accessUnit)) {
- mVideoBuffer->clear();
- }
- if (isAvc) {
- mVideoBuffer->queueAccessUnit(accessUnit);
- }
+ bool seeking = mSeekMode == LiveSession::kSeekModeExactPosition;
+ bool startTimeReached =
+ seeking ? (timeUs >= mStartTimeUs)
+ : (timeUs > mStartTimeUs);
- continue;
+ if (!startTimeReached || (isAvc && !mIDRFound)) {
+ // buffer up to the closest preceding IDR frame in the next segement,
+ // or the closest succeeding IDR frame after the exact position
+ if (isAvc) {
+ if (IsIDR(accessUnit) && (seeking || startTimeReached)) {
+ mVideoBuffer->clear();
+ mIDRFound = true;
+ }
+ if (mIDRFound && seeking && !startTimeReached) {
+ mVideoBuffer->queueAccessUnit(accessUnit);
+ }
+ }
+ if (!startTimeReached || (isAvc && !mIDRFound)) {
+ continue;
+ }
}
}
- CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
- if (mStartTimeUsNotify != NULL && timeUs > mStartTimeUs) {
+ if (mStartTimeUsNotify != NULL) {
int32_t firstSeqNumberInPlaylist;
if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
"media-sequence", &firstSeqNumberInPlaylist)) {
@@ -1328,7 +1594,8 @@
// live stream; re-adjust based on the actual timestamp extracted from the
// media segment; if we didn't move backward after the re-adjustment
// (newSeqNumber), start at least 1 segment prior.
- int32_t newSeqNumber = getSeqNumberWithAnchorTime(timeUs);
+ int32_t newSeqNumber = getSeqNumberWithAnchorTime(
+ timeUs, targetDurationUs);
if (newSeqNumber >= mSeqNumber) {
--mSeqNumber;
} else {
@@ -1336,6 +1603,7 @@
}
mStartTimeUsNotify = mNotify->dup();
mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
+ mIDRFound = false;
return -EAGAIN;
}
@@ -1354,7 +1622,11 @@
if (streamMask == mStreamTypeMask) {
mStartup = false;
- mStartTimeUsNotify->post();
+ // only need to post if we're switching and searching for a
+ // start point in next segment, or next IDR
+ if (mSeekMode != LiveSession::kSeekModeExactPosition) {
+ mStartTimeUsNotify->post();
+ }
mStartTimeUsNotify.clear();
}
}
@@ -1369,7 +1641,6 @@
|| !mStopParams->findInt64(key, &stopTimeUs)
|| (discontinuitySeq == mDiscontinuitySeq
&& timeUs >= stopTimeUs)) {
- packetSource->queueAccessUnit(mSession->createFormatChangeBuffer());
mStreamTypeMask &= ~stream;
mPacketSources.removeItemsAt(i);
break;
@@ -1464,8 +1735,6 @@
}
if (mNextPTSTimeUs >= 0ll) {
- mFirstPTSValid = false;
- mAbsoluteTimeAnchorUs = mNextPTSTimeUs;
mNextPTSTimeUs = -1ll;
}
@@ -1566,7 +1835,7 @@
CHECK(packetSource->getFormat()->findInt32(kKeySampleRate, &sampleRate));
int64_t timeUs = (PTS * 100ll) / 9ll;
- if (!mFirstPTSValid) {
+ if (mStartup && !mFirstPTSValid) {
mFirstPTSValid = true;
mFirstTimeUs = timeUs;
}
@@ -1621,7 +1890,8 @@
// Duplicated logic from how we handle .ts playlists.
if (mStartup && mSegmentStartTimeUs >= 0
&& timeUs - mStartTimeUs > targetDurationUs) {
- int32_t newSeqNumber = getSeqNumberWithAnchorTime(timeUs);
+ int32_t newSeqNumber = getSeqNumberWithAnchorTime(
+ timeUs, targetDurationUs);
if (newSeqNumber >= mSeqNumber) {
--mSeqNumber;
} else {
@@ -1647,7 +1917,6 @@
|| discontinuitySeq > mDiscontinuitySeq
|| !mStopParams->findInt64("timeUsAudio", &stopTimeUs)
|| (discontinuitySeq == mDiscontinuitySeq && unitTimeUs >= stopTimeUs)) {
- packetSource->queueAccessUnit(mSession->createFormatChangeBuffer());
mStreamTypeMask = 0;
mPacketSources.clear();
return ERROR_OUT_OF_RANGE;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index 2f11949..8d34cbc 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -65,9 +65,9 @@
int64_t segmentStartTimeUs = -1ll, // starting position within playlist
// startTimeUs!=segmentStartTimeUs only when playlist is live
int32_t startDiscontinuitySeq = 0,
- bool adaptive = false);
+ LiveSession::SeekMode seekMode = LiveSession::kSeekModeExactPosition);
- void pauseAsync();
+ void pauseAsync(float thresholdRatio);
void stopAsync(bool clear = true);
@@ -95,6 +95,8 @@
kWhatDownloadNext = 'dlnx',
};
+ struct DownloadState;
+
static const int64_t kMaxMonitorDelayUs;
static const int32_t kNumSkipFrames;
@@ -105,6 +107,7 @@
sp<AMessage> mNotify;
sp<AMessage> mStartTimeUsNotify;
+ sp<HTTPBase> mHTTPDataSource;
sp<LiveSession> mSession;
AString mURI;
@@ -116,7 +119,7 @@
// adapting or switching tracks.
int64_t mSegmentStartTimeUs;
- ssize_t mDiscontinuitySeq;
+ int32_t mDiscontinuitySeq;
bool mStartTimeUsRelative;
sp<AMessage> mStopParams; // message containing the latest timestamps we should fetch.
@@ -130,13 +133,17 @@
int32_t mSeqNumber;
int32_t mNumRetries;
bool mStartup;
- bool mAdaptive;
+ bool mIDRFound;
+ int32_t mSeekMode;
bool mPrepared;
+ bool mTimeChangeSignaled;
int64_t mNextPTSTimeUs;
int32_t mMonitorQueueGeneration;
const int32_t mSubtitleGeneration;
+ int32_t mLastDiscontinuitySeq;
+
enum RefreshState {
INITIAL_MINIMUM_RELOAD_DELAY,
FIRST_UNCHANGED_RELOAD_ATTEMPT,
@@ -150,9 +157,8 @@
sp<ATSParser> mTSParser;
bool mFirstPTSValid;
- uint64_t mFirstPTS;
int64_t mFirstTimeUs;
- int64_t mAbsoluteTimeAnchorUs;
+ int64_t mSegmentFirstPTS;
sp<AnotherPacketSource> mVideoBuffer;
// Stores the initialization vector to decrypt the next block of cipher text, which can
@@ -160,6 +166,11 @@
// the last block of cipher text (cipher-block chaining).
unsigned char mAESInitVec[16];
+ Mutex mThresholdLock;
+ float mThresholdRatio;
+
+ sp<DownloadState> mDownloadState;
+
// Set first to true if decrypting the first segment of a playlist segment. When
// first is true, reset the initialization vector based on the available
// information in the manifest; otherwise, use the initialization vector as
@@ -175,6 +186,8 @@
void postMonitorQueue(int64_t delayUs = 0, int64_t minDelayUs = 0);
void cancelMonitorQueue();
+ void setStoppingThreshold(float thresholdRatio);
+ bool shouldPauseDownload(bool startFound);
int64_t delayUsToRefreshPlaylist() const;
status_t refreshPlaylist();
@@ -188,6 +201,11 @@
void onStop(const sp<AMessage> &msg);
void onMonitorQueue();
void onDownloadNext();
+ bool initDownloadState(
+ AString &uri,
+ sp<AMessage> &itemMeta,
+ int32_t &firstSeqNumberInPlaylist,
+ int32_t &lastSeqNumberInPlaylist);
// Resume a fetcher to continue until the stopping point stored in msg.
status_t onResumeUntil(const sp<AMessage> &msg);
@@ -206,7 +224,8 @@
void queueDiscontinuity(
ATSParser::DiscontinuityType type, const sp<AMessage> &extra);
- int32_t getSeqNumberWithAnchorTime(int64_t anchorTimeUs) const;
+ int32_t getSeqNumberWithAnchorTime(
+ int64_t anchorTimeUs, int64_t targetDurationUs) const;
int32_t getSeqNumberForDiscontinuity(size_t discontinuitySeq) const;
int32_t getSeqNumberForTime(int64_t timeUs) const;
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 75d76dc..5c50747 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -46,6 +46,9 @@
DISCONTINUITY_AUDIO_FORMAT
| DISCONTINUITY_VIDEO_FORMAT
| DISCONTINUITY_TIME,
+ DISCONTINUITY_FORMAT_ONLY =
+ DISCONTINUITY_AUDIO_FORMAT
+ | DISCONTINUITY_VIDEO_FORMAT,
};
enum Flags {
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index bb05417..9f42217 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -48,7 +48,10 @@
}
void AnotherPacketSource::setFormat(const sp<MetaData> &meta) {
- CHECK(mFormat == NULL);
+ if (mFormat != NULL) {
+ // Only allowed to be set once. Requires explicit clear to reset.
+ return;
+ }
mIsAudio = false;
mIsVideo = false;
@@ -94,7 +97,8 @@
if (!buffer->meta()->findInt32("discontinuity", &discontinuity)) {
sp<RefBase> object;
if (buffer->meta()->findObject("format", &object)) {
- return mFormat = static_cast<MetaData*>(object.get());
+ setFormat(static_cast<MetaData*>(object.get()));
+ return mFormat;
}
}
@@ -129,7 +133,7 @@
sp<RefBase> object;
if ((*buffer)->meta()->findObject("format", &object)) {
- mFormat = static_cast<MetaData*>(object.get());
+ setFormat(static_cast<MetaData*>(object.get()));
}
return OK;
@@ -164,7 +168,7 @@
sp<RefBase> object;
if (buffer->meta()->findObject("format", &object)) {
- mFormat = static_cast<MetaData*>(object.get());
+ setFormat(static_cast<MetaData*>(object.get()));
}
int64_t timeUs;
@@ -294,6 +298,7 @@
bool AnotherPacketSource::hasBufferAvailable(status_t *finalResult) {
Mutex::Autolock autoLock(mLock);
+ *finalResult = OK;
if (!mBuffers.empty()) {
return true;
}
@@ -302,6 +307,21 @@
return false;
}
+bool AnotherPacketSource::hasDataBufferAvailable(status_t *finalResult) {
+ Mutex::Autolock autoLock(mLock);
+ *finalResult = OK;
+ List<sp<ABuffer> >::iterator it;
+ for (it = mBuffers.begin(); it != mBuffers.end(); it++) {
+ int32_t discontinuity;
+ if (!(*it)->meta()->findInt32("discontinuity", &discontinuity)) {
+ return true;
+ }
+ }
+
+ *finalResult = mEOSResult;
+ return false;
+}
+
int64_t AnotherPacketSource::getBufferedDurationUs(status_t *finalResult) {
Mutex::Autolock autoLock(mLock);
return getBufferedDurationUs_l(finalResult);
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 809a858..d4fde7c 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -43,8 +43,12 @@
void clear();
+ // Returns true if we have any packets including discontinuities
bool hasBufferAvailable(status_t *finalResult);
+ // Returns true if we have packets that's not discontinuities
+ bool hasDataBufferAvailable(status_t *finalResult);
+
// Returns the difference between the last and the first queued
// presentation timestamps since the last discontinuity (if any).
int64_t getBufferedDurationUs(status_t *finalResult);
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index f1b84ad..0ad0bf3 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -26,7 +26,8 @@
libutils \
liblog \
libbinder \
- libsoundtriggerservice
+ libsoundtriggerservice \
+ libradioservice
LOCAL_STATIC_LIBRARIES := \
libregistermsext
@@ -38,7 +39,8 @@
frameworks/av/services/audiopolicy \
frameworks/av/services/camera/libcameraservice \
$(call include-path-for, audio-utils) \
- frameworks/av/services/soundtrigger
+ frameworks/av/services/soundtrigger \
+ frameworks/av/services/radio
LOCAL_MODULE:= mediaserver
LOCAL_32_BIT_ONLY := true
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index 263dd32..99572f8 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -35,6 +35,7 @@
#include "MediaPlayerService.h"
#include "service/AudioPolicyService.h"
#include "SoundTriggerHwService.h"
+#include "RadioService.h"
using namespace android;
@@ -130,6 +131,7 @@
CameraService::instantiate();
AudioPolicyService::instantiate();
SoundTriggerHwService::instantiate();
+ RadioService::instantiate();
registerExtensions();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
diff --git a/radio/Android.mk b/radio/Android.mk
new file mode 100644
index 0000000..ecbb8fd
--- /dev/null
+++ b/radio/Android.mk
@@ -0,0 +1,39 @@
+# Copyright 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ Radio.cpp \
+ IRadio.cpp \
+ IRadioClient.cpp \
+ IRadioService.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ liblog \
+ libbinder \
+ libhardware \
+ libradio_metadata
+
+#LOCAL_C_INCLUDES += \
+ system/media/camera/include \
+ system/media/private/camera/include
+
+LOCAL_MODULE:= libradio
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/radio/IRadio.cpp b/radio/IRadio.cpp
new file mode 100644
index 0000000..242df77
--- /dev/null
+++ b/radio/IRadio.cpp
@@ -0,0 +1,344 @@
+/*
+**
+** Copyright 2015, 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.
+*/
+
+#define LOG_TAG "IRadio"
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <binder/IMemory.h>
+#include <radio/IRadio.h>
+#include <radio/IRadioService.h>
+#include <radio/IRadioClient.h>
+#include <system/radio.h>
+#include <system/radio_metadata.h>
+
+namespace android {
+
+enum {
+ DETACH = IBinder::FIRST_CALL_TRANSACTION,
+ SET_CONFIGURATION,
+ GET_CONFIGURATION,
+ SET_MUTE,
+ GET_MUTE,
+ SCAN,
+ STEP,
+ TUNE,
+ CANCEL,
+ GET_PROGRAM_INFORMATION,
+ HAS_CONTROL
+};
+
+class BpRadio: public BpInterface<IRadio>
+{
+public:
+ BpRadio(const sp<IBinder>& impl)
+ : BpInterface<IRadio>(impl)
+ {
+ }
+
+ void detach()
+ {
+ ALOGV("detach");
+ Parcel data, reply;
+ data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+ remote()->transact(DETACH, data, &reply);
+ }
+
+ virtual status_t setConfiguration(const struct radio_band_config *config)
+ {
+ Parcel data, reply;
+ if (config == NULL) {
+ return BAD_VALUE;
+ }
+ data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+ data.write(config, sizeof(struct radio_band_config));
+ status_t status = remote()->transact(SET_CONFIGURATION, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t getConfiguration(struct radio_band_config *config)
+ {
+ Parcel data, reply;
+ if (config == NULL) {
+ return BAD_VALUE;
+ }
+ data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+ status_t status = remote()->transact(GET_CONFIGURATION, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ if (status == NO_ERROR) {
+ reply.read(config, sizeof(struct radio_band_config));
+ }
+ }
+ return status;
+ }
+
+ virtual status_t setMute(bool mute)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+ data.writeInt32(mute ? 1 : 0);
+ status_t status = remote()->transact(SET_MUTE, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t getMute(bool *mute)
+ {
+ Parcel data, reply;
+ if (mute == NULL) {
+ return BAD_VALUE;
+ }
+ data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+ status_t status = remote()->transact(GET_MUTE, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ if (status == NO_ERROR) {
+ int muteread = reply.readInt32();
+ *mute = muteread != 0;
+ }
+ }
+ return status;
+ }
+
+ virtual status_t scan(radio_direction_t direction, bool skipSubChannel)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+ data.writeInt32(direction);
+ data.writeInt32(skipSubChannel ? 1 : 0);
+ status_t status = remote()->transact(SCAN, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t step(radio_direction_t direction, bool skipSubChannel)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+ data.writeInt32(direction);
+ data.writeInt32(skipSubChannel ? 1 : 0);
+ status_t status = remote()->transact(STEP, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t tune(unsigned int channel, unsigned int subChannel)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+ data.writeInt32(channel);
+ data.writeInt32(subChannel);
+ status_t status = remote()->transact(TUNE, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t cancel()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+ status_t status = remote()->transact(CANCEL, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t getProgramInformation(struct radio_program_info *info)
+ {
+ Parcel data, reply;
+ if (info == NULL) {
+ return BAD_VALUE;
+ }
+ radio_metadata_t *metadata = info->metadata;
+ data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+ status_t status = remote()->transact(GET_PROGRAM_INFORMATION, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ if (status == NO_ERROR) {
+ reply.read(info, sizeof(struct radio_program_info));
+ info->metadata = metadata;
+ if (metadata == NULL) {
+ return status;
+ }
+ size_t size = (size_t)reply.readInt32();
+ if (size == 0) {
+ return status;
+ }
+ metadata =
+ (radio_metadata_t *)calloc(size / sizeof(unsigned int), sizeof(unsigned int));
+ if (metadata == NULL) {
+ return NO_MEMORY;
+ }
+ reply.read(metadata, size);
+ status = radio_metadata_add_metadata(&info->metadata, metadata);
+ free(metadata);
+ }
+ }
+ return status;
+ }
+
+ virtual status_t hasControl(bool *hasControl)
+ {
+ Parcel data, reply;
+ if (hasControl == NULL) {
+ return BAD_VALUE;
+ }
+ data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+ status_t status = remote()->transact(HAS_CONTROL, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ if (status == NO_ERROR) {
+ *hasControl = reply.readInt32() != 0;
+ }
+ }
+ return status;
+ }
+};
+
+IMPLEMENT_META_INTERFACE(Radio, "android.hardware.IRadio");
+
+// ----------------------------------------------------------------------
+
+status_t BnRadio::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case DETACH: {
+ ALOGV("DETACH");
+ CHECK_INTERFACE(IRadio, data, reply);
+ detach();
+ return NO_ERROR;
+ } break;
+ case SET_CONFIGURATION: {
+ CHECK_INTERFACE(IRadio, data, reply);
+ struct radio_band_config config;
+ data.read(&config, sizeof(struct radio_band_config));
+ status_t status = setConfiguration(&config);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ }
+ case GET_CONFIGURATION: {
+ CHECK_INTERFACE(IRadio, data, reply);
+ struct radio_band_config config;
+ status_t status = getConfiguration(&config);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->write(&config, sizeof(struct radio_band_config));
+ }
+ return NO_ERROR;
+ }
+ case SET_MUTE: {
+ CHECK_INTERFACE(IRadio, data, reply);
+ bool mute = data.readInt32() != 0;
+ status_t status = setMute(mute);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ }
+ case GET_MUTE: {
+ CHECK_INTERFACE(IRadio, data, reply);
+ bool mute;
+ status_t status = getMute(&mute);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->writeInt32(mute ? 1 : 0);
+ }
+ return NO_ERROR;
+ }
+ case SCAN: {
+ CHECK_INTERFACE(IRadio, data, reply);
+ radio_direction_t direction = (radio_direction_t)data.readInt32();
+ bool skipSubChannel = data.readInt32() == 1;
+ status_t status = scan(direction, skipSubChannel);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ }
+ case STEP: {
+ CHECK_INTERFACE(IRadio, data, reply);
+ radio_direction_t direction = (radio_direction_t)data.readInt32();
+ bool skipSubChannel = data.readInt32() == 1;
+ status_t status = step(direction, skipSubChannel);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ }
+ case TUNE: {
+ CHECK_INTERFACE(IRadio, data, reply);
+ unsigned int channel = (unsigned int)data.readInt32();
+ unsigned int subChannel = (unsigned int)data.readInt32();
+ status_t status = tune(channel, subChannel);
+ reply->writeInt32(status);
+ return NO_ERROR;
+ }
+ case CANCEL: {
+ CHECK_INTERFACE(IRadio, data, reply);
+ status_t status = cancel();
+ reply->writeInt32(status);
+ return NO_ERROR;
+ }
+ case GET_PROGRAM_INFORMATION: {
+ CHECK_INTERFACE(IRadio, data, reply);
+ struct radio_program_info info;
+
+ status_t status = radio_metadata_allocate(&info.metadata, 0, 0);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = getProgramInformation(&info);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->write(&info, sizeof(struct radio_program_info));
+ int count = radio_metadata_get_count(info.metadata);
+ if (count > 0) {
+ size_t size = radio_metadata_get_size(info.metadata);
+ reply->writeInt32(size);
+ reply->write(info.metadata, size);
+ } else {
+ reply->writeInt32(0);
+ }
+ }
+ radio_metadata_deallocate(info.metadata);
+ return NO_ERROR;
+ }
+ case HAS_CONTROL: {
+ CHECK_INTERFACE(IRadio, data, reply);
+ bool control;
+ status_t status = hasControl(&control);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->writeInt32(control ? 1 : 0);
+ }
+ return NO_ERROR;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/radio/IRadioClient.cpp b/radio/IRadioClient.cpp
new file mode 100644
index 0000000..033ca49
--- /dev/null
+++ b/radio/IRadioClient.cpp
@@ -0,0 +1,75 @@
+/*
+**
+** Copyright 2015, 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.
+*/
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <radio/IRadioClient.h>
+
+namespace android {
+
+enum {
+ ON_EVENT = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BpRadioClient: public BpInterface<IRadioClient>
+{
+
+public:
+ BpRadioClient(const sp<IBinder>& impl)
+ : BpInterface<IRadioClient>(impl)
+ {
+ }
+
+ virtual void onEvent(const sp<IMemory>& eventMemory)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IRadioClient::getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(eventMemory));
+ remote()->transact(ON_EVENT,
+ data,
+ &reply);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(RadioClient,
+ "android.hardware.IRadioClient");
+
+// ----------------------------------------------------------------------
+
+status_t BnRadioClient::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case ON_EVENT: {
+ CHECK_INTERFACE(IRadioClient, data, reply);
+ sp<IMemory> eventMemory = interface_cast<IMemory>(
+ data.readStrongBinder());
+ onEvent(eventMemory);
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ } return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/radio/IRadioService.cpp b/radio/IRadioService.cpp
new file mode 100644
index 0000000..8c2b3ef
--- /dev/null
+++ b/radio/IRadioService.cpp
@@ -0,0 +1,181 @@
+/*
+**
+** Copyright 2015, 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.
+*/
+
+#define LOG_TAG "BpRadioService"
+//
+#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <radio/IRadioService.h>
+#include <radio/IRadio.h>
+#include <radio/IRadioClient.h>
+
+namespace android {
+
+enum {
+ LIST_MODULES = IBinder::FIRST_CALL_TRANSACTION,
+ ATTACH,
+};
+
+#define MAX_ITEMS_PER_LIST 1024
+
+class BpRadioService: public BpInterface<IRadioService>
+{
+public:
+ BpRadioService(const sp<IBinder>& impl)
+ : BpInterface<IRadioService>(impl)
+ {
+ }
+
+ virtual status_t listModules(struct radio_properties *properties,
+ uint32_t *numModules)
+ {
+ if (numModules == NULL || (*numModules != 0 && properties == NULL)) {
+ return BAD_VALUE;
+ }
+ Parcel data, reply;
+ data.writeInterfaceToken(IRadioService::getInterfaceDescriptor());
+ unsigned int numModulesReq = (properties == NULL) ? 0 : *numModules;
+ data.writeInt32(numModulesReq);
+ status_t status = remote()->transact(LIST_MODULES, data, &reply);
+ if (status == NO_ERROR) {
+ status = (status_t)reply.readInt32();
+ *numModules = (unsigned int)reply.readInt32();
+ }
+ ALOGV("listModules() status %d got *numModules %d", status, *numModules);
+ if (status == NO_ERROR) {
+ if (numModulesReq > *numModules) {
+ numModulesReq = *numModules;
+ }
+ if (numModulesReq > 0) {
+ reply.read(properties, numModulesReq * sizeof(struct radio_properties));
+ }
+ }
+ return status;
+ }
+
+ virtual status_t attach(radio_handle_t handle,
+ const sp<IRadioClient>& client,
+ const struct radio_band_config *config,
+ bool withAudio,
+ sp<IRadio>& radio)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IRadioService::getInterfaceDescriptor());
+ data.writeInt32(handle);
+ data.writeStrongBinder(IInterface::asBinder(client));
+ ALOGV("attach() config %p withAudio %d region %d type %d", config, withAudio, config->region, config->band.type);
+ if (config == NULL) {
+ data.writeInt32(0);
+ } else {
+ data.writeInt32(1);
+ data.write(config, sizeof(struct radio_band_config));
+ }
+ data.writeInt32(withAudio ? 1 : 0);
+ status_t status = remote()->transact(ATTACH, data, &reply);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = reply.readInt32();
+ if (reply.readInt32() != 0) {
+ radio = interface_cast<IRadio>(reply.readStrongBinder());
+ }
+ return status;
+ }
+};
+
+IMPLEMENT_META_INTERFACE(RadioService, "android.hardware.IRadioService");
+
+// ----------------------------------------------------------------------
+
+status_t BnRadioService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case LIST_MODULES: {
+ CHECK_INTERFACE(IRadioService, data, reply);
+ unsigned int numModulesReq = data.readInt32();
+ if (numModulesReq > MAX_ITEMS_PER_LIST) {
+ numModulesReq = MAX_ITEMS_PER_LIST;
+ }
+ unsigned int numModules = numModulesReq;
+ struct radio_properties *properties =
+ (struct radio_properties *)calloc(numModulesReq,
+ sizeof(struct radio_properties));
+ if (properties == NULL) {
+ reply->writeInt32(NO_MEMORY);
+ reply->writeInt32(0);
+ return NO_ERROR;
+ }
+
+ status_t status = listModules(properties, &numModules);
+ reply->writeInt32(status);
+ reply->writeInt32(numModules);
+ ALOGV("LIST_MODULES status %d got numModules %d", status, numModules);
+
+ if (status == NO_ERROR) {
+ if (numModulesReq > numModules) {
+ numModulesReq = numModules;
+ }
+ reply->write(properties,
+ numModulesReq * sizeof(struct radio_properties));
+ }
+ free(properties);
+ return NO_ERROR;
+ } break;
+
+ case ATTACH: {
+ CHECK_INTERFACE(IRadioService, data, reply);
+ radio_handle_t handle = data.readInt32();
+ sp<IRadioClient> client =
+ interface_cast<IRadioClient>(data.readStrongBinder());
+ struct radio_band_config config;
+ struct radio_band_config *configPtr = NULL;
+ if (data.readInt32() != 0) {
+ data.read(&config, sizeof(struct radio_band_config));
+ configPtr = &config;
+ }
+ bool withAudio = data.readInt32() != 0;
+ ALOGV("ATTACH configPtr %p withAudio %d", configPtr, withAudio);
+ sp<IRadio> radio;
+ status_t status = attach(handle, client, configPtr, withAudio, radio);
+ reply->writeInt32(status);
+ if (radio != 0) {
+ reply->writeInt32(1);
+ reply->writeStrongBinder(IInterface::asBinder(radio));
+ } else {
+ reply->writeInt32(0);
+ }
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/radio/Radio.cpp b/radio/Radio.cpp
new file mode 100644
index 0000000..e3554c2
--- /dev/null
+++ b/radio/Radio.cpp
@@ -0,0 +1,283 @@
+/*
+**
+** Copyright (C) 2015, 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.
+*/
+
+#define LOG_TAG "Radio"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IMemory.h>
+
+#include <radio/Radio.h>
+#include <radio/IRadio.h>
+#include <radio/IRadioService.h>
+#include <radio/IRadioClient.h>
+#include <radio/RadioCallback.h>
+
+namespace android {
+
+namespace {
+ sp<IRadioService> gRadioService;
+ const int kRadioServicePollDelay = 500000; // 0.5s
+ const char* kRadioServiceName = "media.radio";
+ Mutex gLock;
+
+ class DeathNotifier : public IBinder::DeathRecipient
+ {
+ public:
+ DeathNotifier() {
+ }
+
+ virtual void binderDied(const wp<IBinder>& who __unused) {
+ ALOGV("binderDied");
+ Mutex::Autolock _l(gLock);
+ gRadioService.clear();
+ ALOGW("Radio service died!");
+ }
+ };
+
+ sp<DeathNotifier> gDeathNotifier;
+}; // namespace anonymous
+
+const sp<IRadioService>& Radio::getRadioService()
+{
+ Mutex::Autolock _l(gLock);
+ if (gRadioService.get() == 0) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder;
+ do {
+ binder = sm->getService(String16(kRadioServiceName));
+ if (binder != 0) {
+ break;
+ }
+ ALOGW("RadioService not published, waiting...");
+ usleep(kRadioServicePollDelay);
+ } while(true);
+ if (gDeathNotifier == NULL) {
+ gDeathNotifier = new DeathNotifier();
+ }
+ binder->linkToDeath(gDeathNotifier);
+ gRadioService = interface_cast<IRadioService>(binder);
+ }
+ ALOGE_IF(gRadioService == 0, "no RadioService!?");
+ return gRadioService;
+}
+
+// Static methods
+status_t Radio::listModules(struct radio_properties *properties,
+ uint32_t *numModules)
+{
+ ALOGV("listModules()");
+ const sp<IRadioService>& service = getRadioService();
+ if (service == 0) {
+ return NO_INIT;
+ }
+ return service->listModules(properties, numModules);
+}
+
+sp<Radio> Radio::attach(radio_handle_t handle,
+ const struct radio_band_config *config,
+ bool withAudio,
+ const sp<RadioCallback>& callback)
+{
+ ALOGV("attach()");
+ sp<Radio> radio;
+ const sp<IRadioService>& service = getRadioService();
+ if (service == 0) {
+ return radio;
+ }
+ radio = new Radio(handle, callback);
+ status_t status = service->attach(handle, radio, config, withAudio, radio->mIRadio);
+
+ if (status == NO_ERROR && radio->mIRadio != 0) {
+ IInterface::asBinder(radio->mIRadio)->linkToDeath(radio);
+ } else {
+ ALOGW("Error %d connecting to radio service", status);
+ radio.clear();
+ }
+ return radio;
+}
+
+
+
+// Radio
+Radio::Radio(radio_handle_t handle, const sp<RadioCallback>& callback)
+ : mHandle(handle), mCallback(callback)
+{
+}
+
+Radio::~Radio()
+{
+ if (mIRadio != 0) {
+ mIRadio->detach();
+ }
+}
+
+
+void Radio::detach() {
+ ALOGV("detach()");
+ Mutex::Autolock _l(mLock);
+ mCallback.clear();
+ if (mIRadio != 0) {
+ mIRadio->detach();
+ IInterface::asBinder(mIRadio)->unlinkToDeath(this);
+ mIRadio = 0;
+ }
+}
+
+status_t Radio::setConfiguration(const struct radio_band_config *config)
+{
+ Mutex::Autolock _l(mLock);
+ if (mIRadio == 0) {
+ return NO_INIT;
+ }
+ return mIRadio->setConfiguration(config);
+}
+
+status_t Radio::getConfiguration(struct radio_band_config *config)
+{
+ Mutex::Autolock _l(mLock);
+ if (mIRadio == 0) {
+ return NO_INIT;
+ }
+ return mIRadio->getConfiguration(config);
+}
+
+status_t Radio::setMute(bool mute)
+{
+ Mutex::Autolock _l(mLock);
+ if (mIRadio == 0) {
+ return NO_INIT;
+ }
+ return mIRadio->setMute(mute);
+}
+
+status_t Radio::getMute(bool *mute)
+{
+ Mutex::Autolock _l(mLock);
+ if (mIRadio == 0) {
+ return NO_INIT;
+ }
+ return mIRadio->getMute(mute);
+}
+
+status_t Radio::scan(radio_direction_t direction, bool skipSubchannel)
+{
+ Mutex::Autolock _l(mLock);
+ if (mIRadio == 0) {
+ return NO_INIT;
+ }
+ return mIRadio->scan(direction, skipSubchannel);
+}
+
+status_t Radio::step(radio_direction_t direction, bool skipSubchannel)
+{
+ Mutex::Autolock _l(mLock);
+ if (mIRadio == 0) {
+ return NO_INIT;
+ }
+ return mIRadio->step(direction, skipSubchannel);
+}
+
+status_t Radio::tune(unsigned int channel, unsigned int subChannel)
+{
+ Mutex::Autolock _l(mLock);
+ if (mIRadio == 0) {
+ return NO_INIT;
+ }
+ return mIRadio->tune(channel, subChannel);
+}
+
+status_t Radio::cancel()
+{
+ Mutex::Autolock _l(mLock);
+ if (mIRadio == 0) {
+ return NO_INIT;
+ }
+ return mIRadio->cancel();
+}
+
+status_t Radio::getProgramInformation(struct radio_program_info *info)
+{
+ Mutex::Autolock _l(mLock);
+ if (mIRadio == 0) {
+ return NO_INIT;
+ }
+ return mIRadio->getProgramInformation(info);
+}
+
+status_t Radio::hasControl(bool *hasControl)
+{
+ Mutex::Autolock _l(mLock);
+ if (mIRadio == 0) {
+ return NO_INIT;
+ }
+ return mIRadio->hasControl(hasControl);
+}
+
+
+// BpRadioClient
+void Radio::onEvent(const sp<IMemory>& eventMemory)
+{
+ Mutex::Autolock _l(mLock);
+ if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ return;
+ }
+
+ struct radio_event *event = (struct radio_event *)eventMemory->pointer();
+ // restore local metadata pointer from offset
+ switch (event->type) {
+ case RADIO_EVENT_TUNED:
+ case RADIO_EVENT_AF_SWITCH:
+ if (event->info.metadata != NULL) {
+ event->info.metadata =
+ (radio_metadata_t *)((char *)event + (size_t)event->info.metadata);
+ }
+ break;
+ case RADIO_EVENT_METADATA:
+ if (event->metadata != NULL) {
+ event->metadata =
+ (radio_metadata_t *)((char *)event + (size_t)event->metadata);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (mCallback != 0) {
+ mCallback->onEvent(event);
+ }
+}
+
+
+//IBinder::DeathRecipient
+void Radio::binderDied(const wp<IBinder>& who __unused) {
+ Mutex::Autolock _l(mLock);
+ ALOGW("Radio server binder Died ");
+ mIRadio = 0;
+ struct radio_event event;
+ memset(&event, 0, sizeof(struct radio_event));
+ event.type = RADIO_EVENT_SERVER_DIED;
+ event.status = DEAD_OBJECT;
+ if (mCallback != 0) {
+ mCallback->onEvent(&event);
+ }
+}
+
+}; // namespace android
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 93d821a..dddca02 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -69,9 +69,9 @@
#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
#endif
-// Set kUseNewMixer to true to use the new mixer engine. Otherwise the
-// original code will be used. This is false for now.
-static const bool kUseNewMixer = false;
+// Set kUseNewMixer to true to use the new mixer engine always. Otherwise the
+// original code will be used for stereo sinks, the new mixer for multichannel.
+static const bool kUseNewMixer = true;
// Set kUseFloat to true to allow floating input into the mixer engine.
// If kUseNewMixer is false, this is ignored or may be overridden internally
diff --git a/services/audioflinger/FastCaptureDumpState.cpp b/services/audioflinger/FastCaptureDumpState.cpp
index 00f8da0..53eeba5 100644
--- a/services/audioflinger/FastCaptureDumpState.cpp
+++ b/services/audioflinger/FastCaptureDumpState.cpp
@@ -14,7 +14,13 @@
* limitations under the License.
*/
+#define LOG_TAG "FastCaptureDumpState"
+//define LOG_NDEBUG 0
+
+#include "Configuration.h"
+#include <utils/Log.h>
#include "FastCaptureDumpState.h"
+#include "FastCaptureState.h"
namespace android {
@@ -27,4 +33,21 @@
{
}
+void FastCaptureDumpState::dump(int fd) const
+{
+ if (mCommand == FastCaptureState::INITIAL) {
+ dprintf(fd, " FastCapture not initialized\n");
+ return;
+ }
+ double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) +
+ (mMeasuredWarmupTs.tv_nsec / 1000000.0);
+ double periodSec = (double) mFrameCount / mSampleRate;
+ dprintf(fd, " FastCapture command=%s readSequence=%u framesRead=%u\n"
+ " readErrors=%u sampleRate=%u frameCount=%zu\n"
+ " measuredWarmup=%.3g ms, warmupCycles=%u period=%.2f ms\n",
+ FastCaptureState::commandToString(mCommand), mReadSequence, mFramesRead,
+ mReadErrors, mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles,
+ periodSec * 1e3);
+}
+
} // android
diff --git a/services/audioflinger/FastCaptureDumpState.h b/services/audioflinger/FastCaptureDumpState.h
index ee99099..6f9c4c3 100644
--- a/services/audioflinger/FastCaptureDumpState.h
+++ b/services/audioflinger/FastCaptureDumpState.h
@@ -27,6 +27,8 @@
FastCaptureDumpState();
/*virtual*/ ~FastCaptureDumpState();
+ void dump(int fd) const; // should only be called on a stable copy, not the original
+
// FIXME by renaming, could pull up many of these to FastThreadDumpState
uint32_t mReadSequence; // incremented before and after each read()
uint32_t mFramesRead; // total number of frames read successfully
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index e070f90..f1cf0aa 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -415,6 +415,7 @@
memset(mMixerBuffer, 0, mMixerBufferSize);
mMixerBufferState = ZEROED;
}
+ // prepare the buffer used to write to sink
void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer;
if (mFormat.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format
memcpy_by_audio_format(buffer, mFormat.mFormat, mMixerBuffer, mMixerBufferFormat,
diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp
index 65fbf2b..b10942b 100644
--- a/services/audioflinger/FastMixerDumpState.cpp
+++ b/services/audioflinger/FastMixerDumpState.cpp
@@ -64,7 +64,7 @@
}
double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) +
(mMeasuredWarmupTs.tv_nsec / 1000000.0);
- double mixPeriodSec = (double) mFrameCount / (double) mSampleRate;
+ double mixPeriodSec = (double) mFrameCount / mSampleRate;
dprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n"
" numTracks=%u writeErrors=%u underruns=%u overruns=%u\n"
" sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n"
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index c1da6bc..d0b825c 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -5208,7 +5208,7 @@
}
if (initFastCapture) {
- // create a Pipe for FastMixer to write to, and for us and fast tracks to read from
+ // create a Pipe for FastCapture to write to, and for us and fast tracks to read from
NBAIO_Format format = mInputSource->format();
size_t pipeFramesP2 = roundup(mSampleRate / 25); // double-buffering of 20 ms each
size_t pipeSize = pipeFramesP2 * Format_frameSize(format);
@@ -6164,6 +6164,10 @@
}
dprintf(fd, " Fast capture thread: %s\n", hasFastCapture() ? "yes" : "no");
dprintf(fd, " Fast track available: %s\n", mFastTrackAvail ? "yes" : "no");
+
+ // Make a non-atomic copy of fast capture dump state so it won't change underneath us
+ const FastCaptureDumpState copy(mFastCaptureDumpState);
+ copy.dump(fd);
}
void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args __unused)
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 9350e48..d600ea9 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -427,7 +427,7 @@
bool mStandby; // Whether thread is currently in standby.
audio_devices_t mOutDevice; // output device
audio_devices_t mInDevice; // input device
- audio_source_t mAudioSource; // (see audio.h, audio_source_t)
+ audio_source_t mAudioSource;
const audio_io_handle_t mId;
Vector< sp<EffectChain> > mEffectChains;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 8329be4..38667b9 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -444,8 +444,6 @@
// this means we are potentially denying other more important fast tracks from
// being created. It would be better to allocate the index dynamically.
mFastIndex = i;
- // Read the initial underruns because this field is never cleared by the fast mixer
- mObservedUnderruns = thread->getFastTrackUnderruns(i);
thread->mFastTrackAvailMask &= ~(1 << i);
}
}
@@ -694,6 +692,12 @@
}
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if (isFastTrack()) {
+ // refresh fast track underruns on start because that field is never cleared
+ // by the fast mixer; furthermore, the same track can be recycled, i.e. start
+ // after stop.
+ mObservedUnderruns = playbackThread->getFastTrackUnderruns(mFastIndex);
+ }
status = playbackThread->addTrack_l(this);
if (status == INVALID_OPERATION || status == PERMISSION_DENIED) {
triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
diff --git a/services/audiopolicy/managerdefault/Devices.h b/services/audiopolicy/managerdefault/Devices.h
index 65e1416..af2fbda 100644
--- a/services/audiopolicy/managerdefault/Devices.h
+++ b/services/audiopolicy/managerdefault/Devices.h
@@ -41,7 +41,6 @@
audio_devices_t mDeviceType;
String8 mAddress;
- audio_port_handle_t mId;
static String8 emptyNameStr;
};
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 5d6423a..9c60911 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -42,7 +42,6 @@
api1/client2/CaptureSequencer.cpp \
api1/client2/ZslProcessor3.cpp \
api2/CameraDeviceClient.cpp \
- api_pro/ProCamera2Client.cpp \
device2/Camera2Device.cpp \
device3/Camera3Device.cpp \
device3/Camera3Stream.cpp \
@@ -54,6 +53,7 @@
device3/StatusTracker.cpp \
gui/RingBufferConsumer.cpp \
utils/CameraTraces.cpp \
+ utils/AutoConditionLock.cpp \
LOCAL_SHARED_LIBRARIES:= \
libui \
diff --git a/services/camera/libcameraservice/CameraDeviceFactory.cpp b/services/camera/libcameraservice/CameraDeviceFactory.cpp
index bfef50e..6589e27 100644
--- a/services/camera/libcameraservice/CameraDeviceFactory.cpp
+++ b/services/camera/libcameraservice/CameraDeviceFactory.cpp
@@ -48,6 +48,7 @@
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:
device = new Camera3Device(cameraId);
break;
default:
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index 7a79750..6fda9b2 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -110,6 +110,20 @@
status_t res = OK;
Mutex::Autolock l(mLock);
+ if (mOpenedCameraIds.indexOf(cameraId) != NAME_NOT_FOUND) {
+ // This case is needed to avoid state corruption during the following call sequence:
+ // CameraService::setTorchMode for camera ID 0 begins, does torch status checks
+ // CameraService::connect for camera ID 0 begins, calls prepareDeviceOpen, ends
+ // CameraService::setTorchMode for camera ID 0 continues, calls
+ // CameraFlashlight::setTorchMode
+
+ // TODO: Move torch status checks and state updates behind this CameraFlashlight lock
+ // to avoid other similar race conditions.
+ ALOGE("%s: Camera device %s is in use, cannot set torch mode.",
+ __FUNCTION__, cameraId.string());
+ return -EBUSY;
+ }
+
if (mFlashControl == NULL) {
if (enabled == false) {
return OK;
@@ -389,7 +403,7 @@
return NO_MEMORY;
}
res = device->createStream(mAnw, width, height, format,
- HAL_DATASPACE_UNKNOWN, &mStreamId);
+ HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mStreamId);
if (res) {
return res;
}
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 6f37f16..58512eb 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -17,9 +17,14 @@
#define LOG_TAG "CameraService"
//#define LOG_NDEBUG 0
+#include <algorithm>
+#include <climits>
#include <stdio.h>
-#include <string.h>
+#include <cstring>
+#include <ctime>
+#include <string>
#include <sys/types.h>
+#include <inttypes.h>
#include <pthread.h>
#include <binder/AppOpsManager.h>
@@ -27,6 +32,7 @@
#include <binder/IServiceManager.h>
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
+#include <binder/ProcessInfoService.h>
#include <cutils/atomic.h>
#include <cutils/properties.h>
#include <cutils/multiuser.h>
@@ -46,7 +52,6 @@
#include "CameraService.h"
#include "api1/CameraClient.h"
#include "api1/Camera2Client.h"
-#include "api_pro/ProCamera2Client.h"
#include "api2/CameraDeviceClient.h"
#include "utils/CameraTraces.h"
#include "CameraDeviceFactory.h"
@@ -67,25 +72,16 @@
// ----------------------------------------------------------------------------
-static int getCallingPid() {
- return IPCThreadState::self()->getCallingPid();
-}
-
-static int getCallingUid() {
- return IPCThreadState::self()->getCallingUid();
-}
-
extern "C" {
static void camera_device_status_change(
const struct camera_module_callbacks* callbacks,
int camera_id,
int new_status) {
sp<CameraService> cs = const_cast<CameraService*>(
- static_cast<const CameraService*>(callbacks));
+ static_cast<const CameraService*>(callbacks));
- cs->onDeviceStatusChanged(
- camera_id,
- new_status);
+ cs->onDeviceStatusChanged(static_cast<camera_device_status_t>(camera_id),
+ static_cast<camera_device_status_t>(new_status));
}
static void torch_mode_status_change(
@@ -128,23 +124,20 @@
static CameraService *gCameraService;
CameraService::CameraService()
- :mSoundRef(0), mModule(0), mFlashlight(0)
+ : mEventLog(DEFAULT_EVICTION_LOG_LENGTH), mSoundRef(0), mModule(0), mFlashlight(0)
{
ALOGI("CameraService started (pid=%d)", getpid());
gCameraService = this;
- for (size_t i = 0; i < MAX_CAMERAS; ++i) {
- mStatusList[i] = ICameraServiceListener::STATUS_PRESENT;
- }
-
this->camera_device_status_change = android::camera_device_status_change;
this->torch_mode_status_change = android::torch_mode_status_change;
+ mServiceLockWrapper = std::make_shared<WaitableMutexWrapper>(&mServiceLock);
}
void CameraService::onFirstRef()
{
- LOG1("CameraService::onFirstRef");
+ ALOGI("CameraService process starting");
BnCameraService::onFirstRef();
@@ -157,27 +150,53 @@
else {
mModule = new CameraModule(rawModule);
const hw_module_t *common = mModule->getRawModule();
- ALOGI("Loaded \"%s\" cameraCa module", common->name);
+ ALOGI("Loaded \"%s\" camera module", common->name);
mNumberOfCameras = mModule->getNumberOfCameras();
- if (mNumberOfCameras > MAX_CAMERAS) {
- ALOGE("Number of cameras(%d) > MAX_CAMERAS(%d).",
- mNumberOfCameras, MAX_CAMERAS);
- mNumberOfCameras = MAX_CAMERAS;
- }
mFlashlight = new CameraFlashlight(*mModule, *this);
status_t res = mFlashlight->findFlashUnits();
if (res) {
// impossible because we haven't open any camera devices.
- ALOGE("failed to find flash units.");
+ ALOGE("Failed to find flash units.");
}
for (int i = 0; i < mNumberOfCameras; i++) {
- setCameraFree(i);
+ String8 cameraId = String8::format("%d", i);
- String8 cameraName = String8::format("%d", i);
- if (mFlashlight->hasFlashUnit(cameraName)) {
- mTorchStatusMap.add(cameraName,
+ // Defaults to use for cost and conflicting devices
+ int cost = 100;
+ char** conflicting_devices = nullptr;
+ size_t conflicting_devices_length = 0;
+
+ // If using post-2.4 module version, query the cost + conflicting devices from the HAL
+ if (common->module_api_version >= CAMERA_MODULE_API_VERSION_2_4) {
+ struct camera_info info;
+ status_t rc = mModule->getCameraInfo(i, &info);
+ if (rc == NO_ERROR) {
+ cost = info.resource_cost;
+ conflicting_devices = info.conflicting_devices;
+ conflicting_devices_length = info.conflicting_devices_length;
+ } else {
+ ALOGE("%s: Received error loading camera info for device %d, cost and"
+ " conflicting devices fields set to defaults for this device.",
+ __FUNCTION__, i);
+ }
+ }
+
+ std::set<String8> conflicting;
+ for (size_t i = 0; i < conflicting_devices_length; i++) {
+ conflicting.emplace(String8(conflicting_devices[i]));
+ }
+
+ // Initialize state for each camera device
+ {
+ Mutex::Autolock lock(mCameraStatesLock);
+ mCameraStates.emplace(cameraId, std::make_shared<CameraState>(cameraId, cost,
+ conflicting));
+ }
+
+ if (mFlashlight->hasFlashUnit(cameraId)) {
+ mTorchStatusMap.add(cameraId,
ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
}
}
@@ -197,81 +216,70 @@
}
CameraService::~CameraService() {
- for (int i = 0; i < mNumberOfCameras; i++) {
- if (mBusy[i]) {
- ALOGE("camera %d is still in use in destructor!", i);
- }
- }
-
if (mModule) {
delete mModule;
+ mModule = nullptr;
}
VendorTagDescriptor::clearGlobalVendorTagDescriptor();
- gCameraService = NULL;
+ gCameraService = nullptr;
}
-void CameraService::onDeviceStatusChanged(int cameraId,
- int newStatus)
-{
- ALOGV("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__,
+void CameraService::onDeviceStatusChanged(camera_device_status_t cameraId,
+ camera_device_status_t newStatus) {
+ ALOGI("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__,
cameraId, newStatus);
- if (cameraId < 0 || cameraId >= MAX_CAMERAS) {
+ String8 id = String8::format("%d", cameraId);
+ std::shared_ptr<CameraState> state = getCameraState(id);
+
+ if (state == nullptr) {
ALOGE("%s: Bad camera ID %d", __FUNCTION__, cameraId);
return;
}
- if ((int)getStatus(cameraId) == newStatus) {
- ALOGE("%s: State transition to the same status 0x%x not allowed",
- __FUNCTION__, (uint32_t)newStatus);
+ ICameraServiceListener::Status oldStatus = state->getStatus();
+
+ if (oldStatus == static_cast<ICameraServiceListener::Status>(newStatus)) {
+ ALOGE("%s: State transition to the same status %#x not allowed", __FUNCTION__, newStatus);
return;
}
- /* don't do this in updateStatus
- since it is also called from connect and we could get into a deadlock */
if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) {
- Vector<sp<BasicClient> > clientsToDisconnect;
+ sp<BasicClient> clientToDisconnect;
{
- Mutex::Autolock al(mServiceLock);
+ // Don't do this in updateStatus to avoid deadlock over mServiceLock
+ Mutex::Autolock lock(mServiceLock);
- /* Remove cached parameters from shim cache */
- mShimParams.removeItem(cameraId);
+ // Set the device status to NOT_PRESENT, clients will no longer be able to connect
+ // to this device until the status changes
+ updateStatus(ICameraServiceListener::STATUS_NOT_PRESENT, id);
- /* Find all clients that we need to disconnect */
- sp<BasicClient> client = mClient[cameraId].promote();
- if (client.get() != NULL) {
- clientsToDisconnect.push_back(client);
- }
+ // Remove cached shim parameters
+ state->setShimParams(CameraParameters());
- int i = cameraId;
- for (size_t j = 0; j < mProClientList[i].size(); ++j) {
- sp<ProClient> cl = mProClientList[i][j].promote();
- if (cl != NULL) {
- clientsToDisconnect.push_back(cl);
- }
- }
+ // Remove the client from the list of active clients
+ clientToDisconnect = removeClientLocked(id);
+
+ // Notify the client of disconnection
+ clientToDisconnect->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED,
+ CaptureResultExtras{});
}
- /* now disconnect them. don't hold the lock
- or we can get into a deadlock */
+ ALOGI("%s: Client for camera ID %s evicted due to device status change from HAL",
+ __FUNCTION__, id.string());
- for (size_t i = 0; i < clientsToDisconnect.size(); ++i) {
- sp<BasicClient> client = clientsToDisconnect[i];
-
- client->disconnect();
- /**
- * The remote app will no longer be able to call methods on the
- * client since the client PID will be reset to 0
- */
+ // Disconnect client
+ if (clientToDisconnect.get() != nullptr) {
+ // Ensure not in binder RPC so client disconnect PID checks work correctly
+ LOG_ALWAYS_FATAL_IF(getCallingPid() != getpid(),
+ "onDeviceStatusChanged must be called from the camera service process!");
+ clientToDisconnect->disconnect();
}
- ALOGV("%s: After unplug, disconnected %zu clients",
- __FUNCTION__, clientsToDisconnect.size());
+ } else {
+ updateStatus(static_cast<ICameraServiceListener::Status>(newStatus), id);
}
- updateStatus(
- static_cast<ICameraServiceListener::Status>(newStatus), cameraId);
-
}
void CameraService::onTorchStatusChanged(const String8& cameraId,
@@ -304,9 +312,11 @@
return;
}
- Vector<sp<ICameraServiceListener> >::const_iterator it;
- for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
- (*it)->onTorchStatusChanged(newStatus, String16(cameraId.string()));
+ {
+ Mutex::Autolock lock(mStatusListenerLock);
+ for (auto& i : mListenerList) {
+ i->onTorchStatusChanged(newStatus, String16{cameraId});
+ }
}
}
@@ -333,6 +343,15 @@
return rc;
}
+int CameraService::cameraIdToInt(const String8& cameraId) {
+ errno = 0;
+ size_t pos = 0;
+ int ret = stoi(std::string{cameraId.string()}, &pos);
+ if (errno != 0 || pos != cameraId.size()) {
+ return -1;
+ }
+ return ret;
+}
status_t CameraService::generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo) {
status_t ret = OK;
@@ -466,6 +485,54 @@
return ret;
}
+int CameraService::getCallingPid() {
+ return IPCThreadState::self()->getCallingPid();
+}
+
+int CameraService::getCallingUid() {
+ return IPCThreadState::self()->getCallingUid();
+}
+
+String8 CameraService::getFormattedCurrentTime() {
+ time_t now = time(nullptr);
+ char formattedTime[64];
+ strftime(formattedTime, sizeof(formattedTime), "%m-%d %H:%M:%S", localtime(&now));
+ return String8(formattedTime);
+}
+
+int CameraService::getCameraPriorityFromProcState(int procState) {
+ // Find the priority for the camera usage based on the process state. Higher priority clients
+ // win for evictions.
+ // Note: Unlike the ordering for ActivityManager, persistent system processes will always lose
+ // the camera to the top/foreground applications.
+ switch(procState) {
+ case PROCESS_STATE_TOP: // User visible
+ return 100;
+ case PROCESS_STATE_IMPORTANT_FOREGROUND: // Foreground
+ return 90;
+ case PROCESS_STATE_PERSISTENT: // Persistent system services
+ case PROCESS_STATE_PERSISTENT_UI:
+ return 80;
+ case PROCESS_STATE_IMPORTANT_BACKGROUND: // "Important" background processes
+ return 70;
+ case PROCESS_STATE_BACKUP: // Everything else
+ case PROCESS_STATE_HEAVY_WEIGHT:
+ case PROCESS_STATE_SERVICE:
+ case PROCESS_STATE_RECEIVER:
+ case PROCESS_STATE_HOME:
+ case PROCESS_STATE_LAST_ACTIVITY:
+ case PROCESS_STATE_CACHED_ACTIVITY:
+ case PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+ case PROCESS_STATE_CACHED_EMPTY:
+ return 1;
+ case PROCESS_STATE_NONEXISTENT:
+ return -1;
+ default:
+ ALOGE("%s: Received unknown process state from ActivityManagerService!", __FUNCTION__);
+ return -1;
+ }
+}
+
status_t CameraService::getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescriptor>& desc) {
if (!mModule) {
ALOGE("%s: camera hardware module doesn't exist", __FUNCTION__);
@@ -545,54 +612,90 @@
return true;
}
-status_t CameraService::initializeShimMetadata(int cameraId) {
- int pid = getCallingPid();
- int uid = getCallingUid();
- status_t ret = validateConnect(cameraId, uid);
- if (ret != OK) {
- // Error already logged by callee
- return ret;
+status_t 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) {
+
+ // TODO: Update CameraClients + HAL interface to use strings for Camera IDs
+ int id = cameraIdToInt(cameraId);
+ if (id == -1) {
+ ALOGE("%s: Invalid camera ID %s, cannot convert to integer.", __FUNCTION__,
+ cameraId.string());
+ return BAD_VALUE;
}
- bool needsNewClient = false;
- sp<Client> 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, id, facing,
+ clientPid, clientUid, getpid(), legacyMode);
+ } else { // Camera2 API route
+ ALOGW("Camera using old HAL version: %d", deviceVersion);
+ return -EOPNOTSUPP;
+ }
+ break;
+ case CAMERA_DEVICE_API_VERSION_2_0:
+ case CAMERA_DEVICE_API_VERSION_2_1:
+ 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:
+ if (effectiveApiLevel == API_1) { // Camera1 API route
+ sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
+ *client = new Camera2Client(cameraService, tmp, packageName, id, facing,
+ clientPid, clientUid, servicePid, legacyMode);
+ } else { // Camera2 API route
+ sp<ICameraDeviceCallbacks> tmp =
+ static_cast<ICameraDeviceCallbacks*>(cameraCb.get());
+ *client = new CameraDeviceClient(cameraService, tmp, packageName, id,
+ facing, clientPid, clientUid, servicePid);
+ }
+ break;
+ default:
+ // Should not be reachable
+ ALOGE("Unknown camera device HAL version: %d", deviceVersion);
+ return INVALID_OPERATION;
+ }
+ } 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, id, 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 INVALID_OPERATION;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t CameraService::initializeShimMetadata(int cameraId) {
+ int uid = getCallingUid();
String16 internalPackageName("media");
- { // Scope for service lock
- Mutex::Autolock lock(mServiceLock);
- if (mClient[cameraId] != NULL) {
- client = static_cast<Client*>(mClient[cameraId].promote().get());
- }
- if (client == NULL) {
- needsNewClient = true;
- ret = connectHelperLocked(/*out*/client,
- /*cameraClient*/NULL, // Empty binder callbacks
- cameraId,
- internalPackageName,
- uid,
- pid);
-
- if (ret != OK) {
- // Error already logged by callee
- return ret;
- }
- }
-
- if (client == NULL) {
- ALOGE("%s: Could not connect to client camera device.", __FUNCTION__);
- return BAD_VALUE;
- }
-
- String8 rawParams = client->getParameters();
- CameraParameters params(rawParams);
- mShimParams.add(cameraId, params);
+ String8 id = String8::format("%d", cameraId);
+ status_t ret = NO_ERROR;
+ sp<Client> tmp = nullptr;
+ if ((ret = connectHelper<ICameraClient,Client>(sp<ICameraClient>{nullptr}, id,
+ static_cast<int>(CAMERA_HAL_API_VERSION_UNSPECIFIED), internalPackageName, uid, API_1,
+ false, true, tmp)) != NO_ERROR) {
+ ALOGE("%s: Error %d (%s) initializing shim metadata.", __FUNCTION__, ret, strerror(ret));
+ return ret;
}
-
- // Close client if one was opened solely for this call
- if (needsNewClient) {
- client->disconnect();
- }
- return OK;
+ return NO_ERROR;
}
status_t CameraService::getLegacyParametersLazy(int cameraId,
@@ -608,42 +711,54 @@
return BAD_VALUE;
}
- ssize_t index = -1;
- { // Scope for service lock
+ String8 id = String8::format("%d", cameraId);
+
+ // Check if we already have parameters
+ {
+ // Scope for service lock
Mutex::Autolock lock(mServiceLock);
- index = mShimParams.indexOfKey(cameraId);
- // Release service lock so initializeShimMetadata can be called correctly.
-
- if (index >= 0) {
- *parameters = mShimParams[index];
+ auto cameraState = getCameraState(id);
+ if (cameraState == nullptr) {
+ ALOGE("%s: Invalid camera ID: %s", __FUNCTION__, id.string());
+ return BAD_VALUE;
+ }
+ CameraParameters p = cameraState->getShimParams();
+ if (!p.isEmpty()) {
+ *parameters = p;
+ return NO_ERROR;
}
}
- if (index < 0) {
- int64_t token = IPCThreadState::self()->clearCallingIdentity();
- ret = initializeShimMetadata(cameraId);
- IPCThreadState::self()->restoreCallingIdentity(token);
- if (ret != OK) {
- // Error already logged by callee
- return ret;
+ int64_t token = IPCThreadState::self()->clearCallingIdentity();
+ ret = initializeShimMetadata(cameraId);
+ IPCThreadState::self()->restoreCallingIdentity(token);
+ if (ret != NO_ERROR) {
+ // Error already logged by callee
+ return ret;
+ }
+
+ // Check for parameters again
+ {
+ // Scope for service lock
+ Mutex::Autolock lock(mServiceLock);
+ auto cameraState = getCameraState(id);
+ if (cameraState == nullptr) {
+ ALOGE("%s: Invalid camera ID: %s", __FUNCTION__, id.string());
+ return BAD_VALUE;
}
-
- { // Scope for service lock
- Mutex::Autolock lock(mServiceLock);
- index = mShimParams.indexOfKey(cameraId);
-
- LOG_ALWAYS_FATAL_IF(index < 0, "index should have been initialized");
-
- *parameters = mShimParams[index];
+ CameraParameters p = cameraState->getShimParams();
+ if (!p.isEmpty()) {
+ *parameters = p;
+ return NO_ERROR;
}
}
- return OK;
+ ALOGE("%s: Parameters were not initialized, or were empty. Device may not be present.",
+ __FUNCTION__);
+ return INVALID_OPERATION;
}
-status_t CameraService::validateConnect(int cameraId,
- /*inout*/
- int& clientUid) const {
+status_t CameraService::validateConnect(const String8& cameraId, /*inout*/int& clientUid) const {
int callingPid = getCallingPid();
@@ -652,23 +767,25 @@
} else {
// We only trust our own process to forward client UIDs
if (callingPid != getpid()) {
- ALOGE("CameraService::connect X (pid %d) rejected (don't trust clientUid)",
+ ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid)",
callingPid);
return PERMISSION_DENIED;
}
}
if (!mModule) {
- ALOGE("Camera HAL module not loaded");
+ ALOGE("CameraService::connect X (PID %d) rejected (camera HAL module not loaded)",
+ callingPid);
return -ENODEV;
}
- if (cameraId < 0 || cameraId >= mNumberOfCameras) {
- ALOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).",
- callingPid, cameraId);
+ if (getCameraState(cameraId) == nullptr) {
+ ALOGE("CameraService::connect X (PID %d) rejected (invalid camera ID %s)", callingPid,
+ cameraId.string());
return -ENODEV;
}
+ // Check device policy for this camera
char value[PROPERTY_VALUE_MAX];
char key[PROPERTY_KEY_MAX];
int clientUserId = multiuser_get_user_id(clientUid);
@@ -676,142 +793,218 @@
property_get(key, value, "0");
if (strcmp(value, "1") == 0) {
// Camera is disabled by DevicePolicyManager.
- ALOGI("Camera is disabled. connect X (pid %d) rejected", callingPid);
+ ALOGE("CameraService::connect X (PID %d) rejected (camera %s is disabled by device "
+ "policy)", callingPid, cameraId.string());
return -EACCES;
}
- ICameraServiceListener::Status currentStatus = getStatus(cameraId);
+ return checkIfDeviceIsUsable(cameraId);
+}
+
+status_t CameraService::checkIfDeviceIsUsable(const String8& cameraId) const {
+ auto cameraState = getCameraState(cameraId);
+ int callingPid = getCallingPid();
+ if (cameraState == nullptr) {
+ ALOGE("CameraService::connect X (PID %d) rejected (invalid camera ID %s)", callingPid,
+ cameraId.string());
+ return -ENODEV;
+ }
+
+ ICameraServiceListener::Status currentStatus = cameraState->getStatus();
if (currentStatus == ICameraServiceListener::STATUS_NOT_PRESENT) {
- ALOGI("Camera is not plugged in,"
- " connect X (pid %d) rejected", callingPid);
+ ALOGE("CameraService::connect X (PID %d) rejected (camera %s is not connected)",
+ callingPid, cameraId.string());
return -ENODEV;
} else if (currentStatus == ICameraServiceListener::STATUS_ENUMERATING) {
- ALOGI("Camera is enumerating,"
- " connect X (pid %d) rejected", callingPid);
+ ALOGE("CameraService::connect X (PID %d) rejected, (camera %s is initializing)",
+ callingPid, cameraId.string());
return -EBUSY;
}
- // Else don't check for STATUS_NOT_AVAILABLE.
- // -- It's done implicitly in canConnectUnsafe /w the mBusy array
- return OK;
+ return NO_ERROR;
}
-bool CameraService::canConnectUnsafe(int cameraId,
- const String16& clientPackageName,
- const sp<IBinder>& remoteCallback,
- sp<BasicClient> &client) {
- String8 clientName8(clientPackageName);
- int callingPid = getCallingPid();
+void CameraService::finishConnectLocked(const sp<BasicClient>& client,
+ const CameraService::DescriptorPtr& desc) {
- if (mClient[cameraId] != 0) {
- client = mClient[cameraId].promote();
- if (client != 0) {
- if (remoteCallback == client->getRemote()) {
- LOG1("CameraService::connect X (pid %d) (the same client)",
- callingPid);
- return true;
- } else {
- // TODOSC: need to support 1 regular client,
- // multiple shared clients here
- ALOGW("CameraService::connect X (pid %d) rejected"
- " (existing client).", callingPid);
- return false;
+ // Make a descriptor for the incoming client
+ auto clientDescriptor = CameraService::CameraClientManager::makeClientDescriptor(client, desc);
+ auto evicted = mActiveClientManager.addAndEvict(clientDescriptor);
+
+ logConnected(desc->getKey(), static_cast<int>(desc->getOwnerId()),
+ String8(client->getPackageName()));
+
+ if (evicted.size() > 0) {
+ // This should never happen - clients should already have been removed in disconnect
+ for (auto& i : evicted) {
+ ALOGE("%s: Invalid state: Client for camera %s was not removed in disconnect",
+ __FUNCTION__, i->getKey().string());
+ }
+
+ LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, clients not evicted properly",
+ __FUNCTION__);
+ }
+}
+
+status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clientPid,
+ apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName,
+ /*out*/
+ sp<BasicClient>* client,
+ std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial) {
+
+ status_t ret = NO_ERROR;
+ std::vector<sp<BasicClient>> evictedClients;
+ DescriptorPtr clientDescriptor;
+ {
+ if (effectiveApiLevel == API_1) {
+ // If we are using API1, any existing client for this camera ID with the same remote
+ // should be returned rather than evicted to allow MediaRecorder to work properly.
+
+ auto current = mActiveClientManager.get(cameraId);
+ if (current != nullptr) {
+ auto clientSp = current->getValue();
+ if (clientSp.get() != nullptr) { // should never be needed
+ if (clientSp->getRemote() == remoteCallback) {
+ ALOGI("CameraService::connect X (PID %d) (second call from same"
+ "app binder, returning the same client)", clientPid);
+ *client = clientSp;
+ return NO_ERROR;
+ }
+ }
}
}
- mClient[cameraId].clear();
- }
- /*
- mBusy is set to false as the last step of the Client destructor,
- after which it is guaranteed that the Client destructor has finished (
- including any inherited destructors)
+ // Return error if the device was unplugged or removed by the HAL for some reason
+ if ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) {
+ return ret;
+ }
- We only need this for a Client subclasses since we don't allow
- multiple Clents to be opened concurrently, but multiple BasicClient
- would be fine
- */
- if (mBusy[cameraId]) {
- ALOGW("CameraService::connect X (pid %d, \"%s\") rejected"
- " (camera %d is still busy).", callingPid,
- clientName8.string(), cameraId);
- return false;
- }
+ // Get current active client PIDs
+ std::vector<int> ownerPids(mActiveClientManager.getAllOwners());
+ ownerPids.push_back(clientPid);
- return true;
-}
+ // Use the value +PROCESS_STATE_NONEXISTENT, to avoid taking
+ // address of PROCESS_STATE_NONEXISTENT as a reference argument
+ // for the vector constructor. PROCESS_STATE_NONEXISTENT does
+ // not have an out-of-class definition.
+ std::vector<int> priorities(ownerPids.size(), +PROCESS_STATE_NONEXISTENT);
-status_t CameraService::connectHelperLocked(
- /*out*/
- sp<Client>& client,
- /*in*/
- const sp<ICameraClient>& cameraClient,
- int cameraId,
- const String16& clientPackageName,
- int clientUid,
- int callingPid,
- int halVersion,
- bool legacyMode) {
+ // Get priorites of all active PIDs
+ ProcessInfoService::getProcessStatesFromPids(ownerPids.size(), &ownerPids[0],
+ /*out*/&priorities[0]);
- // give flashlight a chance to close devices if necessary.
- mFlashlight->prepareDeviceOpen(String8::format("%d", cameraId));
+ // Update all active clients' priorities
+ std::map<int,int> pidToPriorityMap;
+ for (size_t i = 0; i < ownerPids.size() - 1; i++) {
+ pidToPriorityMap.emplace(ownerPids[i], getCameraPriorityFromProcState(priorities[i]));
+ }
+ mActiveClientManager.updatePriorities(pidToPriorityMap);
- int facing = -1;
- int deviceVersion = getDeviceVersion(cameraId, &facing);
-
- 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:
- client = new CameraClient(this, cameraClient,
- clientPackageName, cameraId,
- facing, callingPid, clientUid, getpid(), legacyMode);
- break;
- case CAMERA_DEVICE_API_VERSION_2_0:
- case CAMERA_DEVICE_API_VERSION_2_1:
- case CAMERA_DEVICE_API_VERSION_3_0:
- case CAMERA_DEVICE_API_VERSION_3_1:
- case CAMERA_DEVICE_API_VERSION_3_2:
- client = new Camera2Client(this, cameraClient,
- clientPackageName, cameraId,
- facing, callingPid, clientUid, getpid(), legacyMode);
- break;
- case -1:
- ALOGE("Invalid camera id %d", cameraId);
+ // Get state for the given cameraId
+ auto state = getCameraState(cameraId);
+ if (state == nullptr) {
+ ALOGE("CameraService::connect X (PID %d) rejected (no camera device with ID %s)",
+ clientPid, cameraId.string());
return BAD_VALUE;
- default:
- ALOGE("Unknown camera device HAL version: %d", deviceVersion);
- return INVALID_OPERATION;
}
- } 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.
- client = new CameraClient(this, cameraClient,
- clientPackageName, cameraId,
- facing, callingPid, clientUid, getpid(), 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 INVALID_OPERATION;
+
+ // Make descriptor for incoming client
+ clientDescriptor = CameraClientManager::makeClientDescriptor(cameraId,
+ sp<BasicClient>{nullptr}, static_cast<int32_t>(state->getCost()),
+ state->getConflicting(),
+ getCameraPriorityFromProcState(priorities[priorities.size() - 1]), clientPid);
+
+ // Find clients that would be evicted
+ auto evicted = mActiveClientManager.wouldEvict(clientDescriptor);
+
+ // If the incoming client was 'evicted,' higher priority clients have the camera in the
+ // background, so we cannot do evictions
+ if (std::find(evicted.begin(), evicted.end(), clientDescriptor) != evicted.end()) {
+ ALOGE("CameraService::connect X (PID %d) rejected (existing client(s) with higher"
+ " priority).", clientPid);
+
+ sp<BasicClient> clientSp = clientDescriptor->getValue();
+ String8 curTime = getFormattedCurrentTime();
+ auto incompatibleClients =
+ mActiveClientManager.getIncompatibleClients(clientDescriptor);
+
+ String8 msg = String8::format("%s : DENIED connect device %s client for package %s "
+ "(PID %d, priority %d)", curTime.string(),
+ cameraId.string(), packageName.string(), clientPid,
+ getCameraPriorityFromProcState(priorities[priorities.size() - 1]));
+
+ for (auto& i : incompatibleClients) {
+ msg.appendFormat("\n - Blocked by existing device %s client for package %s"
+ "(PID %" PRId32 ", priority %" PRId32 ")", i->getKey().string(),
+ String8{i->getValue()->getPackageName()}.string(), i->getOwnerId(),
+ i->getPriority());
+ }
+
+ // Log the client's attempt
+ mEventLog.add(msg);
+
+ return -EBUSY;
+ }
+
+ for (auto& i : evicted) {
+ sp<BasicClient> clientSp = i->getValue();
+ if (clientSp.get() == nullptr) {
+ ALOGE("%s: Invalid state: Null client in active client list.", __FUNCTION__);
+
+ // TODO: Remove this
+ LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, null client in active list",
+ __FUNCTION__);
+ mActiveClientManager.remove(i);
+ continue;
+ }
+
+ ALOGE("CameraService::connect evicting conflicting client for camera ID %s",
+ i->getKey().string());
+ evictedClients.push_back(clientSp);
+
+ String8 curTime = getFormattedCurrentTime();
+
+ // Log the clients evicted
+ mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %"
+ PRId32 ", priority %" PRId32 ")\n - Evicted by device %s client for "
+ "package %s (PID %d, priority %" PRId32 ")", curTime.string(),
+ i->getKey().string(), String8{clientSp->getPackageName()}.string(),
+ i->getOwnerId(), i->getPriority(), cameraId.string(),
+ packageName.string(), clientPid,
+ getCameraPriorityFromProcState(priorities[priorities.size() - 1])));
+
+ // Notify the client of disconnection
+ clientSp->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED,
+ CaptureResultExtras());
}
}
- status_t status = connectFinishUnsafe(client, client->getRemote());
- if (status != OK) {
- // this is probably not recoverable.. maybe the client can try again
- return status;
+ // Do not hold mServiceLock while disconnecting clients, but retain the condition blocking
+ // other clients from connecting in mServiceLockWrapper if held
+ mServiceLock.unlock();
+
+ // Clear caller identity temporarily so client disconnect PID checks work correctly
+ int64_t token = IPCThreadState::self()->clearCallingIdentity();
+
+ // Destroy evicted clients
+ for (auto& i : evictedClients) {
+ // Disconnect is blocking, and should only have returned when HAL has cleaned up
+ i->disconnect(); // Clients will remove themselves from the active client list here
+ }
+ evictedClients.clear();
+
+ IPCThreadState::self()->restoreCallingIdentity(token);
+
+ // Once clients have been disconnected, relock
+ mServiceLock.lock();
+
+ // Check again if the device was unplugged or something while we weren't holding mServiceLock
+ if ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) {
+ return ret;
}
- mClient[cameraId] = client;
- LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId,
- getpid());
-
- return OK;
+ *partial = clientDescriptor;
+ return NO_ERROR;
}
status_t CameraService::connect(
@@ -822,47 +1015,18 @@
/*out*/
sp<ICamera>& device) {
- String8 clientName8(clientPackageName);
- int callingPid = getCallingPid();
+ status_t ret = NO_ERROR;
+ String8 id = String8::format("%d", cameraId);
+ sp<Client> client = nullptr;
+ ret = connectHelper<ICameraClient,Client>(cameraClient, id, CAMERA_HAL_API_VERSION_UNSPECIFIED,
+ clientPackageName, clientUid, API_1, false, false, /*out*/client);
- LOG1("CameraService::connect E (pid %d \"%s\", id %d)", callingPid,
- clientName8.string(), cameraId);
-
- status_t status = validateConnect(cameraId, /*inout*/clientUid);
- if (status != OK) {
- return status;
+ if(ret != NO_ERROR) {
+ return ret;
}
-
- sp<Client> client;
- {
- Mutex::Autolock lock(mServiceLock);
- sp<BasicClient> clientTmp;
- if (!canConnectUnsafe(cameraId, clientPackageName,
- IInterface::asBinder(cameraClient),
- /*out*/clientTmp)) {
- return -EBUSY;
- } else if (client.get() != NULL) {
- device = static_cast<Client*>(clientTmp.get());
- return OK;
- }
-
- status = connectHelperLocked(/*out*/client,
- cameraClient,
- cameraId,
- clientPackageName,
- clientUid,
- callingPid);
- if (status != OK) {
- return status;
- }
-
- }
- // 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 OK;
+ return NO_ERROR;
}
status_t CameraService::connectLegacy(
@@ -887,70 +1051,41 @@
return INVALID_OPERATION;
}
- String8 clientName8(clientPackageName);
- int callingPid = getCallingPid();
+ status_t ret = NO_ERROR;
+ String8 id = String8::format("%d", cameraId);
+ sp<Client> client = nullptr;
+ ret = connectHelper<ICameraClient,Client>(cameraClient, id, halVersion, clientPackageName,
+ clientUid, API_1, true, false, /*out*/client);
- LOG1("CameraService::connect legacy E (pid %d \"%s\", id %d)", callingPid,
- clientName8.string(), cameraId);
-
- status_t status = validateConnect(cameraId, /*inout*/clientUid);
- if (status != OK) {
- return status;
+ if(ret != NO_ERROR) {
+ return ret;
}
- sp<Client> client;
- {
- Mutex::Autolock lock(mServiceLock);
- sp<BasicClient> clientTmp;
- if (!canConnectUnsafe(cameraId, clientPackageName,
- IInterface::asBinder(cameraClient),
- /*out*/clientTmp)) {
- return -EBUSY;
- } else if (client.get() != NULL) {
- device = static_cast<Client*>(clientTmp.get());
- return OK;
- }
-
- status = connectHelperLocked(/*out*/client,
- cameraClient,
- cameraId,
- clientPackageName,
- clientUid,
- callingPid,
- halVersion,
- /*legacyMode*/true);
- if (status != OK) {
- return status;
- }
-
- }
- // 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 OK;
+ return NO_ERROR;
}
-bool CameraService::validCameraIdForSetTorchMode(const String8& cameraId) {
- // invalid string for int
- if (cameraId.string() == NULL) {
- return false;
- }
- errno = 0;
- char *endptr;
- long id = strtol(cameraId.string(), &endptr, 10); // base 10
- if (errno || id > INT_MAX || id < INT_MIN || *endptr != 0) {
- return false;
+status_t CameraService::connectDevice(
+ const sp<ICameraDeviceCallbacks>& cameraCb,
+ int cameraId,
+ const String16& clientPackageName,
+ int clientUid,
+ /*out*/
+ sp<ICameraDeviceUser>& device) {
+
+ status_t ret = NO_ERROR;
+ String8 id = String8::format("%d", cameraId);
+ sp<CameraDeviceClient> client = nullptr;
+ ret = connectHelper<ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id,
+ CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, clientUid, API_2, false, false,
+ /*out*/client);
+
+ if(ret != NO_ERROR) {
+ return ret;
}
- // id matches one of the plugged-in devices?
- ICameraServiceListener::Status deviceStatus = getStatus(id);
- if (deviceStatus != ICameraServiceListener::STATUS_PRESENT &&
- deviceStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) {
- return false;
- }
-
- return true;
+ device = client;
+ return NO_ERROR;
}
status_t CameraService::setTorchMode(const String16& cameraId, bool enabled,
@@ -963,7 +1098,15 @@
String8 id = String8(cameraId.string());
// verify id is valid.
- if (validCameraIdForSetTorchMode(id) == false) {
+ auto state = getCameraState(id);
+ if (state == nullptr) {
+ ALOGE("%s: camera id is invalid %s", id.string());
+ return -EINVAL;
+ }
+
+ ICameraServiceListener::Status cameraStatus = state->getStatus();
+ if (cameraStatus != ICameraServiceListener::STATUS_PRESENT &&
+ cameraStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) {
ALOGE("%s: camera id is invalid %s", id.string());
return -EINVAL;
}
@@ -979,8 +1122,7 @@
}
if (status == ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE) {
- if (getStatus(atoi(id.string())) ==
- ICameraServiceListener::STATUS_NOT_AVAILABLE) {
+ if (cameraStatus == ICameraServiceListener::STATUS_NOT_AVAILABLE) {
ALOGE("%s: torch mode of camera %s is not available because "
"camera is in use", __FUNCTION__, id.string());
return -EBUSY;
@@ -1022,174 +1164,6 @@
return OK;
}
-status_t CameraService::connectFinishUnsafe(const sp<BasicClient>& client,
- const sp<IBinder>& remoteCallback) {
- status_t status = client->initialize(mModule);
- if (status != OK) {
- ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__);
- return status;
- }
- if (remoteCallback != NULL) {
- remoteCallback->linkToDeath(this);
- }
-
- return OK;
-}
-
-status_t CameraService::connectPro(
- const sp<IProCameraCallbacks>& cameraCb,
- int cameraId,
- const String16& clientPackageName,
- int clientUid,
- /*out*/
- sp<IProCameraUser>& device)
-{
- if (cameraCb == 0) {
- ALOGE("%s: Callback must not be null", __FUNCTION__);
- return BAD_VALUE;
- }
-
- String8 clientName8(clientPackageName);
- int callingPid = getCallingPid();
-
- LOG1("CameraService::connectPro E (pid %d \"%s\", id %d)", callingPid,
- clientName8.string(), cameraId);
- status_t status = validateConnect(cameraId, /*inout*/clientUid);
- if (status != OK) {
- return status;
- }
-
- sp<ProClient> client;
- {
- Mutex::Autolock lock(mServiceLock);
- {
- sp<BasicClient> client;
- if (!canConnectUnsafe(cameraId, clientPackageName,
- IInterface::asBinder(cameraCb),
- /*out*/client)) {
- return -EBUSY;
- }
- }
-
- int facing = -1;
- int deviceVersion = getDeviceVersion(cameraId, &facing);
-
- switch(deviceVersion) {
- case CAMERA_DEVICE_API_VERSION_1_0:
- ALOGE("Camera id %d uses HALv1, doesn't support ProCamera",
- cameraId);
- return -EOPNOTSUPP;
- break;
- case CAMERA_DEVICE_API_VERSION_2_0:
- case CAMERA_DEVICE_API_VERSION_2_1:
- case CAMERA_DEVICE_API_VERSION_3_0:
- case CAMERA_DEVICE_API_VERSION_3_1:
- case CAMERA_DEVICE_API_VERSION_3_2:
- client = new ProCamera2Client(this, cameraCb, clientPackageName,
- cameraId, facing, callingPid, clientUid, getpid());
- break;
- case -1:
- ALOGE("Invalid camera id %d", cameraId);
- return BAD_VALUE;
- default:
- ALOGE("Unknown camera device HAL version: %d", deviceVersion);
- return INVALID_OPERATION;
- }
-
- status_t status = connectFinishUnsafe(client, client->getRemote());
- if (status != OK) {
- return status;
- }
-
- mProClientList[cameraId].push(client);
-
- LOG1("CameraService::connectPro X (id %d, this pid is %d)", cameraId,
- getpid());
- }
- // 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 OK;
-}
-
-status_t CameraService::connectDevice(
- const sp<ICameraDeviceCallbacks>& cameraCb,
- int cameraId,
- const String16& clientPackageName,
- int clientUid,
- /*out*/
- sp<ICameraDeviceUser>& device)
-{
-
- String8 clientName8(clientPackageName);
- int callingPid = getCallingPid();
-
- LOG1("CameraService::connectDevice E (pid %d \"%s\", id %d)", callingPid,
- clientName8.string(), cameraId);
-
- status_t status = validateConnect(cameraId, /*inout*/clientUid);
- if (status != OK) {
- return status;
- }
-
- sp<CameraDeviceClient> client;
- {
- Mutex::Autolock lock(mServiceLock);
- {
- sp<BasicClient> client;
- if (!canConnectUnsafe(cameraId, clientPackageName,
- IInterface::asBinder(cameraCb),
- /*out*/client)) {
- return -EBUSY;
- }
- }
-
- int facing = -1;
- int deviceVersion = getDeviceVersion(cameraId, &facing);
-
- // give flashlight a chance to close devices if necessary.
- mFlashlight->prepareDeviceOpen(String8::format("%d", cameraId));
-
- switch(deviceVersion) {
- case CAMERA_DEVICE_API_VERSION_1_0:
- ALOGW("Camera using old HAL version: %d", deviceVersion);
- return -EOPNOTSUPP;
- // TODO: don't allow 2.0 Only allow 2.1 and higher
- case CAMERA_DEVICE_API_VERSION_2_0:
- case CAMERA_DEVICE_API_VERSION_2_1:
- case CAMERA_DEVICE_API_VERSION_3_0:
- case CAMERA_DEVICE_API_VERSION_3_1:
- case CAMERA_DEVICE_API_VERSION_3_2:
- client = new CameraDeviceClient(this, cameraCb, clientPackageName,
- cameraId, facing, callingPid, clientUid, getpid());
- break;
- case -1:
- ALOGE("Invalid camera id %d", cameraId);
- return BAD_VALUE;
- default:
- ALOGE("Unknown camera device HAL version: %d", deviceVersion);
- return INVALID_OPERATION;
- }
-
- status_t status = connectFinishUnsafe(client, client->getRemote());
- if (status != OK) {
- // this is probably not recoverable.. maybe the client can try again
- return status;
- }
-
- LOG1("CameraService::connectDevice X (id %d, this pid is %d)", cameraId,
- getpid());
-
- mClient[cameraId] = client;
- }
- // 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 OK;
-}
-
-
status_t CameraService::addListener(
const sp<ICameraServiceListener>& listener) {
ALOGV("%s: Add listener %p", __FUNCTION__, listener.get());
@@ -1201,23 +1175,29 @@
Mutex::Autolock lock(mServiceLock);
- Vector<sp<ICameraServiceListener> >::iterator it, end;
- for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
- if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) {
- ALOGW("%s: Tried to add listener %p which was already subscribed",
- __FUNCTION__, listener.get());
- return ALREADY_EXISTS;
+ {
+ Mutex::Autolock lock(mStatusListenerLock);
+ for (auto& it : mListenerList) {
+ if (IInterface::asBinder(it) == IInterface::asBinder(listener)) {
+ ALOGW("%s: Tried to add listener %p which was already subscribed",
+ __FUNCTION__, listener.get());
+ return ALREADY_EXISTS;
+ }
}
+
+ mListenerList.push_back(listener);
}
- mListenerList.push_back(listener);
/* Immediately signal current status to this listener only */
{
- Mutex::Autolock m(mStatusMutex) ;
- int numCams = getNumberOfCameras();
- for (int i = 0; i < numCams; ++i) {
- listener->onStatusChanged(mStatusList[i], i);
+ Mutex::Autolock lock(mCameraStatesLock);
+ for (auto& i : mCameraStates) {
+ // TODO: Update binder to use String16 for camera IDs and remove;
+ int id = cameraIdToInt(i.first);
+ if (id == -1) continue;
+
+ listener->onStatusChanged(i.second->getStatus(), id);
}
}
@@ -1228,13 +1208,12 @@
String16 id = String16(mTorchStatusMap.keyAt(i).string());
listener->onTorchStatusChanged(mTorchStatusMap.valueAt(i), id);
}
-
}
return OK;
}
-status_t CameraService::removeListener(
- const sp<ICameraServiceListener>& listener) {
+
+status_t CameraService::removeListener(const sp<ICameraServiceListener>& listener) {
ALOGV("%s: Remove listener %p", __FUNCTION__, listener.get());
if (listener == 0) {
@@ -1244,11 +1223,13 @@
Mutex::Autolock lock(mServiceLock);
- Vector<sp<ICameraServiceListener> >::iterator it;
- for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
- if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) {
- mListenerList.erase(it);
- return OK;
+ {
+ Mutex::Autolock lock(mStatusListenerLock);
+ for (auto it = mListenerList.begin(); it != mListenerList.end(); it++) {
+ if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) {
+ mListenerList.erase(it);
+ return OK;
+ }
}
}
@@ -1258,10 +1239,7 @@
return BAD_VALUE;
}
-status_t CameraService::getLegacyParameters(
- int cameraId,
- /*out*/
- String16* parameters) {
+status_t CameraService::getLegacyParameters(int cameraId, /*out*/String16* parameters) {
ALOGV("%s: for camera ID = %d", __FUNCTION__, cameraId);
if (parameters == NULL) {
@@ -1316,6 +1294,7 @@
return OK;
}
case CAMERA_DEVICE_API_VERSION_3_2:
+ case CAMERA_DEVICE_API_VERSION_3_3:
ALOGV("%s: Camera id %d uses HAL3.2 or newer, supports api1/api2 directly",
__FUNCTION__, cameraId);
return OK;
@@ -1330,127 +1309,107 @@
return OK;
}
-void CameraService::removeClientByRemote(const wp<IBinder>& remoteBinder) {
- int callingPid = getCallingPid();
- LOG1("CameraService::removeClientByRemote E (pid %d)", callingPid);
-
- // Declare this before the lock to make absolutely sure the
- // destructor won't be called with the lock held.
+void CameraService::removeByClient(const BasicClient* client) {
Mutex::Autolock lock(mServiceLock);
-
- int outIndex;
- sp<BasicClient> client = findClientUnsafe(remoteBinder, outIndex);
-
- if (client != 0) {
- // Found our camera, clear and leave.
- LOG1("removeClient: clear camera %d", outIndex);
-
- sp<IBinder> remote = client->getRemote();
- if (remote != NULL) {
- remote->unlinkToDeath(this);
- }
-
- mClient[outIndex].clear();
- } else {
-
- sp<ProClient> clientPro = findProClientUnsafe(remoteBinder);
-
- if (clientPro != NULL) {
- // Found our camera, clear and leave.
- LOG1("removeClient: clear pro %p", clientPro.get());
-
- IInterface::asBinder(clientPro->getRemoteCallback())->unlinkToDeath(this);
+ for (auto& i : mActiveClientManager.getAll()) {
+ auto clientSp = i->getValue();
+ if (clientSp.get() == client) {
+ mActiveClientManager.remove(i);
}
}
-
- LOG1("CameraService::removeClientByRemote X (pid %d)", callingPid);
}
-sp<CameraService::ProClient> CameraService::findProClientUnsafe(
- const wp<IBinder>& cameraCallbacksRemote)
-{
- sp<ProClient> clientPro;
+bool CameraService::evictClientIdByRemote(const wp<IBinder>& remote) {
+ const int callingPid = getCallingPid();
+ const int servicePid = getpid();
+ bool ret = false;
+ {
+ // Acquire mServiceLock and prevent other clients from connecting
+ std::unique_ptr<AutoConditionLock> lock =
+ AutoConditionLock::waitAndAcquire(mServiceLockWrapper);
- for (int i = 0; i < mNumberOfCameras; ++i) {
- Vector<size_t> removeIdx;
- for (size_t j = 0; j < mProClientList[i].size(); ++j) {
- wp<ProClient> cl = mProClientList[i][j];
+ std::vector<sp<BasicClient>> evicted;
+ for (auto& i : mActiveClientManager.getAll()) {
+ auto clientSp = i->getValue();
+ if (clientSp.get() == nullptr) {
+ ALOGE("%s: Dead client still in mActiveClientManager.", __FUNCTION__);
+ mActiveClientManager.remove(i);
+ continue;
+ }
+ if (remote == clientSp->getRemote() && (callingPid == servicePid ||
+ callingPid == clientSp->getClientPid())) {
+ mActiveClientManager.remove(i);
+ evicted.push_back(clientSp);
- sp<ProClient> clStrong = cl.promote();
- if (clStrong != NULL && clStrong->getRemote() == cameraCallbacksRemote) {
- clientPro = clStrong;
- break;
- } else if (clStrong == NULL) {
- // mark to clean up dead ptr
- removeIdx.push(j);
+ // Notify the client of disconnection
+ clientSp->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED,
+ CaptureResultExtras());
}
}
- // remove stale ptrs (in reverse so the indices dont change)
- for (ssize_t j = (ssize_t)removeIdx.size() - 1; j >= 0; --j) {
- mProClientList[i].removeAt(removeIdx[j]);
+ // Do not hold mServiceLock while disconnecting clients, but retain the condition blocking
+ // other clients from connecting in mServiceLockWrapper if held
+ mServiceLock.unlock();
+
+ for (auto& i : evicted) {
+ if (i.get() != nullptr) {
+ i->disconnect();
+ ret = true;
+ }
}
- }
+ // Reacquire mServiceLock
+ mServiceLock.lock();
- return clientPro;
+ } // lock is destroyed, allow further connect calls
+
+ return ret;
}
-sp<CameraService::BasicClient> CameraService::findClientUnsafe(
- const wp<IBinder>& cameraClient, int& outIndex) {
- sp<BasicClient> client;
- for (int i = 0; i < mNumberOfCameras; i++) {
-
- // This happens when we have already disconnected (or this is
- // just another unused camera).
- if (mClient[i] == 0) continue;
-
- // Promote mClient. It can fail if we are called from this path:
- // Client::~Client() -> disconnect() -> removeClientByRemote().
- client = mClient[i].promote();
-
- // Clean up stale client entry
- if (client == NULL) {
- mClient[i].clear();
- continue;
- }
-
- if (cameraClient == client->getRemote()) {
- // Found our camera
- outIndex = i;
- return client;
+std::shared_ptr<CameraService::CameraState> CameraService::getCameraState(
+ const String8& cameraId) const {
+ std::shared_ptr<CameraState> state;
+ {
+ Mutex::Autolock lock(mCameraStatesLock);
+ auto iter = mCameraStates.find(cameraId);
+ if (iter != mCameraStates.end()) {
+ state = iter->second;
}
}
-
- outIndex = -1;
- return NULL;
+ return state;
}
-CameraService::BasicClient* CameraService::getClientByIdUnsafe(int cameraId) {
- if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL;
- return mClient[cameraId].unsafe_get();
+sp<CameraService::BasicClient> CameraService::removeClientLocked(const String8& cameraId) {
+ // Remove from active clients list
+ auto clientDescriptorPtr = mActiveClientManager.remove(cameraId);
+ if (clientDescriptorPtr == nullptr) {
+ ALOGW("%s: Could not evict client, no client for camera ID %s", __FUNCTION__,
+ cameraId.string());
+ return sp<BasicClient>{nullptr};
+ }
+
+ return clientDescriptorPtr->getValue();
}
-Mutex* CameraService::getClientLockById(int cameraId) {
- if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL;
- return &mClientLock[cameraId];
+
+void CameraService::logDisconnected(const String8& cameraId, int clientPid,
+ const String8& clientPackage) {
+
+ String8 curTime = getFormattedCurrentTime();
+ // Log the clients evicted
+ mEventLog.add(String8::format("%s : DISCONNECT device %s client for package %s (PID %d)",
+ curTime.string(), cameraId.string(), clientPackage.string(), clientPid));
}
-sp<CameraService::BasicClient> CameraService::getClientByRemote(
- const wp<IBinder>& cameraClient) {
+void CameraService::logConnected(const String8& cameraId, int clientPid,
+ const String8& clientPackage) {
- // Declare this before the lock to make absolutely sure the
- // destructor won't be called with the lock held.
- sp<BasicClient> client;
-
- Mutex::Autolock lock(mServiceLock);
-
- int outIndex;
- client = findClientUnsafe(cameraClient, outIndex);
-
- return client;
+ String8 curTime = getFormattedCurrentTime();
+ // Log the clients evicted
+ mEventLog.add(String8::format("%s : CONNECT device %s client for package %s (PID %d)",
+ curTime.string(), cameraId.string(), clientPackage.string(), clientPid));
}
status_t CameraService::onTransact(
@@ -1458,7 +1417,6 @@
// Permission checks
switch (code) {
case BnCameraService::CONNECT:
- case BnCameraService::CONNECT_PRO:
case BnCameraService::CONNECT_DEVICE:
case BnCameraService::CONNECT_LEGACY:
const int pid = getCallingPid();
@@ -1479,24 +1437,6 @@
return BnCameraService::onTransact(code, data, reply, flags);
}
-// The reason we need this busy bit is a new CameraService::connect() request
-// may come in while the previous Client's destructor has not been run or is
-// still running. If the last strong reference of the previous Client is gone
-// but the destructor has not been finished, we should not allow the new Client
-// to be created because we need to wait for the previous Client to tear down
-// the hardware first.
-void CameraService::setCameraBusy(int cameraId) {
- android_atomic_write(1, &mBusy[cameraId]);
-
- ALOGV("setCameraBusy cameraId=%d", cameraId);
-}
-
-void CameraService::setCameraFree(int cameraId) {
- android_atomic_write(0, &mBusy[cameraId]);
-
- ALOGV("setCameraFree cameraId=%d", cameraId);
-}
-
// We share the media players for shutter and recording sound for all clients.
// A reference count is kept to determine when we will actually release the
// media players.
@@ -1565,7 +1505,6 @@
mRemoteCallback = cameraClient;
- cameraService->setCameraBusy(cameraId);
cameraService->loadSound();
LOG1("Client::Client X (pid %d, id %d)", callingPid, cameraId);
@@ -1587,7 +1526,7 @@
int cameraId, int cameraFacing,
int clientPid, uid_t clientUid,
int servicePid):
- mClientPackageName(clientPackageName)
+ mClientPackageName(clientPackageName), mDisconnected(false)
{
mCameraService = cameraService;
mRemoteBinder = remoteCallback;
@@ -1606,14 +1545,34 @@
}
void CameraService::BasicClient::disconnect() {
- ALOGV("BasicClient::disconnect");
- mCameraService->removeClientByRemote(mRemoteBinder);
+ if (mDisconnected) return;
+ mDisconnected = true;;
+
+ mCameraService->removeByClient(this);
+ mCameraService->logDisconnected(String8::format("%d", mCameraId), mClientPid,
+ String8(mClientPackageName));
+
+ sp<IBinder> remote = getRemote();
+ if (remote != nullptr) {
+ remote->unlinkToDeath(mCameraService);
+ }
finishCameraOps();
+ ALOGI("%s: Disconnected client for camera %d for PID %d", __FUNCTION__, mCameraId, mClientPid);
+
// client shouldn't be able to call into us anymore
mClientPid = 0;
}
+String16 CameraService::BasicClient::getPackageName() const {
+ return mClientPackageName;
+}
+
+
+int CameraService::BasicClient::getClientPid() const {
+ return mClientPid;
+}
+
status_t CameraService::BasicClient::startCameraOps() {
int32_t res;
// Notify app ops that the camera is not available
@@ -1639,7 +1598,7 @@
// Transition device availability listeners from PRESENT -> NOT_AVAILABLE
mCameraService->updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE,
- mCameraId);
+ String8::format("%d", mCameraId));
return OK;
}
@@ -1652,18 +1611,12 @@
mClientPackageName);
mOpsActive = false;
- // Notify device availability listeners that this camera is available
- // again
+ auto rejected = {ICameraServiceListener::STATUS_NOT_PRESENT,
+ ICameraServiceListener::STATUS_ENUMERATING};
- StatusVector rejectSourceStates;
- rejectSourceStates.push_back(ICameraServiceListener::STATUS_NOT_PRESENT);
- rejectSourceStates.push_back(ICameraServiceListener::STATUS_ENUMERATING);
-
- // Transition to PRESENT if the camera is not in either of above 2
- // states
+ // Transition to PRESENT if the camera is not in either of the rejected states
mCameraService->updateStatus(ICameraServiceListener::STATUS_PRESENT,
- mCameraId,
- &rejectSourceStates);
+ String8::format("%d", mCameraId), rejected);
// Notify flashlight that a camera device is closed.
mCameraService->mFlashlight->deviceClosed(
@@ -1710,26 +1663,15 @@
// ----------------------------------------------------------------------------
-Mutex* CameraService::Client::getClientLockFromCookie(void* user) {
- return gCameraService->getClientLockById((int)(intptr_t) user);
-}
-
-// Provide client pointer for callbacks. Client lock returned from getClientLockFromCookie should
-// be acquired for this to be safe
-CameraService::Client* CameraService::Client::getClientFromCookie(void* user) {
- BasicClient *basicClient = gCameraService->getClientByIdUnsafe((int)(intptr_t) user);
- // OK: only CameraClient calls this, and they already cast anyway.
- Client* client = static_cast<Client*>(basicClient);
-
- // This could happen if the Client is in the process of shutting down (the
- // last strong reference is gone, but the destructor hasn't finished
- // stopping the hardware).
- if (client == NULL) return NULL;
-
- // destruction already started, so should not be accessed
- if (client->mDestructionStarted) return NULL;
-
- return client;
+// Provide client strong pointer for callbacks.
+sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user) {
+ String8 cameraId = String8::format("%d", (int)(intptr_t) user);
+ auto clientDescriptor = gCameraService->mActiveClientManager.get(cameraId);
+ if (clientDescriptor != nullptr) {
+ return sp<Client>{
+ static_cast<Client*>(clientDescriptor->getValue().get())};
+ }
+ return sp<Client>{nullptr};
}
void CameraService::Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
@@ -1741,7 +1683,6 @@
void CameraService::Client::disconnect() {
ALOGV("Client::disconnect");
BasicClient::disconnect();
- mCameraService->setCameraFree(mCameraId);
}
CameraService::Client::OpsCallback::OpsCallback(wp<BasicClient> client):
@@ -1757,30 +1698,101 @@
}
// ----------------------------------------------------------------------------
-// IProCamera
+// CameraState
// ----------------------------------------------------------------------------
-CameraService::ProClient::ProClient(const sp<CameraService>& cameraService,
- const sp<IProCameraCallbacks>& remoteCallback,
- const String16& clientPackageName,
- int cameraId,
- int cameraFacing,
- int clientPid,
- uid_t clientUid,
- int servicePid)
- : CameraService::BasicClient(cameraService, IInterface::asBinder(remoteCallback),
- clientPackageName, cameraId, cameraFacing,
- clientPid, clientUid, servicePid)
-{
- mRemoteCallback = remoteCallback;
+CameraService::CameraState::CameraState(const String8& id, int cost,
+ const std::set<String8>& conflicting) : mId(id),
+ mStatus(ICameraServiceListener::STATUS_PRESENT), mCost(cost), mConflicting(conflicting) {}
+
+CameraService::CameraState::~CameraState() {}
+
+ICameraServiceListener::Status CameraService::CameraState::getStatus() const {
+ Mutex::Autolock lock(mStatusLock);
+ return mStatus;
}
-CameraService::ProClient::~ProClient() {
+CameraParameters CameraService::CameraState::getShimParams() const {
+ return mShimParams;
}
-void CameraService::ProClient::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
- const CaptureResultExtras& resultExtras) {
- mRemoteCallback->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0);
+void CameraService::CameraState::setShimParams(const CameraParameters& params) {
+ mShimParams = params;
+}
+
+int CameraService::CameraState::getCost() const {
+ return mCost;
+}
+
+std::set<String8> CameraService::CameraState::getConflicting() const {
+ return mConflicting;
+}
+
+String8 CameraService::CameraState::getId() const {
+ return mId;
+}
+
+// ----------------------------------------------------------------------------
+// CameraClientManager
+// ----------------------------------------------------------------------------
+
+CameraService::CameraClientManager::~CameraClientManager() {}
+
+sp<CameraService::BasicClient> CameraService::CameraClientManager::getCameraClient(
+ const String8& id) const {
+ auto descriptor = get(id);
+ if (descriptor == nullptr) {
+ return sp<BasicClient>{nullptr};
+ }
+ return descriptor->getValue();
+}
+
+String8 CameraService::CameraClientManager::toString() const {
+ auto all = getAll();
+ String8 ret("[");
+ bool hasAny = false;
+ for (auto& i : all) {
+ hasAny = true;
+ String8 key = i->getKey();
+ int32_t cost = i->getCost();
+ int32_t pid = i->getOwnerId();
+ int32_t priority = i->getPriority();
+ auto conflicting = i->getConflicting();
+ auto clientSp = i->getValue();
+ String8 packageName;
+ if (clientSp.get() != nullptr) {
+ packageName = String8{clientSp->getPackageName()};
+ }
+ ret.appendFormat("\n(Camera ID: %s, Cost: %" PRId32 ", PID: %" PRId32 ", Priority: %"
+ PRId32 ", ", key.string(), cost, pid, priority);
+
+ if (packageName.size() != 0) {
+ ret.appendFormat("Client Package Name: %s", packageName.string());
+ }
+
+ ret.append(", Conflicting Client Devices: {");
+ for (auto& j : conflicting) {
+ ret.appendFormat("%s, ", j.string());
+ }
+ ret.append("})");
+ }
+ if (hasAny) ret.append("\n");
+ ret.append("]\n");
+ return ret;
+}
+
+CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor(
+ const String8& key, const sp<BasicClient>& value, int32_t cost,
+ const std::set<String8>& conflictingKeys, int32_t priority, int32_t ownerId) {
+
+ return std::make_shared<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>(
+ key, value, cost, conflictingKeys, priority, ownerId);
+}
+
+CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor(
+ const sp<BasicClient>& value, const CameraService::DescriptorPtr& partial) {
+ return makeClientDescriptor(partial->getKey(), value, partial->getCost(),
+ partial->getConflicting(), partial->getPriority(), partial->getOwnerId());
}
// ----------------------------------------------------------------------------
@@ -1826,11 +1838,14 @@
}
const hw_module_t* common = mModule->getRawModule();
- result = String8::format("Camera module HAL API version: 0x%x\n", common->hal_api_version);
- result.appendFormat("Camera module API version: 0x%x\n", common->module_api_version);
+ result = String8::format("Camera module HAL API version: %#x\n", common->hal_api_version);
+ result.appendFormat("Camera module API version: %#x\n", common->module_api_version);
result.appendFormat("Camera module name: %s\n", common->name);
result.appendFormat("Camera module author: %s\n", common->author);
- result.appendFormat("Number of camera devices: %d\n\n", mNumberOfCameras);
+ result.appendFormat("Number of camera devices: %d\n", mNumberOfCameras);
+ String8 activeClientString = mActiveClientManager.toString();
+ result.appendFormat("Active Camera Clients:\n%s", activeClientString.string());
+
sp<VendorTagDescriptor> desc = VendorTagDescriptor::getGlobalVendorTagDescriptor();
if (desc == NULL) {
@@ -1845,11 +1860,31 @@
desc->dump(fd, /*verbosity*/2, /*indentation*/4);
}
- for (int i = 0; i < mNumberOfCameras; i++) {
- result = String8::format("Camera %d static information:\n", i);
+ result = String8("Prior client events (most recent at top):\n");
+
+ for (const auto& msg : mEventLog) {
+ result.appendFormat("%s\n", msg.string());
+ }
+
+ if (mEventLog.size() == DEFAULT_EVICTION_LOG_LENGTH) {
+ result.append("...\n");
+ }
+
+ write(fd, result.string(), result.size());
+
+ bool stateLocked = tryLock(mCameraStatesLock);
+ if (!stateLocked) {
+ result = String8::format("CameraStates in use, may be deadlocked\n");
+ write(fd, result.string(), result.size());
+ }
+
+ for (auto& state : mCameraStates) {
+ String8 cameraId = state.first;
+ result = String8::format("Camera %s information:\n", cameraId.string());
camera_info info;
- status_t rc = mModule->getCameraInfo(i, &info);
+ // TODO: Change getCameraInfo + HAL to use String cameraIds
+ status_t rc = mModule->getCameraInfo(cameraIdToInt(cameraId), &info);
if (rc != OK) {
result.appendFormat(" Error reading static information!\n");
write(fd, result.string(), result.size());
@@ -1863,7 +1898,19 @@
} else {
deviceVersion = info.device_version;
}
- result.appendFormat(" Device version: 0x%x\n", deviceVersion);
+
+ auto conflicting = state.second->getConflicting();
+ result.appendFormat(" Resource Cost: %d\n", state.second->getCost());
+ result.appendFormat(" Conflicting Devices:");
+ for (auto& id : conflicting) {
+ result.appendFormat(" %s", cameraId.string());
+ }
+ if (conflicting.size() == 0) {
+ result.appendFormat(" NONE");
+ }
+ result.appendFormat("\n");
+
+ result.appendFormat(" Device version: %#x\n", deviceVersion);
if (deviceVersion >= CAMERA_DEVICE_API_VERSION_2_0) {
result.appendFormat(" Device static metadata:\n");
write(fd, result.string(), result.size());
@@ -1872,19 +1919,38 @@
} else {
write(fd, result.string(), result.size());
}
+
+ CameraParameters p = state.second->getShimParams();
+ if (!p.isEmpty()) {
+ result = String8::format(" Camera1 API shim is using parameters:\n ");
+ write(fd, result.string(), result.size());
+ p.dump(fd, args);
+ }
}
- sp<BasicClient> client = mClient[i].promote();
- if (client == 0) {
- result = String8::format(" Device is closed, no client instance\n");
+ auto clientDescriptor = mActiveClientManager.get(cameraId);
+ if (clientDescriptor == nullptr) {
+ result = String8::format(" Device %s is closed, no client instance\n",
+ cameraId.string());
write(fd, result.string(), result.size());
continue;
}
hasClient = true;
- result = String8::format(" Device is open. Client instance dump:\n");
+ result = String8::format(" Device %s is open. Client instance dump:\n\n",
+ cameraId.string());
+ result.appendFormat("Client priority level: %d\n", clientDescriptor->getPriority());
+ result.appendFormat("Client PID: %d\n", clientDescriptor->getOwnerId());
+
+ auto client = clientDescriptor->getValue();
+ result.appendFormat("Client package: %s\n",
+ String8(client->getPackageName()).string());
write(fd, result.string(), result.size());
+
client->dump(fd, args);
}
+
+ if (stateLocked) mCameraStatesLock.unlock();
+
if (!hasClient) {
result = String8::format("\nNo active camera clients yet.\n");
write(fd, result.string(), result.size());
@@ -1908,7 +1974,6 @@
write(fd, result.string(), result.size());
}
}
-
}
return NO_ERROR;
}
@@ -1931,124 +1996,68 @@
}
}
-/*virtual*/void CameraService::binderDied(
- const wp<IBinder> &who) {
+/*virtual*/void CameraService::binderDied(const wp<IBinder> &who) {
/**
* While tempting to promote the wp<IBinder> into a sp,
* it's actually not supported by the binder driver
*/
- ALOGV("java clients' binder died");
-
// check torch client
handleTorchClientBinderDied(who);
// check camera device client
- sp<BasicClient> cameraClient = getClientByRemote(who);
-
- if (cameraClient == 0) {
- ALOGV("java clients' binder death already cleaned up (normal case)");
+ if(!evictClientIdByRemote(who)) {
+ ALOGV("%s: Java client's binder death already cleaned up (normal case)", __FUNCTION__);
return;
}
- ALOGW("Disconnecting camera client %p since the binder for it "
- "died (this pid %d)", cameraClient.get(), getCallingPid());
-
- cameraClient->disconnect();
-
+ ALOGE("%s: Java client's binder died, removing it from the list of active clients",
+ __FUNCTION__);
}
-void CameraService::updateStatus(ICameraServiceListener::Status status,
- int32_t cameraId,
- const StatusVector *rejectSourceStates) {
- // do not lock mServiceLock here or can get into a deadlock from
- // connect() -> ProClient::disconnect -> updateStatus
- Mutex::Autolock lock(mStatusMutex);
-
- ICameraServiceListener::Status oldStatus = mStatusList[cameraId];
-
- mStatusList[cameraId] = status;
-
- if (oldStatus != status) {
- ALOGV("%s: Status has changed for camera ID %d from 0x%x to 0x%x",
- __FUNCTION__, cameraId, (uint32_t)oldStatus, (uint32_t)status);
-
- if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT &&
- (status != ICameraServiceListener::STATUS_PRESENT &&
- status != ICameraServiceListener::STATUS_ENUMERATING)) {
-
- ALOGW("%s: From NOT_PRESENT can only transition into PRESENT"
- " or ENUMERATING", __FUNCTION__);
- mStatusList[cameraId] = oldStatus;
- return;
- }
-
- if (rejectSourceStates != NULL) {
- const StatusVector &rejectList = *rejectSourceStates;
- StatusVector::const_iterator it = rejectList.begin();
-
- /**
- * Sometimes we want to conditionally do a transition.
- * For example if a client disconnects, we want to go to PRESENT
- * only if we weren't already in NOT_PRESENT or ENUMERATING.
- */
- for (; it != rejectList.end(); ++it) {
- if (oldStatus == *it) {
- ALOGV("%s: Rejecting status transition for Camera ID %d, "
- " since the source state was was in one of the bad "
- " states.", __FUNCTION__, cameraId);
- mStatusList[cameraId] = oldStatus;
- return;
- }
- }
- }
-
- /**
- * ProClients lose their exclusive lock.
- * - Done before the CameraClient can initialize the HAL device,
- * since we want to be able to close it before they get to initialize
- */
- if (status == ICameraServiceListener::STATUS_NOT_AVAILABLE) {
- Vector<wp<ProClient> > proClients(mProClientList[cameraId]);
- Vector<wp<ProClient> >::const_iterator it;
-
- for (it = proClients.begin(); it != proClients.end(); ++it) {
- sp<ProClient> proCl = it->promote();
- if (proCl.get() != NULL) {
- proCl->onExclusiveLockStolen();
- }
- }
- }
-
- if (status == ICameraServiceListener::STATUS_NOT_PRESENT ||
- status == ICameraServiceListener::STATUS_NOT_AVAILABLE) {
- // update torch status to not available when the camera device
- // becomes not present or not available.
- onTorchStatusChanged(String8::format("%d", cameraId),
- ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE);
- } else if (status == ICameraServiceListener::STATUS_PRESENT) {
- // update torch status to available when the camera device becomes
- // present or available
- onTorchStatusChanged(String8::format("%d", cameraId),
- ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
- }
-
- Vector<sp<ICameraServiceListener> >::const_iterator it;
- for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
- (*it)->onStatusChanged(status, cameraId);
- }
- }
+void CameraService::updateStatus(ICameraServiceListener::Status status, const String8& cameraId) {
+ updateStatus(status, cameraId, {});
}
-ICameraServiceListener::Status CameraService::getStatus(int cameraId) const {
- if (cameraId < 0 || cameraId >= MAX_CAMERAS) {
- ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId);
- return ICameraServiceListener::STATUS_UNKNOWN;
+void CameraService::updateStatus(ICameraServiceListener::Status status, const String8& cameraId,
+ std::initializer_list<ICameraServiceListener::Status> rejectSourceStates) {
+ // Do not lock mServiceLock here or can get into a deadlock from
+ // connect() -> disconnect -> updateStatus
+
+ auto state = getCameraState(cameraId);
+
+ if (state == nullptr) {
+ ALOGW("%s: Could not update the status for %s, no such device exists", __FUNCTION__,
+ cameraId.string());
+ return;
}
- Mutex::Autolock al(mStatusMutex);
- return mStatusList[cameraId];
+ // Update the status for this camera state, then send the onStatusChangedCallbacks to each
+ // of the listeners with both the mStatusStatus and mStatusListenerLock held
+ state->updateStatus(status, cameraId, rejectSourceStates, [this]
+ (const String8& cameraId, ICameraServiceListener::Status status) {
+
+ // Update torch status
+ if (status == ICameraServiceListener::STATUS_NOT_PRESENT ||
+ status == ICameraServiceListener::STATUS_NOT_AVAILABLE) {
+ // Update torch status to not available when the camera device becomes not present
+ // or not available.
+ onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE);
+ } else if (status == ICameraServiceListener::STATUS_PRESENT) {
+ // Update torch status to available when the camera device becomes present or
+ // available
+ onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
+ }
+
+ Mutex::Autolock lock(mStatusListenerLock);
+
+ for (auto& listener : mListenerList) {
+ // TODO: Refactor status listeners to use strings for Camera IDs and remove this.
+ int id = cameraIdToInt(cameraId);
+ if (id != -1) listener->onStatusChanged(status, id);
+ }
+ });
}
status_t CameraService::getTorchStatusLocked(
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 22afc8c..53f1c72 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -27,8 +27,6 @@
#include <camera/ICamera.h>
#include <camera/ICameraClient.h>
-#include <camera/IProCameraUser.h>
-#include <camera/IProCameraCallbacks.h>
#include <camera/camera2/ICameraDeviceUser.h>
#include <camera/camera2/ICameraDeviceCallbacks.h>
#include <camera/VendorTagDescriptor.h>
@@ -38,11 +36,15 @@
#include <camera/ICameraServiceListener.h>
#include "CameraFlashlight.h"
-
#include "common/CameraModule.h"
+#include "utils/AutoConditionLock.h"
+#include "utils/ClientManager.h"
+#include "utils/RingBuffer.h"
-/* This needs to be increased if we can have more cameras */
-#define MAX_CAMERAS 2
+#include <set>
+#include <string>
+#include <map>
+#include <memory>
namespace android {
@@ -62,6 +64,34 @@
class Client;
class BasicClient;
+ enum apiLevel {
+ API_1 = 1,
+ API_2 = 2
+ };
+
+ // Process States (mirrors frameworks/base/core/java/android/app/ActivityManager.java)
+ static const int PROCESS_STATE_NONEXISTENT = -1;
+ static const int PROCESS_STATE_PERSISTENT = 0;
+ static const int PROCESS_STATE_PERSISTENT_UI = 1;
+ static const int PROCESS_STATE_TOP = 2;
+ static const int PROCESS_STATE_IMPORTANT_FOREGROUND = 3;
+ static const int PROCESS_STATE_IMPORTANT_BACKGROUND = 4;
+ static const int PROCESS_STATE_BACKUP = 5;
+ static const int PROCESS_STATE_HEAVY_WEIGHT = 6;
+ static const int PROCESS_STATE_SERVICE = 7;
+ static const int PROCESS_STATE_RECEIVER = 8;
+ static const int PROCESS_STATE_HOME = 9;
+ static const int PROCESS_STATE_LAST_ACTIVITY = 10;
+ static const int PROCESS_STATE_CACHED_ACTIVITY = 11;
+ static const int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12;
+ static const int PROCESS_STATE_CACHED_EMPTY = 13;
+
+ // 3 second busy timeout when other clients are connecting
+ static const nsecs_t DEFAULT_CONNECT_TIMEOUT_NS = 3000000000;
+
+ // Default number of messages to store in eviction log
+ static const size_t DEFAULT_EVICTION_LOG_LENGTH = 50;
+
// Implementation of BinderService<T>
static char const* getServiceName() { return "media.camera"; }
@@ -70,8 +100,8 @@
/////////////////////////////////////////////////////////////////////
// HAL Callbacks
- virtual void onDeviceStatusChanged(int cameraId,
- int newStatus);
+ virtual void onDeviceStatusChanged(camera_device_status_t cameraId,
+ camera_device_status_t newStatus);
virtual void onTorchStatusChanged(const String8& cameraId,
ICameraServiceListener::TorchStatus
newStatus);
@@ -95,11 +125,6 @@
/*out*/
sp<ICamera>& device);
- virtual status_t connectPro(const sp<IProCameraCallbacks>& cameraCb,
- int cameraId, const String16& clientPackageName, int clientUid,
- /*out*/
- sp<IProCameraUser>& device);
-
virtual status_t connectDevice(
const sp<ICameraDeviceCallbacks>& cameraCb,
int cameraId,
@@ -132,7 +157,6 @@
/////////////////////////////////////////////////////////////////////
// Client functionality
- virtual void removeClientByRemote(const wp<IBinder>& remoteBinder);
enum sound_kind {
SOUND_SHUTTER = 0,
@@ -155,11 +179,6 @@
/////////////////////////////////////////////////////////////////////
// CameraClient functionality
- // returns plain pointer of client. Note that mClientLock should be acquired to
- // prevent the client from destruction. The result can be NULL.
- virtual BasicClient* getClientByIdUnsafe(int cameraId);
- virtual Mutex* getClientLockById(int cameraId);
-
class BasicClient : public virtual RefBase {
public:
virtual status_t initialize(CameraModule *module) = 0;
@@ -169,13 +188,22 @@
// virtual inheritance
virtual sp<IBinder> asBinderWrapper() = 0;
- // Return the remote callback binder object (e.g. IProCameraCallbacks)
+ // Return the remote callback binder object (e.g. ICameraDeviceCallbacks)
sp<IBinder> getRemote() {
return mRemoteBinder;
}
virtual status_t dump(int fd, const Vector<String16>& args) = 0;
+ // Return the package name for this client
+ virtual String16 getPackageName() const;
+
+ // Notify client about a fatal error
+ virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
+ const CaptureResultExtras& resultExtras) = 0;
+
+ // Get the PID of the application client using this
+ virtual int getClientPid() const;
protected:
BasicClient(const sp<CameraService>& cameraService,
const sp<IBinder>& remoteCallback,
@@ -202,6 +230,7 @@
pid_t mClientPid;
uid_t mClientUid; // immutable after constructor
pid_t mServicePid; // immutable after constructor
+ bool mDisconnected;
// - The app-side Binder interface to receive callbacks from us
sp<IBinder> mRemoteBinder; // immutable after constructor
@@ -210,10 +239,6 @@
status_t startCameraOps();
status_t finishCameraOps();
- // Notify client about a fatal error
- virtual void notifyError(
- ICameraDeviceCallbacks::CameraErrorCode errorCode,
- const CaptureResultExtras& resultExtras) = 0;
private:
AppOpsManager mAppOpsManager;
@@ -285,13 +310,11 @@
return asBinder(this);
}
- protected:
- static Mutex* getClientLockFromCookie(void* user);
- // convert client from cookie. Client lock should be acquired before getting Client.
- static Client* getClientFromCookie(void* user);
-
virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
const CaptureResultExtras& resultExtras);
+ protected:
+ // Convert client from cookie.
+ static sp<CameraService::Client> getClientFromCookie(void* user);
// Initialized in constructor
@@ -300,93 +323,224 @@
}; // class Client
- class ProClient : public BnProCameraUser, public BasicClient {
+ typedef std::shared_ptr<resource_policy::ClientDescriptor<String8,
+ sp<CameraService::BasicClient>>> DescriptorPtr;
+
+ /**
+ * A container class for managing active camera clients that are using HAL devices. Active
+ * clients are represented by ClientDescriptor objects that contain strong pointers to the
+ * actual BasicClient subclass binder interface implementation.
+ *
+ * This class manages the eviction behavior for the camera clients. See the parent class
+ * implementation in utils/ClientManager for the specifics of this behavior.
+ */
+ class CameraClientManager :
+ public resource_policy::ClientManager<String8, sp<CameraService::BasicClient>> {
public:
- typedef IProCameraCallbacks TCamCallbacks;
+ virtual ~CameraClientManager();
- ProClient(const sp<CameraService>& cameraService,
- const sp<IProCameraCallbacks>& remoteCallback,
- const String16& clientPackageName,
- int cameraId,
- int cameraFacing,
- int clientPid,
- uid_t clientUid,
- int servicePid);
+ /**
+ * Return a strong pointer to the active BasicClient for this camera ID, or an empty
+ * if none exists.
+ */
+ sp<CameraService::BasicClient> getCameraClient(const String8& id) const;
- virtual ~ProClient();
+ /**
+ * Return a string describing the current state.
+ */
+ String8 toString() const;
- const sp<IProCameraCallbacks>& getRemoteCallback() {
- return mRemoteCallback;
- }
+ /**
+ * Make a ClientDescriptor object wrapping the given BasicClient strong pointer.
+ */
+ static DescriptorPtr makeClientDescriptor(const String8& key, const sp<BasicClient>& value,
+ int32_t cost, const std::set<String8>& conflictingKeys, int32_t priority,
+ int32_t ownerId);
- /***
- IProCamera implementation
- ***/
- virtual status_t connect(const sp<IProCameraCallbacks>& callbacks)
- = 0;
- virtual status_t exclusiveTryLock() = 0;
- virtual status_t exclusiveLock() = 0;
- virtual status_t exclusiveUnlock() = 0;
+ /**
+ * Make a ClientDescriptor object wrapping the given BasicClient strong pointer with
+ * values intialized from a prior ClientDescriptor.
+ */
+ static DescriptorPtr makeClientDescriptor(const sp<BasicClient>& value,
+ const CameraService::DescriptorPtr& partial);
- virtual bool hasExclusiveLock() = 0;
-
- // Note that the callee gets a copy of the metadata.
- virtual int submitRequest(camera_metadata_t* metadata,
- bool streaming = false) = 0;
- virtual status_t cancelRequest(int requestId) = 0;
-
- // Callbacks from camera service
- virtual void onExclusiveLockStolen() = 0;
-
- protected:
- virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
- const CaptureResultExtras& resultExtras);
-
- sp<IProCameraCallbacks> mRemoteCallback;
- }; // class ProClient
+ }; // class CameraClientManager
private:
+ /**
+ * Container class for the state of each logical camera device, including: ID, status, and
+ * dependencies on other devices. The mapping of camera ID -> state saved in mCameraStates
+ * represents the camera devices advertised by the HAL (and any USB devices, when we add
+ * those).
+ *
+ * This container does NOT represent an active camera client. These are represented using
+ * the ClientDescriptors stored in mActiveClientManager.
+ */
+ class CameraState {
+ public:
+ /**
+ * Make a new CameraState and set the ID, cost, and conflicting devices using the values
+ * returned in the HAL's camera_info struct for each device.
+ */
+ CameraState(const String8& id, int cost, const std::set<String8>& conflicting);
+ virtual ~CameraState();
+
+ /**
+ * Return the status for this device.
+ *
+ * This method acquires mStatusLock.
+ */
+ ICameraServiceListener::Status getStatus() const;
+
+ /**
+ * This function updates the status for this camera device, unless the given status
+ * is in the given list of rejected status states, and execute the function passed in
+ * with a signature onStatusUpdateLocked(const String8&, ICameraServiceListener::Status)
+ * if the status has changed.
+ *
+ * This method is idempotent, and will not result in the function passed to
+ * onStatusUpdateLocked being called more than once for the same arguments.
+ * This method aquires mStatusLock.
+ */
+ template<class Func>
+ void updateStatus(ICameraServiceListener::Status status, const String8& cameraId,
+ std::initializer_list<ICameraServiceListener::Status> rejectSourceStates,
+ Func onStatusUpdatedLocked);
+
+ /**
+ * Return the last set CameraParameters object generated from the information returned by
+ * the HAL for this device (or an empty CameraParameters object if none has been set).
+ */
+ CameraParameters getShimParams() const;
+
+ /**
+ * Set the CameraParameters for this device.
+ */
+ void setShimParams(const CameraParameters& params);
+
+ /**
+ * Return the resource_cost advertised by the HAL for this device.
+ */
+ int getCost() const;
+
+ /**
+ * Return a set of the IDs of conflicting devices advertised by the HAL for this device.
+ */
+ std::set<String8> getConflicting() const;
+
+ /**
+ * Return the ID of this camera device.
+ */
+ String8 getId() const;
+
+ private:
+ const String8 mId;
+ ICameraServiceListener::Status mStatus; // protected by mStatusLock
+ const int mCost;
+ std::set<String8> mConflicting;
+ mutable Mutex mStatusLock;
+ CameraParameters mShimParams;
+ }; // class CameraState
+
// Delay-load the Camera HAL module
virtual void onFirstRef();
- // Step 1. Check if we can connect, before we acquire the service lock.
- status_t validateConnect(int cameraId,
- /*inout*/
- int& clientUid) const;
+ // Check if we can connect, before we acquire the service lock.
+ status_t validateConnect(const String8& cameraId, /*inout*/int& clientUid) const;
- // Step 2. Check if we can connect, after we acquire the service lock.
- bool canConnectUnsafe(int cameraId,
- const String16& clientPackageName,
- const sp<IBinder>& remoteCallback,
- /*out*/
- sp<BasicClient> &client);
+ // Handle active client evictions, and update service state.
+ // Only call with with mServiceLock held.
+ status_t handleEvictionsLocked(const String8& cameraId, int clientPid,
+ apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName,
+ /*out*/
+ sp<BasicClient>* client,
+ std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial);
- // When connection is successful, initialize client and track its death
- status_t connectFinishUnsafe(const sp<BasicClient>& client,
- const sp<IBinder>& remoteCallback);
+ // Single implementation shared between the various connect calls
+ template<class CALLBACK, class CLIENT>
+ status_t connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId, int halVersion,
+ const String16& clientPackageName, int clientUid, apiLevel effectiveApiLevel,
+ bool legacyMode, bool shimUpdateOnly, /*out*/sp<CLIENT>& device);
- virtual sp<BasicClient> getClientByRemote(const wp<IBinder>& cameraClient);
+ // Lock guarding camera service state
Mutex mServiceLock;
- // either a Client or CameraDeviceClient
- wp<BasicClient> mClient[MAX_CAMERAS]; // protected by mServiceLock
- Mutex mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks
+
+ // Condition to use with mServiceLock, used to handle simultaneous connect calls from clients
+ std::shared_ptr<WaitableMutexWrapper> mServiceLockWrapper;
+
+ // Return NO_ERROR if the device with a give ID can be connected to
+ status_t checkIfDeviceIsUsable(const String8& cameraId) const;
+
+ // Container for managing currently active application-layer clients
+ CameraClientManager mActiveClientManager;
+
+ // Mapping from camera ID -> state for each device, map is protected by mCameraStatesLock
+ std::map<String8, std::shared_ptr<CameraState>> mCameraStates;
+
+ // Mutex guarding mCameraStates map
+ mutable Mutex mCameraStatesLock;
+
+ // Circular buffer for storing event logging for dumps
+ RingBuffer<String8> mEventLog;
+
+ /**
+ * Get the camera state for a given camera id.
+ *
+ * This acquires mCameraStatesLock.
+ */
+ std::shared_ptr<CameraService::CameraState> getCameraState(const String8& cameraId) const;
+
+ /**
+ * Evict client who's remote binder has died. Returns true if this client was in the active
+ * list and was disconnected.
+ *
+ * This method acquires mServiceLock.
+ */
+ bool evictClientIdByRemote(const wp<IBinder>& cameraClient);
+
+ /**
+ * Remove the given client from the active clients list; does not disconnect the client.
+ *
+ * This method acquires mServiceLock.
+ */
+ void removeByClient(const BasicClient* client);
+
+ /**
+ * Add new client to active clients list after conflicting clients have disconnected using the
+ * values set in the partial descriptor passed in to construct the actual client descriptor.
+ * This is typically called at the end of a connect call.
+ *
+ * This method must be called with mServiceLock held.
+ */
+ void finishConnectLocked(const sp<BasicClient>& client, const DescriptorPtr& desc);
+
+ /**
+ * Returns the integer corresponding to the given camera ID string, or -1 on failure.
+ */
+ static int cameraIdToInt(const String8& cameraId);
+
+ /**
+ * Remove a single client corresponding to the given camera id from the list of active clients.
+ * If none exists, return an empty strongpointer.
+ *
+ * This method must be called with mServiceLock held.
+ */
+ sp<CameraService::BasicClient> removeClientLocked(const String8& cameraId);
+
+ /**
+ * Add a event log message that a client has been disconnected.
+ */
+ void logDisconnected(const String8& cameraId, int clientPid, const String8& clientPackage);
+
+ /**
+ * Add a event log message that a client has been connected.
+ */
+ void logConnected(const String8& cameraId, int clientPid, const String8& clientPackage);
+
int mNumberOfCameras;
- typedef wp<ProClient> weak_pro_client_ptr;
- Vector<weak_pro_client_ptr> mProClientList[MAX_CAMERAS];
-
- // needs to be called with mServiceLock held
- sp<BasicClient> findClientUnsafe(const wp<IBinder>& cameraClient, int& outIndex);
- sp<ProClient> findProClientUnsafe(
- const wp<IBinder>& cameraCallbacksRemote);
-
- // atomics to record whether the hardware is allocated to some client.
- volatile int32_t mBusy[MAX_CAMERAS];
- void setCameraBusy(int cameraId);
- void setCameraFree(int cameraId);
-
// sounds
MediaPlayer* newMediaPlayer(const char *file);
@@ -396,24 +550,21 @@
CameraModule* mModule;
- Vector<sp<ICameraServiceListener> >
- mListenerList;
+ // Guarded by mStatusListenerMutex
+ std::vector<sp<ICameraServiceListener>> mListenerList;
+ Mutex mStatusListenerLock;
- // guard only mStatusList and the broadcasting of ICameraServiceListener
- mutable Mutex mStatusMutex;
- ICameraServiceListener::Status
- mStatusList[MAX_CAMERAS];
-
- // Read the current status (locks mStatusMutex)
- ICameraServiceListener::Status
- getStatus(int cameraId) const;
-
- typedef Vector<ICameraServiceListener::Status> StatusVector;
- // Broadcast the new status if it changed (locks the service mutex)
- void updateStatus(
- ICameraServiceListener::Status status,
- int32_t cameraId,
- const StatusVector *rejectSourceStates = NULL);
+ /**
+ * Update the status for the given camera id (if that device exists), and broadcast the
+ * status update to all current ICameraServiceListeners if the status has changed. Any
+ * statuses in rejectedSourceStates will be ignored.
+ *
+ * This method must be idempotent.
+ * This method acquires mStatusLock and mStatusListenerLock.
+ */
+ void updateStatus(ICameraServiceListener::Status status, const String8& cameraId,
+ std::initializer_list<ICameraServiceListener::Status> rejectedSourceStates);
+ void updateStatus(ICameraServiceListener::Status status, const String8& cameraId);
// flashlight control
sp<CameraFlashlight> mFlashlight;
@@ -435,9 +586,6 @@
void onTorchStatusChangedLocked(const String8& cameraId,
ICameraServiceListener::TorchStatus newStatus);
- // validate the camera id for use of setting a torch mode.
- bool validCameraIdForSetTorchMode(const String8& cameraId);
-
// get a camera's torch status. mTorchStatusMutex should be locked.
status_t getTorchStatusLocked(const String8 &cameraId,
ICameraServiceListener::TorchStatus *status) const;
@@ -451,19 +599,9 @@
// Helpers
- bool isValidCameraId(int cameraId);
-
bool setUpVendorTags();
/**
- * A mapping of camera ids to CameraParameters returned by that camera device.
- *
- * This cache is used to generate CameraCharacteristic metadata when using
- * the HAL1 shim.
- */
- KeyedVector<int, CameraParameters> mShimParams;
-
- /**
* Initialize and cache the metadata used by the HAL1 shim for a given cameraId.
*
* Returns OK on success, or a negative error code.
@@ -486,25 +624,190 @@
*/
status_t generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo);
+ static int getCallingPid();
+
+ static int getCallingUid();
+
/**
- * Connect a new camera client. This should only be used while holding the
- * mutex for mServiceLock.
- *
- * Returns OK on success, or a negative error code.
+ * Get the current system time as a formatted string.
*/
- status_t connectHelperLocked(
- /*out*/
- sp<Client>& client,
- /*in*/
- const sp<ICameraClient>& cameraClient,
- int cameraId,
- const String16& clientPackageName,
- int clientUid,
- int callingPid,
- int halVersion = CAMERA_HAL_API_VERSION_UNSPECIFIED,
- bool legacyMode = false);
+ static String8 getFormattedCurrentTime();
+
+ /**
+ * Get the camera eviction priority from the current process state given by ActivityManager.
+ */
+ static int getCameraPriorityFromProcState(int procState);
+
+ static status_t 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);
};
+template<class Func>
+void CameraService::CameraState::updateStatus(ICameraServiceListener::Status status,
+ const String8& cameraId,
+ std::initializer_list<ICameraServiceListener::Status> rejectSourceStates,
+ Func onStatusUpdatedLocked) {
+ Mutex::Autolock lock(mStatusLock);
+ ICameraServiceListener::Status oldStatus = mStatus;
+ mStatus = status;
+
+ if (oldStatus == status) {
+ return;
+ }
+
+ ALOGV("%s: Status has changed for camera ID %s from %#x to %#x", __FUNCTION__,
+ cameraId.string(), oldStatus, status);
+
+ if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT &&
+ (status != ICameraServiceListener::STATUS_PRESENT &&
+ status != ICameraServiceListener::STATUS_ENUMERATING)) {
+
+ ALOGW("%s: From NOT_PRESENT can only transition into PRESENT or ENUMERATING",
+ __FUNCTION__);
+ mStatus = oldStatus;
+ return;
+ }
+
+ /**
+ * Sometimes we want to conditionally do a transition.
+ * For example if a client disconnects, we want to go to PRESENT
+ * only if we weren't already in NOT_PRESENT or ENUMERATING.
+ */
+ for (auto& rejectStatus : rejectSourceStates) {
+ if (oldStatus == rejectStatus) {
+ ALOGV("%s: Rejecting status transition for Camera ID %s, since the source "
+ "state was was in one of the bad states.", __FUNCTION__, cameraId.string());
+ mStatus = oldStatus;
+ return;
+ }
+ }
+
+ onStatusUpdatedLocked(cameraId, status);
+}
+
+
+template<class CALLBACK, class CLIENT>
+status_t CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
+ int halVersion, const String16& clientPackageName, int clientUid,
+ apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
+ /*out*/sp<CLIENT>& device) {
+ status_t ret = NO_ERROR;
+ String8 clientName8(clientPackageName);
+ int clientPid = getCallingPid();
+
+ ALOGI("CameraService::connect call E (PID %d \"%s\", camera ID %s) for HAL version %d and "
+ "Camera API version %d", clientPid, clientName8.string(), cameraId.string(),
+ halVersion, static_cast<int>(effectiveApiLevel));
+
+ // Enforce client permissions and do basic sanity checks
+ if((ret = validateConnect(cameraId, /*inout*/clientUid)) != NO_ERROR) {
+ return ret;
+ }
+
+ 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 X (PID %d) rejected (too many other clients connecting)."
+ , clientPid);
+ return -EBUSY;
+ }
+
+ // 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 NO_ERROR;
+ }
+ }
+
+ sp<BasicClient> clientTmp = nullptr;
+ std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>> partial;
+ if ((ret = handleEvictionsLocked(cameraId, clientPid, effectiveApiLevel,
+ IInterface::asBinder(cameraCb), clientName8, /*out*/&clientTmp,
+ /*out*/&partial)) != NO_ERROR) {
+ return ret;
+ }
+
+ if (clientTmp.get() != nullptr) {
+ // Handle special case for API1 MediaRecorder where the existing client is returned
+ device = static_cast<CLIENT*>(clientTmp.get());
+ return NO_ERROR;
+ }
+
+ // give flashlight a chance to close devices if necessary.
+ mFlashlight->prepareDeviceOpen(cameraId);
+
+ // TODO: Update getDeviceVersion + HAL interface to use strings for Camera IDs
+ int id = cameraIdToInt(cameraId);
+ if (id == -1) {
+ ALOGE("%s: Invalid camera ID %s, cannot get device version from HAL.", __FUNCTION__,
+ cameraId.string());
+ return BAD_VALUE;
+ }
+
+ int facing = -1;
+ int deviceVersion = getDeviceVersion(id, /*out*/&facing);
+ sp<BasicClient> tmp = nullptr;
+ if((ret = makeClient(this, cameraCb, clientPackageName, cameraId, facing, clientPid,
+ clientUid, getpid(), legacyMode, halVersion, deviceVersion, effectiveApiLevel,
+ /*out*/&tmp)) != NO_ERROR) {
+ return ret;
+ }
+ client = static_cast<CLIENT*>(tmp.get());
+
+ LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",
+ __FUNCTION__);
+
+ if ((ret = client->initialize(mModule)) != OK) {
+ ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__);
+ return ret;
+ }
+
+ sp<IBinder> remoteCallback = client->getRemote();
+ if (remoteCallback != nullptr) {
+ remoteCallback->linkToDeath(this);
+ }
+
+ // 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 NO_ERROR;
+}
+
} // namespace android
#endif
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 5dbdeb2..6f44aee 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -163,11 +163,9 @@
status_t Camera2Client::dump(int fd, const Vector<String16>& args) {
String8 result;
- result.appendFormat("Client2[%d] (%p) Client: %s PID: %d, dump:\n",
- mCameraId,
+ result.appendFormat("Client2[%d] (%p) PID: %d, dump:\n", mCameraId,
(getRemoteCallback() != NULL ?
(IInterface::asBinder(getRemoteCallback()).get()) : NULL),
- String8(mClientPackageName).string(),
mClientPid);
result.append(" State: ");
#define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break;
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index 6bea3b6..e552633 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -99,12 +99,7 @@
// tear down the client
CameraClient::~CameraClient() {
- // this lock should never be NULL
- Mutex* lock = mCameraService->getClientLockById(mCameraId);
- lock->lock();
mDestructionStarted = true;
- // client will not be accessed from callback. should unlock to prevent dead-lock in disconnect
- lock->unlock();
int callingPid = getCallingPid();
LOG1("CameraClient::~CameraClient E (pid %d, this %p)", callingPid, this);
@@ -116,11 +111,11 @@
const size_t SIZE = 256;
char buffer[SIZE];
- size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) PID: %d\n",
+ size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) with UID %d\n",
mCameraId,
(getRemoteCallback() != NULL ?
IInterface::asBinder(getRemoteCallback()).get() : NULL),
- mClientPid);
+ mClientUid);
len = (len > SIZE - 1) ? SIZE - 1 : len;
write(fd, buffer, len);
@@ -677,6 +672,13 @@
LOG1("lockIfMessageWanted(%d): waited for %d ms",
msgType, sleepCount * CHECK_MESSAGE_INTERVAL);
}
+
+ // If messages are no longer enabled after acquiring lock, release and drop message
+ if ((mMsgEnabled & msgType) == 0) {
+ mLock.unlock();
+ break;
+ }
+
return true;
}
if (sleepCount++ == 0) {
@@ -702,26 +704,13 @@
// (others) c->dataCallback
// dataCallbackTimestamp
// (others) c->dataCallbackTimestamp
-//
-// NOTE: the *Callback functions grab mLock of the client before passing
-// control to handle* functions. So the handle* functions must release the
-// lock before calling the ICameraClient's callbacks, so those callbacks can
-// invoke methods in the Client class again (For example, the preview frame
-// callback may want to releaseRecordingFrame). The handle* functions must
-// release the lock after all accesses to member variables, so it must be
-// handled very carefully.
void CameraClient::notifyCallback(int32_t msgType, int32_t ext1,
int32_t ext2, void* user) {
LOG2("notifyCallback(%d)", msgType);
- Mutex* lock = getClientLockFromCookie(user);
- if (lock == NULL) return;
- Mutex::Autolock alock(*lock);
-
- CameraClient* client =
- static_cast<CameraClient*>(getClientFromCookie(user));
- if (client == NULL) return;
+ sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get());
+ if (client.get() == nullptr) return;
if (!client->lockIfMessageWanted(msgType)) return;
@@ -740,13 +729,8 @@
const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) {
LOG2("dataCallback(%d)", msgType);
- Mutex* lock = getClientLockFromCookie(user);
- if (lock == NULL) return;
- Mutex::Autolock alock(*lock);
-
- CameraClient* client =
- static_cast<CameraClient*>(getClientFromCookie(user));
- if (client == NULL) return;
+ sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get());
+ if (client.get() == nullptr) return;
if (!client->lockIfMessageWanted(msgType)) return;
if (dataPtr == 0 && metadata == NULL) {
@@ -778,13 +762,8 @@
int32_t msgType, const sp<IMemory>& dataPtr, void* user) {
LOG2("dataCallbackTimestamp(%d)", msgType);
- Mutex* lock = getClientLockFromCookie(user);
- if (lock == NULL) return;
- Mutex::Autolock alock(*lock);
-
- CameraClient* client =
- static_cast<CameraClient*>(getClientFromCookie(user));
- if (client == NULL) return;
+ sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get());
+ if (client.get() == nullptr) return;
if (!client->lockIfMessageWanted(msgType)) return;
diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
index fd4e714..5c8f750 100644
--- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
@@ -154,8 +154,8 @@
params.previewWidth, params.previewHeight,
callbackFormat, params.previewFormat);
res = device->createStream(mCallbackWindow,
- params.previewWidth, params.previewHeight,
- callbackFormat, HAL_DATASPACE_JFIF, &mCallbackStreamId);
+ params.previewWidth, params.previewHeight, callbackFormat,
+ HAL_DATASPACE_JFIF, CAMERA3_STREAM_ROTATION_0, &mCallbackStreamId);
if (res != OK) {
ALOGE("%s: Camera %d: Can't create output stream for callbacks: "
"%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index 5b387f9..34798bf 100644
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -146,7 +146,7 @@
res = device->createStream(mCaptureWindow,
params.pictureWidth, params.pictureHeight,
HAL_PIXEL_FORMAT_BLOB, HAL_DATASPACE_JFIF,
- &mCaptureStreamId);
+ CAMERA3_STREAM_ROTATION_0, &mCaptureStreamId);
if (res != OK) {
ALOGE("%s: Camera %d: Can't create output stream for capture: "
"%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index a5a2fec..b6071f6 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -183,7 +183,7 @@
res = device->createStream(mPreviewWindow,
params.previewWidth, params.previewHeight,
CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, HAL_DATASPACE_UNKNOWN,
- &mPreviewStreamId);
+ CAMERA3_STREAM_ROTATION_0, &mPreviewStreamId);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
@@ -426,8 +426,8 @@
// TODO: Wire this in from encoder side
res = device->createStream(mRecordingWindow,
params.videoWidth, params.videoHeight,
- CAMERA2_HAL_PIXEL_FORMAT_OPAQUE,
- HAL_DATASPACE_BT709, &mRecordingStreamId);
+ CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, HAL_DATASPACE_BT709,
+ CAMERA3_STREAM_ROTATION_0, &mRecordingStreamId);
if (res != OK) {
ALOGE("%s: Camera %d: Can't create output stream for recording: "
"%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 68aca2d..a03f9c7 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -185,8 +185,8 @@
(int)CAMERA2_HAL_PIXEL_FORMAT_ZSL :
(int)HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
res = device->createStream(mZslWindow,
- params.fastInfo.arrayWidth, params.fastInfo.arrayHeight,
- streamType, HAL_DATASPACE_UNKNOWN, &mZslStreamId);
+ params.fastInfo.arrayWidth, params.fastInfo.arrayHeight, streamType,
+ HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mZslStreamId);
if (res != OK) {
ALOGE("%s: Camera %d: Can't create output stream for ZSL: "
"%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index dde1779..8587e0e 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -314,8 +314,7 @@
return res;
}
-status_t CameraDeviceClient::createStream(
- const sp<IGraphicBufferProducer>& bufferProducer)
+status_t CameraDeviceClient::createStream(const OutputConfiguration &outputConfiguration)
{
ATRACE_CALL();
@@ -324,6 +323,8 @@
Mutex::Autolock icl(mBinderSerializationLock);
+
+ sp<IGraphicBufferProducer> bufferProducer = outputConfiguration.getGraphicBufferProducer();
if (bufferProducer == NULL) {
ALOGE("%s: bufferProducer must not be null", __FUNCTION__);
return BAD_VALUE;
@@ -413,7 +414,9 @@
int streamId = -1;
res = mDevice->createStream(anw, width, height, format, dataSpace,
- &streamId);
+ static_cast<camera3_stream_rotation_t>
+ (outputConfiguration.getRotation()),
+ &streamId);
if (res == OK) {
mStreamMap.add(binder, streamId);
@@ -595,9 +598,7 @@
mCameraId,
(getRemoteCallback() != NULL ?
IInterface::asBinder(getRemoteCallback()).get() : NULL) );
- result.appendFormat(" Current client: %s (PID %d, UID %u)\n",
- String8(mClientPackageName).string(),
- mClientPid, mClientUid);
+ result.appendFormat(" Current client UID %u\n", mClientUid);
result.append(" State:\n");
result.appendFormat(" Request ID counter: %d\n", mRequestIdCounter);
@@ -644,9 +645,6 @@
}
}
-// TODO: refactor the code below this with IProCameraUser.
-// it's 100% copy-pasted, so lets not change it right now to make it easier.
-
void CameraDeviceClient::detachDevice() {
if (mDevice == 0) return;
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index c89c269..a3dbb90 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -19,6 +19,7 @@
#include <camera/camera2/ICameraDeviceUser.h>
#include <camera/camera2/ICameraDeviceCallbacks.h>
+#include <camera/camera2/OutputConfiguration.h>
#include "CameraService.h"
#include "common/FrameProcessorBase.h"
@@ -83,8 +84,7 @@
// Returns -EBUSY if device is not idle
virtual status_t deleteStream(int streamId);
- virtual status_t createStream(
- const sp<IGraphicBufferProducer>& bufferProducer);
+ virtual status_t createStream(const OutputConfiguration &outputConfiguration);
// Create a request object from a template.
virtual status_t createDefaultRequest(int templateId,
diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
deleted file mode 100644
index ba93554..0000000
--- a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "ProCamera2Client"
-#define ATRACE_TAG ATRACE_TAG_CAMERA
-//#define LOG_NDEBUG 0
-
-#include <utils/Log.h>
-#include <utils/Trace.h>
-
-#include <cutils/properties.h>
-#include <gui/Surface.h>
-#include <gui/Surface.h>
-
-#include "api_pro/ProCamera2Client.h"
-#include "common/CameraDeviceBase.h"
-
-namespace android {
-using namespace camera2;
-
-// Interface used by CameraService
-
-ProCamera2Client::ProCamera2Client(const sp<CameraService>& cameraService,
- const sp<IProCameraCallbacks>& remoteCallback,
- const String16& clientPackageName,
- int cameraId,
- int cameraFacing,
- int clientPid,
- uid_t clientUid,
- int servicePid) :
- Camera2ClientBase(cameraService, remoteCallback, clientPackageName,
- cameraId, cameraFacing, clientPid, clientUid, servicePid)
-{
- ATRACE_CALL();
- ALOGI("ProCamera %d: Opened", cameraId);
-
- mExclusiveLock = false;
-}
-
-status_t ProCamera2Client::initialize(CameraModule *module)
-{
- ATRACE_CALL();
- status_t res;
-
- res = Camera2ClientBase::initialize(module);
- if (res != OK) {
- return res;
- }
-
- String8 threadName;
- mFrameProcessor = new FrameProcessorBase(mDevice);
- threadName = String8::format("PC2-%d-FrameProc", mCameraId);
- mFrameProcessor->run(threadName.string());
-
- mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
- FRAME_PROCESSOR_LISTENER_MAX_ID,
- /*listener*/this);
-
- return OK;
-}
-
-ProCamera2Client::~ProCamera2Client() {
-}
-
-status_t ProCamera2Client::exclusiveTryLock() {
- ATRACE_CALL();
- ALOGV("%s", __FUNCTION__);
-
- Mutex::Autolock icl(mBinderSerializationLock);
- SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
-
- if (!mDevice.get()) return PERMISSION_DENIED;
-
- if (!mExclusiveLock) {
- mExclusiveLock = true;
-
- if (mRemoteCallback != NULL) {
- mRemoteCallback->onLockStatusChanged(
- IProCameraCallbacks::LOCK_ACQUIRED);
- }
-
- ALOGV("%s: exclusive lock acquired", __FUNCTION__);
-
- return OK;
- }
-
- // TODO: have a PERMISSION_DENIED case for when someone else owns the lock
-
- // don't allow recursive locking
- ALOGW("%s: exclusive lock already exists - recursive locking is not"
- "allowed", __FUNCTION__);
-
- return ALREADY_EXISTS;
-}
-
-status_t ProCamera2Client::exclusiveLock() {
- ATRACE_CALL();
- ALOGV("%s", __FUNCTION__);
-
- Mutex::Autolock icl(mBinderSerializationLock);
- SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
-
- if (!mDevice.get()) return PERMISSION_DENIED;
-
- /**
- * TODO: this should asynchronously 'wait' until the lock becomes available
- * if another client already has an exclusive lock.
- *
- * once we have proper sharing support this will need to do
- * more than just return immediately
- */
- if (!mExclusiveLock) {
- mExclusiveLock = true;
-
- if (mRemoteCallback != NULL) {
- mRemoteCallback->onLockStatusChanged(IProCameraCallbacks::LOCK_ACQUIRED);
- }
-
- ALOGV("%s: exclusive lock acquired", __FUNCTION__);
-
- return OK;
- }
-
- // don't allow recursive locking
- ALOGW("%s: exclusive lock already exists - recursive locking is not allowed"
- , __FUNCTION__);
- return ALREADY_EXISTS;
-}
-
-status_t ProCamera2Client::exclusiveUnlock() {
- ATRACE_CALL();
- ALOGV("%s", __FUNCTION__);
-
- Mutex::Autolock icl(mBinderSerializationLock);
- SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
-
- // don't allow unlocking if we have no lock
- if (!mExclusiveLock) {
- ALOGW("%s: cannot unlock, no lock was held in the first place",
- __FUNCTION__);
- return BAD_VALUE;
- }
-
- mExclusiveLock = false;
- if (mRemoteCallback != NULL ) {
- mRemoteCallback->onLockStatusChanged(
- IProCameraCallbacks::LOCK_RELEASED);
- }
- ALOGV("%s: exclusive lock released", __FUNCTION__);
-
- return OK;
-}
-
-bool ProCamera2Client::hasExclusiveLock() {
- Mutex::Autolock icl(mBinderSerializationLock);
- return mExclusiveLock;
-}
-
-void ProCamera2Client::onExclusiveLockStolen() {
- ALOGV("%s: ProClient lost exclusivity (id %d)",
- __FUNCTION__, mCameraId);
-
- Mutex::Autolock icl(mBinderSerializationLock);
- SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
-
- if (mExclusiveLock && mRemoteCallback.get() != NULL) {
- mRemoteCallback->onLockStatusChanged(
- IProCameraCallbacks::LOCK_STOLEN);
- }
-
- mExclusiveLock = false;
-
- //TODO: we should not need to detach the device, merely reset it.
- detachDevice();
-}
-
-status_t ProCamera2Client::submitRequest(camera_metadata_t* request,
- bool streaming) {
- ATRACE_CALL();
- ALOGV("%s", __FUNCTION__);
-
- Mutex::Autolock icl(mBinderSerializationLock);
-
- if (!mDevice.get()) return DEAD_OBJECT;
-
- if (!mExclusiveLock) {
- return PERMISSION_DENIED;
- }
-
- CameraMetadata metadata(request);
-
- if (!enforceRequestPermissions(metadata)) {
- return PERMISSION_DENIED;
- }
-
- if (streaming) {
- return mDevice->setStreamingRequest(metadata);
- } else {
- return mDevice->capture(metadata);
- }
-
- // unreachable. thx gcc for a useless warning
- return OK;
-}
-
-status_t ProCamera2Client::cancelRequest(int requestId) {
- (void)requestId;
- ATRACE_CALL();
- ALOGV("%s", __FUNCTION__);
-
- Mutex::Autolock icl(mBinderSerializationLock);
-
- if (!mDevice.get()) return DEAD_OBJECT;
-
- if (!mExclusiveLock) {
- return PERMISSION_DENIED;
- }
-
- // TODO: implement
- ALOGE("%s: not fully implemented yet", __FUNCTION__);
- return INVALID_OPERATION;
-}
-
-status_t ProCamera2Client::deleteStream(int streamId) {
- ATRACE_CALL();
- ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId);
-
- status_t res;
- if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
-
- Mutex::Autolock icl(mBinderSerializationLock);
-
- if (!mDevice.get()) return DEAD_OBJECT;
- mDevice->clearStreamingRequest();
-
- status_t code;
- if ((code = mDevice->waitUntilDrained()) != OK) {
- ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__, code);
- }
-
- return mDevice->deleteStream(streamId);
-}
-
-status_t ProCamera2Client::createStream(int width, int height, int format,
- const sp<IGraphicBufferProducer>& bufferProducer,
- /*out*/
- int* streamId)
-{
- if (streamId) {
- *streamId = -1;
- }
-
- ATRACE_CALL();
- ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format);
-
- status_t res;
- if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
-
- Mutex::Autolock icl(mBinderSerializationLock);
-
- if (!mDevice.get()) return DEAD_OBJECT;
-
- sp<IBinder> binder;
- sp<ANativeWindow> window;
- if (bufferProducer != 0) {
- binder = IInterface::asBinder(bufferProducer);
- window = new Surface(bufferProducer);
- }
-
- return mDevice->createStream(window, width, height, format,
- HAL_DATASPACE_UNKNOWN, streamId);
-}
-
-// Create a request object from a template.
-// -- Caller owns the newly allocated metadata
-status_t ProCamera2Client::createDefaultRequest(int templateId,
- /*out*/
- camera_metadata** request)
-{
- ATRACE_CALL();
- ALOGV("%s (templateId = 0x%x)", __FUNCTION__, templateId);
-
- if (request) {
- *request = NULL;
- }
-
- status_t res;
- if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
-
- Mutex::Autolock icl(mBinderSerializationLock);
-
- if (!mDevice.get()) return DEAD_OBJECT;
-
- CameraMetadata metadata;
- if ( (res = mDevice->createDefaultRequest(templateId, &metadata) ) == OK) {
- *request = metadata.release();
- }
-
- return res;
-}
-
-status_t ProCamera2Client::getCameraInfo(int cameraId,
- /*out*/
- camera_metadata** info)
-{
- if (cameraId != mCameraId) {
- return INVALID_OPERATION;
- }
-
- Mutex::Autolock icl(mBinderSerializationLock);
-
- if (!mDevice.get()) return DEAD_OBJECT;
-
- CameraMetadata deviceInfo = mDevice->info();
- *info = deviceInfo.release();
-
- return OK;
-}
-
-status_t ProCamera2Client::dump(int fd, const Vector<String16>& args) {
- String8 result;
- result.appendFormat("ProCamera2Client[%d] (%p) PID: %d, dump:\n",
- mCameraId,
- (getRemoteCallback() != NULL ?
- IInterface::asBinder(getRemoteCallback()).get() : NULL),
- mClientPid);
- result.append(" State:\n");
- write(fd, result.string(), result.size());
-
- // TODO: print dynamic/request section from most recent requests
- mFrameProcessor->dump(fd, args);
- return dumpDevice(fd, args);
-}
-
-// IProCameraUser interface
-
-void ProCamera2Client::detachDevice() {
- if (mDevice == 0) return;
-
- ALOGV("Camera %d: Stopping processors", mCameraId);
-
- mFrameProcessor->removeListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
- FRAME_PROCESSOR_LISTENER_MAX_ID,
- /*listener*/this);
- mFrameProcessor->requestExit();
- ALOGV("Camera %d: Waiting for threads", mCameraId);
- mFrameProcessor->join();
- ALOGV("Camera %d: Disconnecting device", mCameraId);
-
- // WORKAROUND: HAL refuses to disconnect while there's streams in flight
- {
- mDevice->clearStreamingRequest();
-
- status_t code;
- if ((code = mDevice->waitUntilDrained()) != OK) {
- ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__,
- code);
- }
- }
-
- Camera2ClientBase::detachDevice();
-}
-
-void ProCamera2Client::onResultAvailable(const CaptureResult& result) {
- ATRACE_CALL();
- ALOGV("%s", __FUNCTION__);
-
- Mutex::Autolock icl(mBinderSerializationLock);
- SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
-
- if (mRemoteCallback != NULL) {
- CameraMetadata tmp(result.mMetadata);
- camera_metadata_t* meta = tmp.release();
- ALOGV("%s: meta = %p ", __FUNCTION__, meta);
- mRemoteCallback->onResultReceived(result.mResultExtras.requestId, meta);
- tmp.acquire(meta);
- }
-}
-
-bool ProCamera2Client::enforceRequestPermissions(CameraMetadata& metadata) {
-
- const int pid = IPCThreadState::self()->getCallingPid();
- const int selfPid = getpid();
- camera_metadata_entry_t entry;
-
- /**
- * Mixin default important security values
- * - android.led.transmit = defaulted ON
- */
- CameraMetadata staticInfo = mDevice->info();
- entry = staticInfo.find(ANDROID_LED_AVAILABLE_LEDS);
- for(size_t i = 0; i < entry.count; ++i) {
- uint8_t led = entry.data.u8[i];
-
- switch(led) {
- case ANDROID_LED_AVAILABLE_LEDS_TRANSMIT: {
- uint8_t transmitDefault = ANDROID_LED_TRANSMIT_ON;
- if (!metadata.exists(ANDROID_LED_TRANSMIT)) {
- metadata.update(ANDROID_LED_TRANSMIT,
- &transmitDefault, 1);
- }
- break;
- }
- }
- }
-
- // We can do anything!
- if (pid == selfPid) {
- return true;
- }
-
- /**
- * Permission check special fields in the request
- * - android.led.transmit = android.permission.CAMERA_DISABLE_TRANSMIT
- */
- entry = metadata.find(ANDROID_LED_TRANSMIT);
- if (entry.count > 0 && entry.data.u8[0] != ANDROID_LED_TRANSMIT_ON) {
- String16 permissionString =
- String16("android.permission.CAMERA_DISABLE_TRANSMIT_LED");
- if (!checkCallingPermission(permissionString)) {
- const int uid = IPCThreadState::self()->getCallingUid();
- ALOGE("Permission Denial: "
- "can't disable transmit LED pid=%d, uid=%d", pid, uid);
- return false;
- }
- }
-
- return true;
-}
-
-} // namespace android
diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.h b/services/camera/libcameraservice/api_pro/ProCamera2Client.h
deleted file mode 100644
index 7f5f6ac..0000000
--- a/services/camera/libcameraservice/api_pro/ProCamera2Client.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_SERVERS_CAMERA_PROCAMERA2CLIENT_H
-#define ANDROID_SERVERS_CAMERA_PROCAMERA2CLIENT_H
-
-#include "CameraService.h"
-#include "common/FrameProcessorBase.h"
-#include "common/Camera2ClientBase.h"
-#include "device2/Camera2Device.h"
-#include "camera/CaptureResult.h"
-
-namespace android {
-
-class IMemory;
-/**
- * Implements the binder IProCameraUser API,
- * meant for HAL2-level private API access.
- */
-class ProCamera2Client :
- public Camera2ClientBase<CameraService::ProClient>,
- public camera2::FrameProcessorBase::FilteredListener
-{
-public:
- /**
- * IProCameraUser interface (see IProCameraUser for details)
- */
- virtual status_t exclusiveTryLock();
- virtual status_t exclusiveLock();
- virtual status_t exclusiveUnlock();
-
- virtual bool hasExclusiveLock();
-
- // Note that the callee gets a copy of the metadata.
- virtual int submitRequest(camera_metadata_t* metadata,
- bool streaming = false);
- virtual status_t cancelRequest(int requestId);
-
- virtual status_t deleteStream(int streamId);
-
- virtual status_t createStream(
- int width,
- int height,
- int format,
- const sp<IGraphicBufferProducer>& bufferProducer,
- /*out*/
- int* streamId);
-
- // Create a request object from a template.
- // -- Caller owns the newly allocated metadata
- virtual status_t createDefaultRequest(int templateId,
- /*out*/
- camera_metadata** request);
-
- // Get the static metadata for the camera
- // -- Caller owns the newly allocated metadata
- virtual status_t getCameraInfo(int cameraId,
- /*out*/
- camera_metadata** info);
-
- /**
- * Interface used by CameraService
- */
-
- ProCamera2Client(const sp<CameraService>& cameraService,
- const sp<IProCameraCallbacks>& remoteCallback,
- const String16& clientPackageName,
- int cameraId,
- int cameraFacing,
- int clientPid,
- uid_t clientUid,
- int servicePid);
- virtual ~ProCamera2Client();
-
- virtual status_t initialize(CameraModule *module);
-
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- // Callbacks from camera service
- virtual void onExclusiveLockStolen();
-
- /**
- * Interface used by independent components of ProCamera2Client.
- */
-
-protected:
- /** FilteredListener implementation **/
- virtual void onResultAvailable(const CaptureResult& result);
-
- virtual void detachDevice();
-
-private:
- /** IProCameraUser interface-related private members */
-
- /** Preview callback related members */
- sp<camera2::FrameProcessorBase> mFrameProcessor;
- static const int32_t FRAME_PROCESSOR_LISTENER_MIN_ID = 0;
- static const int32_t FRAME_PROCESSOR_LISTENER_MAX_ID = 0x7fffffffL;
-
- /** Utility members */
- bool enforceRequestPermissions(CameraMetadata& metadata);
-
- // Whether or not we have an exclusive lock on the device
- // - if no we can't modify the request queue.
- // note that creating/deleting streams we own is still OK
- bool mExclusiveLock;
-};
-
-}; // namespace android
-
-#endif
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 0415d67..c0c2314 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -337,7 +337,6 @@
mRemoteCallback.clear();
}
-template class Camera2ClientBase<CameraService::ProClient>;
template class Camera2ClientBase<CameraService::Client>;
template class Camera2ClientBase<CameraDeviceClientBase>;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index eb21d55..168ea0a 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -36,7 +36,7 @@
typedef typename TClientBase::TCamCallbacks TCamCallbacks;
/**
- * Base binder interface (see ICamera/IProCameraUser for details)
+ * Base binder interface (see ICamera/ICameraDeviceUser for details)
*/
virtual status_t connect(const sp<TCamCallbacks>& callbacks);
virtual void disconnect();
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 8764504..fe55b9e 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -100,14 +100,14 @@
nsecs_t timeout) = 0;
/**
- * Create an output stream of the requested size, format, and dataspace
+ * Create an output stream of the requested size, format, rotation and dataspace
*
* For HAL_PIXEL_FORMAT_BLOB formats, the width and height should be the
* logical dimensions of the buffer, not the number of bytes.
*/
virtual status_t createStream(sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, int *id) = 0;
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id) = 0;
/**
* Create an input reprocess stream that uses buffers from an existing
diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp
index ee862a2..878986b 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.cpp
+++ b/services/camera/libcameraservice/device2/Camera2Device.cpp
@@ -242,7 +242,7 @@
status_t Camera2Device::createStream(sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace /*dataSpace*/, int *id) {
+ android_dataspace /*dataSpace*/, camera3_stream_rotation_t rotation, int *id) {
ATRACE_CALL();
status_t res;
ALOGV("%s: E", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h
index e4c2856..9b32fa6 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.h
+++ b/services/camera/libcameraservice/device2/Camera2Device.h
@@ -58,7 +58,7 @@
virtual status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout);
virtual status_t createStream(sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, int *id);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id);
virtual status_t createReprocessStreamFromStream(int outputId, int *id);
virtual status_t getStreamInfo(int id,
uint32_t *width, uint32_t *height, uint32_t *format);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 529d249..8236788 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -802,12 +802,12 @@
status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, int format, android_dataspace dataSpace,
- int *id) {
+ camera3_stream_rotation_t rotation, int *id) {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
- ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d, dataspace %d",
- mId, mNextStreamId, width, height, format, dataSpace);
+ ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d, dataspace %d rotation %d",
+ mId, mNextStreamId, width, height, format, dataSpace, rotation);
status_t res;
bool wasActive = false;
@@ -847,10 +847,10 @@
}
newStream = new Camera3OutputStream(mNextStreamId, consumer,
- width, height, jpegBufferSize, format, dataSpace);
+ width, height, jpegBufferSize, format, dataSpace, rotation);
} else {
newStream = new Camera3OutputStream(mNextStreamId, consumer,
- width, height, format, dataSpace);
+ width, height, format, dataSpace, rotation);
}
newStream->setStatusTracker(mStatusTracker);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index e2ad1fa..a77548d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -96,7 +96,7 @@
// stream, reconfiguring device, and unpausing.
virtual status_t createStream(sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace, int *id);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id);
virtual status_t createInputStream(
uint32_t width, uint32_t height, int format,
int *id);
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 6201484..01edfff 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -28,7 +28,7 @@
Camera3DummyStream::Camera3DummyStream(int id) :
Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, DUMMY_WIDTH, DUMMY_HEIGHT,
- /*maxSize*/0, DUMMY_FORMAT, DUMMY_DATASPACE) {
+ /*maxSize*/0, DUMMY_FORMAT, DUMMY_DATASPACE, DUMMY_ROTATION) {
}
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index 7f52d84..d023c57 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -76,6 +76,7 @@
static const int DUMMY_HEIGHT = 240;
static const int DUMMY_FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
static const android_dataspace DUMMY_DATASPACE = HAL_DATASPACE_UNKNOWN;
+ static const camera3_stream_rotation_t DUMMY_ROTATION = CAMERA3_STREAM_ROTATION_0;
static const uint32_t DUMMY_USAGE = GRALLOC_USAGE_HW_COMPOSER;
/**
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index ff0acbb..8696413 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -31,9 +31,9 @@
Camera3IOStreamBase::Camera3IOStreamBase(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace) :
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
Camera3Stream(id, type,
- width, height, maxSize, format, dataSpace),
+ width, height, maxSize, format, dataSpace, rotation),
mTotalBufferCount(0),
mHandoutTotalBufferCount(0),
mHandoutOutputBufferCount(0),
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 83d4350..abcf2b1 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -34,7 +34,7 @@
protected:
Camera3IOStreamBase(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation);
public:
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 87907bf..6bf671e 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -29,8 +29,8 @@
Camera3InputStream::Camera3InputStream(int id,
uint32_t width, uint32_t height, int format) :
- Camera3IOStreamBase(id, CAMERA3_STREAM_INPUT, width, height,
- /*maxSize*/0, format, HAL_DATASPACE_UNKNOWN) {
+ Camera3IOStreamBase(id, CAMERA3_STREAM_INPUT, width, height, /*maxSize*/0,
+ format, HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0) {
if (format == HAL_PIXEL_FORMAT_BLOB) {
ALOGE("%s: Bad format, BLOB not supported", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 103d90b..96bed0d 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -34,9 +34,9 @@
Camera3OutputStream::Camera3OutputStream(int id,
sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace) :
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height,
- /*maxSize*/0, format, dataSpace),
+ /*maxSize*/0, format, dataSpace, rotation),
mConsumer(consumer),
mTransform(0),
mTraceFirstBuffer(true) {
@@ -50,9 +50,9 @@
Camera3OutputStream::Camera3OutputStream(int id,
sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace) :
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height, maxSize,
- format, dataSpace),
+ format, dataSpace, rotation),
mConsumer(consumer),
mTransform(0),
mTraceFirstBuffer(true) {
@@ -72,10 +72,11 @@
Camera3OutputStream::Camera3OutputStream(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height,
int format,
- android_dataspace dataSpace) :
+ android_dataspace dataSpace,
+ camera3_stream_rotation_t rotation) :
Camera3IOStreamBase(id, type, width, height,
/*maxSize*/0,
- format, dataSpace),
+ format, dataSpace, rotation),
mTransform(0) {
// Subclasses expected to initialize mConsumer themselves
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index f016d60..12b2ebb 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -40,7 +40,7 @@
*/
Camera3OutputStream(int id, sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation);
/**
* Set up a stream for formats that have a variable buffer size for the same
@@ -48,7 +48,7 @@
*/
Camera3OutputStream(int id, sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation);
virtual ~Camera3OutputStream();
@@ -67,7 +67,7 @@
protected:
Camera3OutputStream(int id, camera3_stream_type_t type,
uint32_t width, uint32_t height, int format,
- android_dataspace dataSpace);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation);
/**
* Note that we release the lock briefly in this function
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index f829741..4acbce3 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -47,7 +47,7 @@
Camera3Stream::Camera3Stream(int id,
camera3_stream_type type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace) :
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
camera3_stream(),
mId(id),
mName(String8::format("Camera3Stream[%d]", id)),
@@ -60,6 +60,7 @@
camera3_stream::height = height;
camera3_stream::format = format;
camera3_stream::data_space = dataSpace;
+ camera3_stream::rotation = rotation;
camera3_stream::usage = 0;
camera3_stream::max_buffers = 0;
camera3_stream::priv = NULL;
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 72f3ee9..aba27fe 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -266,7 +266,7 @@
Camera3Stream(int id, camera3_stream_type type,
uint32_t width, uint32_t height, size_t maxSize, int format,
- android_dataspace dataSpace);
+ android_dataspace dataSpace, camera3_stream_rotation_t rotation);
/**
* Interface to be implemented by derived classes
diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
index 5bf7a4c..10d7f2e 100644
--- a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
@@ -115,7 +115,7 @@
Camera3OutputStream(id, CAMERA3_STREAM_BIDIRECTIONAL,
width, height,
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
- HAL_DATASPACE_UNKNOWN),
+ HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0),
mDepth(bufferCount) {
sp<IGraphicBufferProducer> producer;
diff --git a/services/camera/libcameraservice/utils/AutoConditionLock.cpp b/services/camera/libcameraservice/utils/AutoConditionLock.cpp
new file mode 100644
index 0000000..c8ee965
--- /dev/null
+++ b/services/camera/libcameraservice/utils/AutoConditionLock.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "AutoConditionLock.h"
+
+namespace android {
+
+WaitableMutexWrapper::WaitableMutexWrapper(Mutex* mutex) : mMutex{mutex}, mState{false} {}
+
+WaitableMutexWrapper::~WaitableMutexWrapper() {}
+
+// Locks manager-owned mutex
+AutoConditionLock::AutoConditionLock(const std::shared_ptr<WaitableMutexWrapper>& manager) :
+ mManager{manager}, mAutoLock{manager->mMutex} {}
+
+// Unlocks manager-owned mutex
+AutoConditionLock::~AutoConditionLock() {
+ // Unset the condition and wake everyone up before releasing lock
+ mManager->mState = false;
+ mManager->mCondition.broadcast();
+}
+
+std::unique_ptr<AutoConditionLock> AutoConditionLock::waitAndAcquire(
+ const std::shared_ptr<WaitableMutexWrapper>& manager, nsecs_t waitTime) {
+
+ if (manager == nullptr || manager->mMutex == nullptr) {
+ // Bad input, return null
+ return std::unique_ptr<AutoConditionLock>{nullptr};
+ }
+
+ // Acquire scoped lock
+ std::unique_ptr<AutoConditionLock> scopedLock(new AutoConditionLock(manager));
+
+ // Figure out what time in the future we should hit the timeout
+ nsecs_t failTime = systemTime(SYSTEM_TIME_MONOTONIC) + waitTime;
+
+ // Wait until we timeout, or success
+ while(manager->mState) {
+ status_t ret = manager->mCondition.waitRelative(*(manager->mMutex), waitTime);
+ if (ret != NO_ERROR) {
+ // Timed out or whatever, return null
+ return std::unique_ptr<AutoConditionLock>{nullptr};
+ }
+ waitTime = failTime - systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+
+ // Set the condition and return
+ manager->mState = true;
+ return scopedLock;
+}
+
+std::unique_ptr<AutoConditionLock> AutoConditionLock::waitAndAcquire(
+ const std::shared_ptr<WaitableMutexWrapper>& manager) {
+
+ if (manager == nullptr || manager->mMutex == nullptr) {
+ // Bad input, return null
+ return std::unique_ptr<AutoConditionLock>{nullptr};
+ }
+
+ // Acquire scoped lock
+ std::unique_ptr<AutoConditionLock> scopedLock(new AutoConditionLock(manager));
+
+ // Wait until we timeout, or success
+ while(manager->mState) {
+ status_t ret = manager->mCondition.wait(*(manager->mMutex));
+ if (ret != NO_ERROR) {
+ // Timed out or whatever, return null
+ return std::unique_ptr<AutoConditionLock>{nullptr};
+ }
+ }
+
+ // Set the condition and return
+ manager->mState = true;
+ return scopedLock;
+}
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/utils/AutoConditionLock.h b/services/camera/libcameraservice/utils/AutoConditionLock.h
new file mode 100644
index 0000000..9a3eafc
--- /dev/null
+++ b/services/camera/libcameraservice/utils/AutoConditionLock.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+
+#ifndef ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H
+#define ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H
+
+#include <utils/Timers.h>
+#include <utils/Condition.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+
+#include <memory>
+
+namespace android {
+
+/**
+ * WaitableMutexWrapper can be used with AutoConditionLock to construct scoped locks for the
+ * wrapped Mutex with timeouts for lock acquisition.
+ */
+class WaitableMutexWrapper {
+ friend class AutoConditionLock;
+public:
+ /**
+ * Construct the ConditionManger with the given Mutex.
+ */
+ WaitableMutexWrapper(Mutex* mutex);
+
+ virtual ~WaitableMutexWrapper();
+private:
+ Mutex* mMutex;
+ bool mState;
+ Condition mCondition;
+};
+
+/**
+ * AutoConditionLock is a scoped lock similar to Mutex::Autolock, but allows timeouts to be
+ * specified for lock acquisition.
+ *
+ * AutoConditionLock is used with a WaitableMutexWrapper to lock/unlock the WaitableMutexWrapper's
+ * wrapped Mutex, and wait/set/signal the WaitableMutexWrapper's wrapped condition. To use this,
+ * call AutoConditionLock::waitAndAcquire to get an instance. This will:
+ * - Lock the given WaitableMutexWrapper's mutex.
+ * - Wait for the WaitableMutexWrapper's condition to become false, or timeout.
+ * - Set the WaitableMutexWrapper's condition to true.
+ *
+ * When the AutoConditionLock goes out of scope and is destroyed, this will:
+ * - Set the WaitableMutexWrapper's condition to false.
+ * - Signal threads waiting on this condition to wakeup.
+ * - Release WaitableMutexWrapper's mutex.
+ */
+class AutoConditionLock final {
+public:
+ AutoConditionLock() = delete;
+ AutoConditionLock(const AutoConditionLock& other) = delete;
+ AutoConditionLock & operator=(const AutoConditionLock&) = delete;
+
+ ~AutoConditionLock();
+
+ /**
+ * Make a new AutoConditionLock from a given WaitableMutexWrapper, waiting up to waitTime
+ * nanoseconds to acquire the WaitableMutexWrapper's wrapped lock.
+ *
+ * Return an empty unique_ptr if this fails, or a timeout occurs.
+ */
+ static std::unique_ptr<AutoConditionLock> waitAndAcquire(
+ const std::shared_ptr<WaitableMutexWrapper>& manager, nsecs_t waitTime);
+
+ /**
+ * Make a new AutoConditionLock from a given WaitableMutexWrapper, waiting indefinitely to
+ * acquire the WaitableMutexWrapper's wrapped lock.
+ *
+ * Return an empty unique_ptr if this fails.
+ */
+ static std::unique_ptr<AutoConditionLock> waitAndAcquire(
+ const std::shared_ptr<WaitableMutexWrapper>& manager);
+private:
+ AutoConditionLock(const std::shared_ptr<WaitableMutexWrapper>& manager);
+
+ std::shared_ptr<WaitableMutexWrapper> mManager;
+ Mutex::Autolock mAutoLock;
+};
+
+}; // namespace android
+
+#endif // ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H
diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h
new file mode 100644
index 0000000..ad5486d
--- /dev/null
+++ b/services/camera/libcameraservice/utils/ClientManager.h
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
+#define ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
+
+#include <utils/Mutex.h>
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+#include <set>
+#include <map>
+#include <memory>
+
+namespace android {
+namespace resource_policy {
+
+// --------------------------------------------------------------------------------
+
+/**
+ * The ClientDescriptor class is a container for a given key/value pair identifying a shared
+ * resource, and the corresponding cost, priority, owner ID, and conflicting keys list used
+ * in determining eviction behavior.
+ *
+ * Aside from the priority, these values are immutable once the ClientDescriptor has been
+ * constructed.
+ */
+template<class KEY, class VALUE>
+class ClientDescriptor final {
+public:
+ ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
+ const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId);
+ ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, std::set<KEY>&& conflictingKeys,
+ int32_t priority, int32_t ownerId);
+
+ ~ClientDescriptor();
+
+ /**
+ * Return the key for this descriptor.
+ */
+ const KEY& getKey() const;
+
+ /**
+ * Return the value for this descriptor.
+ */
+ const VALUE& getValue() const;
+
+ /**
+ * Return the cost for this descriptor.
+ */
+ int32_t getCost() const;
+
+ /**
+ * Return the priority for this descriptor.
+ */
+ int32_t getPriority() const;
+
+ /**
+ * Return the owner ID for this descriptor.
+ */
+ int32_t getOwnerId() const;
+
+ /**
+ * Return true if the given key is in this descriptor's conflicting keys list.
+ */
+ bool isConflicting(const KEY& key) const;
+
+ /**
+ * Return the set of all conflicting keys for this descriptor.
+ */
+ std::set<KEY> getConflicting() const;
+
+ /**
+ * Set the proirity for this descriptor.
+ */
+ void setPriority(int32_t priority);
+
+ // This class is ordered by key
+ template<class K, class V>
+ friend bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b);
+
+private:
+ KEY mKey;
+ VALUE mValue;
+ int32_t mCost;
+ std::set<KEY> mConflicting;
+ int32_t mPriority;
+ int32_t mOwnerId;
+}; // class ClientDescriptor
+
+template<class K, class V>
+bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b) {
+ return a.mKey < b.mKey;
+}
+
+template<class KEY, class VALUE>
+ClientDescriptor<KEY, VALUE>::ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
+ const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId) : mKey{key},
+ mValue{value}, mCost{cost}, mConflicting{conflictingKeys}, mPriority{priority},
+ mOwnerId{ownerId} {}
+
+template<class KEY, class VALUE>
+ClientDescriptor<KEY, VALUE>::ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost,
+ std::set<KEY>&& conflictingKeys, int32_t priority, int32_t ownerId) :
+ mKey{std::forward<KEY>(key)}, mValue{std::forward<VALUE>(value)}, mCost{cost},
+ mConflicting{std::forward<std::set<KEY>>(conflictingKeys)}, mPriority{priority},
+ mOwnerId{ownerId} {}
+
+template<class KEY, class VALUE>
+ClientDescriptor<KEY, VALUE>::~ClientDescriptor() {}
+
+template<class KEY, class VALUE>
+const KEY& ClientDescriptor<KEY, VALUE>::getKey() const {
+ return mKey;
+}
+
+template<class KEY, class VALUE>
+const VALUE& ClientDescriptor<KEY, VALUE>::getValue() const {
+ return mValue;
+}
+
+template<class KEY, class VALUE>
+int32_t ClientDescriptor<KEY, VALUE>::getCost() const {
+ return mCost;
+}
+
+template<class KEY, class VALUE>
+int32_t ClientDescriptor<KEY, VALUE>::getPriority() const {
+ return mPriority;
+}
+
+template<class KEY, class VALUE>
+int32_t ClientDescriptor<KEY, VALUE>::getOwnerId() const {
+ return mOwnerId;
+}
+
+template<class KEY, class VALUE>
+bool ClientDescriptor<KEY, VALUE>::isConflicting(const KEY& key) const {
+ if (key == mKey) return true;
+ for (const auto& x : mConflicting) {
+ if (key == x) return true;
+ }
+ return false;
+}
+
+template<class KEY, class VALUE>
+std::set<KEY> ClientDescriptor<KEY, VALUE>::getConflicting() const {
+ return mConflicting;
+}
+
+template<class KEY, class VALUE>
+void ClientDescriptor<KEY, VALUE>::setPriority(int32_t priority) {
+ mPriority = priority;
+}
+
+// --------------------------------------------------------------------------------
+
+/**
+ * The ClientManager class wraps an LRU-ordered list of active clients and implements eviction
+ * behavior for handling shared resource access.
+ *
+ * When adding a new descriptor, eviction behavior is as follows:
+ * - Keys are unique, adding a descriptor with the same key as an existing descriptor will
+ * result in the lower-priority of the two being removed. Priority ties result in the
+ * LRU descriptor being evicted (this means the incoming descriptor be added in this case).
+ * - Any descriptors with keys that are in the incoming descriptor's 'conflicting keys' list
+ * will be removed if they have an equal or lower priority than the incoming descriptor;
+ * if any have a higher priority, the incoming descriptor is removed instead.
+ * - If the sum of all descriptors' costs, including the incoming descriptor's, is more than
+ * the max cost allowed for this ClientManager, descriptors with non-zero cost, equal or lower
+ * priority, and a different owner will be evicted in LRU order until either the cost is less
+ * than the max cost, or all descriptors meeting this criteria have been evicted and the
+ * incoming descriptor has the highest priority. Otherwise, the incoming descriptor is
+ * removed instead.
+ */
+template<class KEY, class VALUE>
+class ClientManager {
+public:
+ // The default maximum "cost" allowed before evicting
+ static constexpr int32_t DEFAULT_MAX_COST = 100;
+
+ ClientManager();
+ ClientManager(int32_t totalCost);
+
+ /**
+ * Add a given ClientDescriptor to the managed list. ClientDescriptors for clients that
+ * are evicted by this action are returned in a vector.
+ *
+ * This may return the ClientDescriptor passed in if it would be evicted.
+ */
+ std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> addAndEvict(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client);
+
+ /**
+ * Given a map containing owner (pid) -> priority mappings, update the priority of each
+ * ClientDescriptor with an owner in this mapping.
+ */
+ void updatePriorities(const std::map<int32_t,int32_t>& ownerPriorityList);
+
+ /**
+ * Remove all ClientDescriptors.
+ */
+ void removeAll();
+
+ /**
+ * Remove and return the ClientDescriptor with a given key.
+ */
+ std::shared_ptr<ClientDescriptor<KEY, VALUE>> remove(const KEY& key);
+
+ /**
+ * Remove the given ClientDescriptor.
+ */
+ void remove(const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value);
+
+ /**
+ * Return a vector of the ClientDescriptors that would be evicted by adding the given
+ * ClientDescriptor.
+ *
+ * This may return the ClientDescriptor passed in if it would be evicted.
+ */
+ std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvict(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;
+
+ /**
+ * Return a vector of active ClientDescriptors that prevent this client from being added.
+ */
+ std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getIncompatibleClients(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;
+
+ /**
+ * Return a vector containing all currently active ClientDescriptors.
+ */
+ std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getAll() const;
+
+ /**
+ * Return a vector containing all keys of currently active ClientDescriptors.
+ */
+ std::vector<KEY> getAllKeys() const;
+
+ /**
+ * Return a vector of the owner tags of all currently active ClientDescriptors (duplicates
+ * will be removed).
+ */
+ std::vector<int32_t> getAllOwners() const;
+
+ /**
+ * Return the ClientDescriptor corresponding to the given key, or an empty shared pointer
+ * if none exists.
+ */
+ std::shared_ptr<ClientDescriptor<KEY, VALUE>> get(const KEY& key) const;
+
+protected:
+ ~ClientManager();
+
+private:
+
+ /**
+ * Return a vector of the ClientDescriptors that would be evicted by adding the given
+ * ClientDescriptor. If returnIncompatibleClients is set to true, instead, return the
+ * vector of ClientDescriptors that are higher priority than the incoming client and
+ * either conflict with this client, or contribute to the resource cost if that would
+ * prevent the incoming client from being added.
+ *
+ * This may return the ClientDescriptor passed in.
+ */
+ std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvictLocked(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
+ bool returnIncompatibleClients = false) const;
+
+ int64_t getCurrentCostLocked() const;
+
+ mutable Mutex mLock;
+ int32_t mMaxCost;
+ // LRU ordered, most recent at end
+ std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients;
+}; // class ClientManager
+
+template<class KEY, class VALUE>
+ClientManager<KEY, VALUE>::ClientManager() :
+ ClientManager(DEFAULT_MAX_COST) {}
+
+template<class KEY, class VALUE>
+ClientManager<KEY, VALUE>::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {}
+
+template<class KEY, class VALUE>
+ClientManager<KEY, VALUE>::~ClientManager() {}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::wouldEvict(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
+ Mutex::Autolock lock(mLock);
+ return wouldEvictLocked(client);
+}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
+ClientManager<KEY, VALUE>::getIncompatibleClients(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
+ Mutex::Autolock lock(mLock);
+ return wouldEvictLocked(client, /*returnIncompatibleClients*/true);
+}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
+ClientManager<KEY, VALUE>::wouldEvictLocked(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
+ bool returnIncompatibleClients) const {
+
+ std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> evictList;
+
+ // Disallow null clients, return input
+ if (client == nullptr) {
+ evictList.push_back(client);
+ return evictList;
+ }
+
+ const KEY& key = client->getKey();
+ int32_t cost = client->getCost();
+ int32_t priority = client->getPriority();
+ int32_t owner = client->getOwnerId();
+
+ int64_t totalCost = getCurrentCostLocked() + cost;
+
+ // Determine the MRU of the owners tied for having the highest priority
+ int32_t highestPriorityOwner = owner;
+ int32_t highestPriority = priority;
+ for (const auto& i : mClients) {
+ int32_t curPriority = i->getPriority();
+ if (curPriority >= highestPriority) {
+ highestPriority = curPriority;
+ highestPriorityOwner = i->getOwnerId();
+ }
+ }
+
+ if (highestPriority == priority) {
+ // Switch back owner if the incoming client has the highest priority, as it is MRU
+ highestPriorityOwner = owner;
+ }
+
+ // Build eviction list of clients to remove
+ for (const auto& i : mClients) {
+ const KEY& curKey = i->getKey();
+ int32_t curCost = i->getCost();
+ int32_t curPriority = i->getPriority();
+ int32_t curOwner = i->getOwnerId();
+
+ bool conflicting = (curKey == key || i->isConflicting(key) ||
+ client->isConflicting(curKey));
+
+ if (!returnIncompatibleClients) {
+ // Find evicted clients
+
+ if (conflicting && curPriority > priority) {
+ // Pre-existing conflicting client with higher priority exists
+ evictList.clear();
+ evictList.push_back(client);
+ return evictList;
+ } else if (conflicting || ((totalCost > mMaxCost && curCost > 0) &&
+ (curPriority <= priority) &&
+ !(highestPriorityOwner == owner && owner == curOwner))) {
+ // Add a pre-existing client to the eviction list if:
+ // - We are adding a client with higher priority that conflicts with this one.
+ // - The total cost including the incoming client's is more than the allowable
+ // maximum, and the client has a non-zero cost, lower priority, and a different
+ // owner than the incoming client when the incoming client has the
+ // highest priority.
+ evictList.push_back(i);
+ totalCost -= curCost;
+ }
+ } else {
+ // Find clients preventing the incoming client from being added
+
+ if (curPriority > priority && (conflicting || (totalCost > mMaxCost && curCost > 0))) {
+ // Pre-existing conflicting client with higher priority exists
+ evictList.push_back(i);
+ }
+ }
+ }
+
+ // Immediately return the incompatible clients if we are calculating these instead
+ if (returnIncompatibleClients) {
+ return evictList;
+ }
+
+ // If the total cost is too high, return the input unless the input has the highest priority
+ if (totalCost > mMaxCost && highestPriorityOwner != owner) {
+ evictList.clear();
+ evictList.push_back(client);
+ return evictList;
+ }
+
+ return evictList;
+
+}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::addAndEvict(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) {
+ Mutex::Autolock lock(mLock);
+ auto evicted = wouldEvictLocked(client);
+ auto it = evicted.begin();
+ if (it != evicted.end() && *it == client) {
+ return evicted;
+ }
+
+ auto iter = evicted.cbegin();
+
+ // Remove evicted clients from list
+ mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
+ [&iter] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
+ if (curClientPtr->getKey() == (*iter)->getKey()) {
+ iter++;
+ return true;
+ }
+ return false;
+ }), mClients.end());
+
+ mClients.push_back(client);
+
+ return evicted;
+}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
+ClientManager<KEY, VALUE>::getAll() const {
+ Mutex::Autolock lock(mLock);
+ return mClients;
+}
+
+template<class KEY, class VALUE>
+std::vector<KEY> ClientManager<KEY, VALUE>::getAllKeys() const {
+ Mutex::Autolock lock(mLock);
+ std::vector<KEY> keys(mClients.size());
+ for (const auto& i : mClients) {
+ keys.push_back(i->getKey());
+ }
+ return keys;
+}
+
+template<class KEY, class VALUE>
+std::vector<int32_t> ClientManager<KEY, VALUE>::getAllOwners() const {
+ Mutex::Autolock lock(mLock);
+ std::set<int32_t> owners;
+ for (const auto& i : mClients) {
+ owners.emplace(i->getOwnerId());
+ }
+ return std::vector<int32_t>(owners.begin(), owners.end());
+}
+
+template<class KEY, class VALUE>
+void ClientManager<KEY, VALUE>::updatePriorities(
+ const std::map<int32_t,int32_t>& ownerPriorityList) {
+ Mutex::Autolock lock(mLock);
+ for (auto& i : mClients) {
+ auto j = ownerPriorityList.find(i->getOwnerId());
+ if (j != ownerPriorityList.end()) {
+ i->setPriority(j->second);
+ }
+ }
+}
+
+template<class KEY, class VALUE>
+std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::get(
+ const KEY& key) const {
+ Mutex::Autolock lock(mLock);
+ for (const auto& i : mClients) {
+ if (i->getKey() == key) return i;
+ }
+ return std::shared_ptr<ClientDescriptor<KEY, VALUE>>(nullptr);
+}
+
+template<class KEY, class VALUE>
+void ClientManager<KEY, VALUE>::removeAll() {
+ Mutex::Autolock lock(mLock);
+ mClients.clear();
+}
+
+template<class KEY, class VALUE>
+std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::remove(const KEY& key) {
+ Mutex::Autolock lock(mLock);
+
+ std::shared_ptr<ClientDescriptor<KEY, VALUE>> ret;
+
+ // Remove evicted clients from list
+ mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
+ [&key, &ret] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
+ if (curClientPtr->getKey() == key) {
+ ret = curClientPtr;
+ return true;
+ }
+ return false;
+ }), mClients.end());
+
+ return ret;
+}
+
+template<class KEY, class VALUE>
+void ClientManager<KEY, VALUE>::remove(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value) {
+ Mutex::Autolock lock(mLock);
+ // Remove evicted clients from list
+ mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
+ [&value] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
+ if (curClientPtr == value) {
+ return true;
+ }
+ return false;
+ }), mClients.end());
+}
+
+template<class KEY, class VALUE>
+int64_t ClientManager<KEY, VALUE>::getCurrentCostLocked() const {
+ int64_t totalCost = 0;
+ for (const auto& x : mClients) {
+ totalCost += x->getCost();
+ }
+ return totalCost;
+}
+
+// --------------------------------------------------------------------------------
+
+}; // namespace resource_policy
+}; // namespace android
+
+#endif // ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
diff --git a/services/camera/libcameraservice/utils/RingBuffer.h b/services/camera/libcameraservice/utils/RingBuffer.h
new file mode 100644
index 0000000..df7c00e
--- /dev/null
+++ b/services/camera/libcameraservice/utils/RingBuffer.h
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+
+#ifndef ANDROID_SERVICE_UTILS_RING_BUFFER_H
+#define ANDROID_SERVICE_UTILS_RING_BUFFER_H
+
+#include <utils/Log.h>
+#include <cutils/compiler.h>
+
+#include <iterator>
+#include <utility>
+#include <vector>
+
+namespace android {
+
+/**
+ * A RingBuffer class that maintains an array of objects that can grow up to a certain size.
+ * Elements added to the RingBuffer are inserted in the logical front of the buffer, and
+ * invalidate all current iterators for that RingBuffer object.
+ */
+template <class T>
+class RingBuffer final {
+public:
+
+ /**
+ * Construct a RingBuffer that can grow up to the given length.
+ */
+ RingBuffer(size_t length);
+
+ /**
+ * Forward iterator to this class. Implements an std:forward_iterator.
+ */
+ class iterator : public std::iterator<std::forward_iterator_tag, T> {
+ public:
+ iterator(T* ptr, size_t size, size_t pos, size_t ctr);
+
+ iterator& operator++();
+
+ iterator operator++(int);
+
+ bool operator==(const iterator& rhs);
+
+ bool operator!=(const iterator& rhs);
+
+ T& operator*();
+
+ T* operator->();
+
+ private:
+ T* mPtr;
+ size_t mSize;
+ size_t mPos;
+ size_t mCtr;
+ };
+
+ /**
+ * Constant forward iterator to this class. Implements an std:forward_iterator.
+ */
+ class const_iterator : public std::iterator<std::forward_iterator_tag, T> {
+ public:
+ const_iterator(const T* ptr, size_t size, size_t pos, size_t ctr);
+
+ const_iterator& operator++();
+
+ const_iterator operator++(int);
+
+ bool operator==(const const_iterator& rhs);
+
+ bool operator!=(const const_iterator& rhs);
+
+ const T& operator*();
+
+ const T* operator->();
+
+ private:
+ const T* mPtr;
+ size_t mSize;
+ size_t mPos;
+ size_t mCtr;
+ };
+
+ /**
+ * Adds item to the front of this RingBuffer. If the RingBuffer is at its maximum length,
+ * this will result in the last element being replaced (this is done using the element's
+ * assignment operator).
+ *
+ * All current iterators are invalidated.
+ */
+ void add(const T& item);
+
+ /**
+ * Moves item to the front of this RingBuffer. Following a call to this, item should no
+ * longer be used. If the RingBuffer is at its maximum length, this will result in the
+ * last element being replaced (this is done using the element's assignment operator).
+ *
+ * All current iterators are invalidated.
+ */
+ void add(T&& item);
+
+ /**
+ * Construct item in-place in the front of this RingBuffer using the given arguments. If
+ * the RingBuffer is at its maximum length, this will result in the last element being
+ * replaced (this is done using the element's assignment operator).
+ *
+ * All current iterators are invalidated.
+ */
+ template <class... Args>
+ void emplace(Args&&... args);
+
+ /**
+ * Get an iterator to the front of this RingBuffer.
+ */
+ iterator begin();
+
+ /**
+ * Get an iterator to the end of this RingBuffer.
+ */
+ iterator end();
+
+ /**
+ * Get a const_iterator to the front of this RingBuffer.
+ */
+ const_iterator begin() const;
+
+ /**
+ * Get a const_iterator to the end of this RingBuffer.
+ */
+ const_iterator end() const;
+
+ /**
+ * Return a reference to the element at a given index. If the index is out of range for
+ * this ringbuffer, [0, size), the behavior for this is undefined.
+ */
+ T& operator[](size_t index);
+
+ /**
+ * Return a const reference to the element at a given index. If the index is out of range
+ * for this ringbuffer, [0, size), the behavior for this is undefined.
+ */
+ const T& operator[](size_t index) const;
+
+ /**
+ * Return the current size of this RingBuffer.
+ */
+ size_t size() const;
+
+ /**
+ * Remove all elements from this RingBuffer and set the size to 0.
+ */
+ void clear();
+
+private:
+ size_t mFrontIdx;
+ size_t mMaxBufferSize;
+ std::vector<T> mBuffer;
+}; // class RingBuffer
+
+
+template <class T>
+RingBuffer<T>::RingBuffer(size_t length) : mFrontIdx{0}, mMaxBufferSize{length} {}
+
+template <class T>
+RingBuffer<T>::iterator::iterator(T* ptr, size_t size, size_t pos, size_t ctr) :
+ mPtr{ptr}, mSize{size}, mPos{pos}, mCtr{ctr} {}
+
+template <class T>
+typename RingBuffer<T>::iterator& RingBuffer<T>::iterator::operator++() {
+ ++mCtr;
+
+ if (CC_UNLIKELY(mCtr == mSize)) {
+ mPos = mSize;
+ return *this;
+ }
+
+ mPos = ((CC_UNLIKELY(mPos == 0)) ? mSize - 1 : mPos - 1);
+ return *this;
+}
+
+template <class T>
+typename RingBuffer<T>::iterator RingBuffer<T>::iterator::operator++(int) {
+ iterator tmp{mPtr, mSize, mPos, mCtr};
+ ++(*this);
+ return tmp;
+}
+
+template <class T>
+bool RingBuffer<T>::iterator::operator==(const iterator& rhs) {
+ return (mPtr + mPos) == (rhs.mPtr + rhs.mPos);
+}
+
+template <class T>
+bool RingBuffer<T>::iterator::operator!=(const iterator& rhs) {
+ return (mPtr + mPos) != (rhs.mPtr + rhs.mPos);
+}
+
+template <class T>
+T& RingBuffer<T>::iterator::operator*() {
+ return *(mPtr + mPos);
+}
+
+template <class T>
+T* RingBuffer<T>::iterator::operator->() {
+ return mPtr + mPos;
+}
+
+template <class T>
+RingBuffer<T>::const_iterator::const_iterator(const T* ptr, size_t size, size_t pos, size_t ctr) :
+ mPtr{ptr}, mSize{size}, mPos{pos}, mCtr{ctr} {}
+
+template <class T>
+typename RingBuffer<T>::const_iterator& RingBuffer<T>::const_iterator::operator++() {
+ ++mCtr;
+
+ if (CC_UNLIKELY(mCtr == mSize)) {
+ mPos = mSize;
+ return *this;
+ }
+
+ mPos = ((CC_UNLIKELY(mPos == 0)) ? mSize - 1 : mPos - 1);
+ return *this;
+}
+
+template <class T>
+typename RingBuffer<T>::const_iterator RingBuffer<T>::const_iterator::operator++(int) {
+ const_iterator tmp{mPtr, mSize, mPos, mCtr};
+ ++(*this);
+ return tmp;
+}
+
+template <class T>
+bool RingBuffer<T>::const_iterator::operator==(const const_iterator& rhs) {
+ return (mPtr + mPos) == (rhs.mPtr + rhs.mPos);
+}
+
+template <class T>
+bool RingBuffer<T>::const_iterator::operator!=(const const_iterator& rhs) {
+ return (mPtr + mPos) != (rhs.mPtr + rhs.mPos);
+}
+
+template <class T>
+const T& RingBuffer<T>::const_iterator::operator*() {
+ return *(mPtr + mPos);
+}
+
+template <class T>
+const T* RingBuffer<T>::const_iterator::operator->() {
+ return mPtr + mPos;
+}
+
+template <class T>
+void RingBuffer<T>::add(const T& item) {
+ if (mBuffer.size() < mMaxBufferSize) {
+ mBuffer.push_back(item);
+ mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+ return;
+ }
+
+ mBuffer[mFrontIdx] = item;
+ mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+}
+
+template <class T>
+void RingBuffer<T>::add(T&& item) {
+ if (mBuffer.size() != mMaxBufferSize) {
+ mBuffer.push_back(std::forward<T>(item));
+ mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+ return;
+ }
+
+ // Only works for types with move assignment operator
+ mBuffer[mFrontIdx] = std::forward<T>(item);
+ mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+}
+
+template <class T>
+template <class... Args>
+void RingBuffer<T>::emplace(Args&&... args) {
+ if (mBuffer.size() != mMaxBufferSize) {
+ mBuffer.emplace_back(std::forward<Args>(args)...);
+ mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+ return;
+ }
+
+ // Only works for types with move assignment operator
+ mBuffer[mFrontIdx] = T(std::forward<Args>(args)...);
+ mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+}
+
+template <class T>
+typename RingBuffer<T>::iterator RingBuffer<T>::begin() {
+ size_t tmp = (mBuffer.size() == 0) ? 0 : mBuffer.size() - 1;
+ return iterator(mBuffer.data(), mBuffer.size(), (mFrontIdx == 0) ? tmp : mFrontIdx - 1, 0);
+}
+
+template <class T>
+typename RingBuffer<T>::iterator RingBuffer<T>::end() {
+ size_t s = mBuffer.size();
+ return iterator(mBuffer.data(), s, s, s);
+}
+
+template <class T>
+typename RingBuffer<T>::const_iterator RingBuffer<T>::begin() const {
+ size_t tmp = (mBuffer.size() == 0) ? 0 : mBuffer.size() - 1;
+ return const_iterator(mBuffer.data(), mBuffer.size(),
+ (mFrontIdx == 0) ? tmp : mFrontIdx - 1, 0);
+}
+
+template <class T>
+typename RingBuffer<T>::const_iterator RingBuffer<T>::end() const {
+ size_t s = mBuffer.size();
+ return const_iterator(mBuffer.data(), s, s, s);
+}
+
+template <class T>
+T& RingBuffer<T>::operator[](size_t index) {
+ LOG_ALWAYS_FATAL_IF(index >= mBuffer.size(), "Index %zu out of bounds, size is %zu.",
+ index, mBuffer.size());
+ size_t pos = (index >= mFrontIdx) ?
+ mBuffer.size() - 1 - (index - mFrontIdx) : mFrontIdx - 1 - index;
+ return mBuffer[pos];
+}
+
+template <class T>
+const T& RingBuffer<T>::operator[](size_t index) const {
+ LOG_ALWAYS_FATAL_IF(index >= mBuffer.size(), "Index %zu out of bounds, size is %zu.",
+ index, mBuffer.size());
+ size_t pos = (index >= mFrontIdx) ?
+ mBuffer.size() - 1 - (index - mFrontIdx) : mFrontIdx - 1 - index;
+ return mBuffer[pos];
+}
+
+template <class T>
+size_t RingBuffer<T>::size() const {
+ return mBuffer.size();
+}
+
+template <class T>
+void RingBuffer<T>::clear() {
+ mBuffer.clear();
+ mFrontIdx = 0;
+}
+
+}; // namespace android
+
+#endif // ANDROID_SERVICE_UTILS_RING_BUFFER_H
+
+
diff --git a/services/radio/Android.mk b/services/radio/Android.mk
new file mode 100644
index 0000000..9ee5666
--- /dev/null
+++ b/services/radio/Android.mk
@@ -0,0 +1,36 @@
+# Copyright 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+
+LOCAL_SRC_FILES:= \
+ RadioService.cpp
+
+LOCAL_SHARED_LIBRARIES:= \
+ libui \
+ liblog \
+ libutils \
+ libbinder \
+ libcutils \
+ libmedia \
+ libhardware \
+ libradio \
+ libradio_metadata
+
+LOCAL_MODULE:= libradioservice
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/radio/RadioRegions.h b/services/radio/RadioRegions.h
new file mode 100644
index 0000000..3335b8a
--- /dev/null
+++ b/services/radio/RadioRegions.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_HARDWARE_RADIO_REGIONS_H
+#define ANDROID_HARDWARE_RADIO_REGIONS_H
+
+namespace android {
+
+#define RADIO_BAND_LOWER_FM_ITU1 87500
+#define RADIO_BAND_UPPER_FM_ITU1 108000
+#define RADIO_BAND_SPACING_FM_ITU1 100
+
+#define RADIO_BAND_LOWER_FM_ITU2 87900
+#define RADIO_BAND_UPPER_FM_ITU2 107900
+#define RADIO_BAND_SPACING_FM_ITU2 200
+
+#define RADIO_BAND_LOWER_FM_JAPAN 76000
+#define RADIO_BAND_UPPER_FM_JAPAN 90000
+#define RADIO_BAND_SPACING_FM_JAPAN 100
+
+#define RADIO_BAND_LOWER_FM_OIRT 65800
+#define RADIO_BAND_UPPER_FM_OIRT 74000
+#define RADIO_BAND_SPACING_FM_OIRT 10
+
+#define RADIO_BAND_LOWER_LW 153
+#define RADIO_BAND_UPPER_LW 279
+#define RADIO_BAND_SPACING_LW 9
+
+#define RADIO_BAND_LOWER_MW_IUT1 531
+#define RADIO_BAND_UPPER_MW_ITU1 1611
+#define RADIO_BAND_SPACING_MW_ITU1 9
+
+#define RADIO_BAND_LOWER_MW_IUT2 540
+#define RADIO_BAND_UPPER_MW_ITU2 1610
+#define RADIO_BAND_SPACING_MW_ITU2 10
+
+#define RADIO_BAND_LOWER_SW 2300
+#define RADIO_BAND_UPPER_SW 26100
+#define RADIO_BAND_SPACING_SW 5
+
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+const radio_band_config_t sKnownRegionConfigs[] = {
+ { // FM ITU 1
+ RADIO_REGION_ITU_1,
+ {
+ RADIO_BAND_FM,
+ false,
+ RADIO_BAND_LOWER_FM_ITU1,
+ RADIO_BAND_UPPER_FM_ITU1,
+ 1,
+ {RADIO_BAND_SPACING_FM_ITU1},
+ {
+ RADIO_DEEMPHASIS_50,
+ true,
+ RADIO_RDS_WORLD,
+ true,
+ true,
+ }
+ }
+ },
+ { // FM Americas
+ RADIO_REGION_ITU_2,
+ {
+ RADIO_BAND_FM,
+ false,
+ RADIO_BAND_LOWER_FM_ITU2,
+ RADIO_BAND_UPPER_FM_ITU2,
+ 1,
+ {RADIO_BAND_SPACING_FM_ITU2},
+ {
+ RADIO_DEEMPHASIS_75,
+ true,
+ RADIO_RDS_US,
+ true,
+ true,
+ }
+ }
+ },
+ { // FM Japan
+ RADIO_REGION_JAPAN,
+ {
+ RADIO_BAND_FM,
+ false,
+ RADIO_BAND_LOWER_FM_JAPAN,
+ RADIO_BAND_UPPER_FM_JAPAN,
+ 1,
+ {RADIO_BAND_SPACING_FM_JAPAN},
+ {
+ RADIO_DEEMPHASIS_50,
+ true,
+ RADIO_RDS_WORLD,
+ true,
+ true,
+ }
+ }
+ },
+ { // FM Korea
+ RADIO_REGION_KOREA,
+ {
+ RADIO_BAND_FM,
+ false,
+ RADIO_BAND_LOWER_FM_ITU1,
+ RADIO_BAND_UPPER_FM_ITU1,
+ 1,
+ {RADIO_BAND_SPACING_FM_ITU1},
+ {
+ RADIO_DEEMPHASIS_75,
+ true,
+ RADIO_RDS_WORLD,
+ true,
+ true,
+ }
+ }
+ },
+ { // FM OIRT
+ RADIO_REGION_OIRT,
+ {
+ RADIO_BAND_FM,
+ false,
+ RADIO_BAND_LOWER_FM_OIRT,
+ RADIO_BAND_UPPER_FM_OIRT,
+ 1,
+ {RADIO_BAND_SPACING_FM_OIRT},
+ {
+ RADIO_DEEMPHASIS_50,
+ true,
+ RADIO_RDS_WORLD,
+ true,
+ true,
+ }
+ }
+ },
+ { // FM US HD radio
+ RADIO_REGION_ITU_2,
+ {
+ RADIO_BAND_FM_HD,
+ false,
+ RADIO_BAND_LOWER_FM_ITU2,
+ RADIO_BAND_UPPER_FM_ITU2,
+ 1,
+ {RADIO_BAND_SPACING_FM_ITU2},
+ {
+ RADIO_DEEMPHASIS_75,
+ true,
+ RADIO_RDS_US,
+ true,
+ true,
+ }
+ }
+ },
+ { // AM LW
+ RADIO_REGION_ITU_1,
+ {
+ RADIO_BAND_AM,
+ false,
+ RADIO_BAND_LOWER_LW,
+ RADIO_BAND_UPPER_LW,
+ 1,
+ {RADIO_BAND_SPACING_LW},
+ {
+ }
+ }
+ },
+ { // AM SW
+ RADIO_REGION_ITU_1,
+ {
+ RADIO_BAND_AM,
+ false,
+ RADIO_BAND_LOWER_SW,
+ RADIO_BAND_UPPER_SW,
+ 1,
+ {RADIO_BAND_SPACING_SW},
+ {
+ }
+ }
+ },
+ { // AM MW ITU1
+ RADIO_REGION_ITU_1,
+ {
+ RADIO_BAND_AM,
+ false,
+ RADIO_BAND_LOWER_MW_IUT1,
+ RADIO_BAND_UPPER_MW_ITU1,
+ 1,
+ {RADIO_BAND_SPACING_MW_ITU1},
+ {
+ }
+ }
+ },
+ { // AM MW ITU2
+ RADIO_REGION_ITU_2,
+ {
+ RADIO_BAND_AM,
+ false,
+ RADIO_BAND_LOWER_MW_IUT2,
+ RADIO_BAND_UPPER_MW_ITU2,
+ 1,
+ {RADIO_BAND_SPACING_MW_ITU2},
+ {
+ }
+ }
+ }
+};
+
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_RADIO_REGIONS_H
diff --git a/services/radio/RadioService.cpp b/services/radio/RadioService.cpp
new file mode 100644
index 0000000..a6c2bdf
--- /dev/null
+++ b/services/radio/RadioService.cpp
@@ -0,0 +1,901 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#define LOG_TAG "RadioService"
+//#define LOG_NDEBUG 0
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pthread.h>
+
+#include <system/audio.h>
+#include <system/audio_policy.h>
+#include <system/radio.h>
+#include <system/radio_metadata.h>
+#include <cutils/atomic.h>
+#include <cutils/properties.h>
+#include <hardware/hardware.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <hardware/radio.h>
+#include <media/AudioSystem.h>
+#include "RadioService.h"
+#include "RadioRegions.h"
+
+namespace android {
+
+static const char kRadioTunerAudioDeviceName[] = "Radio tuner source";
+
+RadioService::RadioService()
+ : BnRadioService(), mNextUniqueId(1)
+{
+ ALOGI("%s", __FUNCTION__);
+}
+
+void RadioService::onFirstRef()
+{
+ const hw_module_t *mod;
+ int rc;
+ struct radio_hw_device *dev;
+
+ ALOGI("%s", __FUNCTION__);
+
+ rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, RADIO_HARDWARE_MODULE_ID_FM, &mod);
+ if (rc != 0) {
+ ALOGE("couldn't load radio module %s.%s (%s)",
+ RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
+ return;
+ }
+ rc = radio_hw_device_open(mod, &dev);
+ if (rc != 0) {
+ ALOGE("couldn't open radio hw device in %s.%s (%s)",
+ RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
+ return;
+ }
+ if (dev->common.version != RADIO_DEVICE_API_VERSION_CURRENT) {
+ ALOGE("wrong radio hw device version %04x", dev->common.version);
+ return;
+ }
+
+ struct radio_hal_properties halProperties;
+ rc = dev->get_properties(dev, &halProperties);
+ if (rc != 0) {
+ ALOGE("could not read implementation properties");
+ return;
+ }
+
+ radio_properties_t properties;
+ properties.handle =
+ (radio_handle_t)android_atomic_inc(&mNextUniqueId);
+
+ ALOGI("loaded default module %s, handle %d", properties.product, properties.handle);
+
+ convertProperties(&properties, &halProperties);
+ sp<Module> module = new Module(dev, properties);
+ mModules.add(properties.handle, module);
+}
+
+RadioService::~RadioService()
+{
+ for (size_t i = 0; i < mModules.size(); i++) {
+ radio_hw_device_close(mModules.valueAt(i)->hwDevice());
+ }
+}
+
+status_t RadioService::listModules(struct radio_properties *properties,
+ uint32_t *numModules)
+{
+ ALOGV("listModules");
+
+ AutoMutex lock(mServiceLock);
+ if (numModules == NULL || (*numModules != 0 && properties == NULL)) {
+ return BAD_VALUE;
+ }
+ size_t maxModules = *numModules;
+ *numModules = mModules.size();
+ for (size_t i = 0; i < mModules.size() && i < maxModules; i++) {
+ properties[i] = mModules.valueAt(i)->properties();
+ }
+ return NO_ERROR;
+}
+
+status_t RadioService::attach(radio_handle_t handle,
+ const sp<IRadioClient>& client,
+ const struct radio_band_config *config,
+ bool withAudio,
+ sp<IRadio>& radio)
+{
+ ALOGV("%s %d config %p withAudio %d", __FUNCTION__, handle, config, withAudio);
+
+ AutoMutex lock(mServiceLock);
+ radio.clear();
+ if (client == 0) {
+ return BAD_VALUE;
+ }
+ ssize_t index = mModules.indexOfKey(handle);
+ if (index < 0) {
+ return BAD_VALUE;
+ }
+ sp<Module> module = mModules.valueAt(index);
+
+ if (config == NULL) {
+ config = module->getDefaultConfig();
+ if (config == NULL) {
+ return INVALID_OPERATION;
+ }
+ }
+ ALOGV("%s region %d type %d", __FUNCTION__, config->region, config->band.type);
+
+ radio = module->addClient(client, config, withAudio);
+
+ if (radio == 0) {
+ NO_INIT;
+ }
+ return NO_ERROR;
+}
+
+
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleep = 60000;
+
+static bool tryLock(Mutex& mutex)
+{
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mutex.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleep);
+ }
+ return locked;
+}
+
+status_t RadioService::dump(int fd, const Vector<String16>& args __unused) {
+ String8 result;
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ result.appendFormat("Permission Denial: can't dump RadioService");
+ write(fd, result.string(), result.size());
+ } else {
+ bool locked = tryLock(mServiceLock);
+ // failed to lock - RadioService is probably deadlocked
+ if (!locked) {
+ result.append("RadioService may be deadlocked\n");
+ write(fd, result.string(), result.size());
+ }
+
+ if (locked) mServiceLock.unlock();
+ }
+ return NO_ERROR;
+}
+
+status_t RadioService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ return BnRadioService::onTransact(code, data, reply, flags);
+}
+
+
+// static
+void RadioService::callback(radio_hal_event_t *halEvent, void *cookie)
+{
+ CallbackThread *callbackThread = (CallbackThread *)cookie;
+ if (callbackThread == NULL) {
+ return;
+ }
+ callbackThread->sendEvent(halEvent);
+}
+
+/* static */
+void RadioService::convertProperties(radio_properties_t *properties,
+ const radio_hal_properties_t *halProperties)
+{
+ memset(properties, 0, sizeof(struct radio_properties));
+ properties->class_id = halProperties->class_id;
+ strlcpy(properties->implementor, halProperties->implementor,
+ RADIO_STRING_LEN_MAX);
+ strlcpy(properties->product, halProperties->product,
+ RADIO_STRING_LEN_MAX);
+ strlcpy(properties->version, halProperties->version,
+ RADIO_STRING_LEN_MAX);
+ strlcpy(properties->serial, halProperties->serial,
+ RADIO_STRING_LEN_MAX);
+ properties->num_tuners = halProperties->num_tuners;
+ properties->num_audio_sources = halProperties->num_audio_sources;
+ properties->supports_capture = halProperties->supports_capture;
+
+ for (size_t i = 0; i < ARRAY_SIZE(sKnownRegionConfigs); i++) {
+ const radio_hal_band_config_t *band = &sKnownRegionConfigs[i].band;
+ size_t j;
+ for (j = 0; j < halProperties->num_bands; j++) {
+ const radio_hal_band_config_t *halBand = &halProperties->bands[j];
+ size_t k;
+ if (band->type != halBand->type) continue;
+ if (band->lower_limit < halBand->lower_limit) continue;
+ if (band->upper_limit > halBand->upper_limit) continue;
+ for (k = 0; k < halBand->num_spacings; k++) {
+ if (band->spacings[0] == halBand->spacings[k]) break;
+ }
+ if (k == halBand->num_spacings) continue;
+ if (band->type == RADIO_BAND_AM) break;
+ if ((band->fm.deemphasis & halBand->fm.deemphasis) == 0) continue;
+ if (halBand->fm.rds == 0) break;
+ if ((band->fm.rds & halBand->fm.rds) != 0) break;
+ }
+ if (j == halProperties->num_bands) continue;
+
+ ALOGI("convertProperties() Adding band type %d region %d",
+ sKnownRegionConfigs[i].band.type , sKnownRegionConfigs[i].region);
+
+ memcpy(&properties->bands[properties->num_bands++],
+ &sKnownRegionConfigs[i],
+ sizeof(radio_band_config_t));
+ }
+}
+
+#undef LOG_TAG
+#define LOG_TAG "RadioService::CallbackThread"
+
+RadioService::CallbackThread::CallbackThread(const wp<ModuleClient>& moduleClient)
+ : mModuleClient(moduleClient), mMemoryDealer(new MemoryDealer(1024 * 1024, "RadioService"))
+{
+}
+
+RadioService::CallbackThread::~CallbackThread()
+{
+ mEventQueue.clear();
+}
+
+void RadioService::CallbackThread::onFirstRef()
+{
+ run("RadioService cbk", ANDROID_PRIORITY_URGENT_AUDIO);
+}
+
+bool RadioService::CallbackThread::threadLoop()
+{
+ while (!exitPending()) {
+ sp<IMemory> eventMemory;
+ sp<ModuleClient> moduleClient;
+ {
+ Mutex::Autolock _l(mCallbackLock);
+ while (mEventQueue.isEmpty() && !exitPending()) {
+ ALOGV("CallbackThread::threadLoop() sleep");
+ mCallbackCond.wait(mCallbackLock);
+ ALOGV("CallbackThread::threadLoop() wake up");
+ }
+ if (exitPending()) {
+ break;
+ }
+ eventMemory = mEventQueue[0];
+ mEventQueue.removeAt(0);
+ moduleClient = mModuleClient.promote();
+ }
+ if (moduleClient != 0) {
+ moduleClient->onCallbackEvent(eventMemory);
+ eventMemory.clear();
+ }
+ }
+ return false;
+}
+
+void RadioService::CallbackThread::exit()
+{
+ Mutex::Autolock _l(mCallbackLock);
+ requestExit();
+ mCallbackCond.broadcast();
+}
+
+sp<IMemory> RadioService::CallbackThread::prepareEvent(radio_hal_event_t *halEvent)
+{
+ sp<IMemory> eventMemory;
+
+ size_t headerSize =
+ (sizeof(struct radio_event) + sizeof(unsigned int) - 1) /sizeof(unsigned int);
+ size_t metadataSize = 0;
+ switch (halEvent->type) {
+ case RADIO_EVENT_TUNED:
+ case RADIO_EVENT_AF_SWITCH:
+ if (radio_metadata_check(halEvent->info.metadata) == 0) {
+ metadataSize = radio_metadata_get_size(halEvent->info.metadata);
+ }
+ break;
+ case RADIO_EVENT_METADATA:
+ if (radio_metadata_check(halEvent->metadata) != 0) {
+ return eventMemory;
+ }
+ metadataSize = radio_metadata_get_size(halEvent->metadata);
+ break;
+ default:
+ break;
+ }
+ size_t size = headerSize + metadataSize;
+ eventMemory = mMemoryDealer->allocate(size);
+ if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ eventMemory.clear();
+ return eventMemory;
+ }
+ struct radio_event *event = (struct radio_event *)eventMemory->pointer();
+ event->type = halEvent->type;
+ event->status = halEvent->status;
+
+ switch (event->type) {
+ case RADIO_EVENT_CONFIG:
+ event->config.band = halEvent->config;
+ break;
+ case RADIO_EVENT_TUNED:
+ case RADIO_EVENT_AF_SWITCH:
+ event->info = halEvent->info;
+ if (metadataSize != 0) {
+ memcpy((char *)event + headerSize, halEvent->info.metadata, metadataSize);
+ // replace meta data pointer by offset while in shared memory so that receiving side
+ // can restore the pointer in destination process.
+ event->info.metadata = (radio_metadata_t *)headerSize;
+ }
+ break;
+ case RADIO_EVENT_TA:
+ case RADIO_EVENT_ANTENNA:
+ case RADIO_EVENT_CONTROL:
+ event->on = halEvent->on;
+ break;
+ case RADIO_EVENT_METADATA:
+ memcpy((char *)event + headerSize, halEvent->metadata, metadataSize);
+ // replace meta data pointer by offset while in shared memory so that receiving side
+ // can restore the pointer in destination process.
+ event->metadata = (radio_metadata_t *)headerSize;
+ break;
+ case RADIO_EVENT_HW_FAILURE:
+ default:
+ break;
+ }
+
+ return eventMemory;
+}
+
+void RadioService::CallbackThread::sendEvent(radio_hal_event_t *event)
+ {
+ sp<IMemory> eventMemory = prepareEvent(event);
+ if (eventMemory == 0) {
+ return;
+ }
+
+ AutoMutex lock(mCallbackLock);
+ mEventQueue.add(eventMemory);
+ mCallbackCond.signal();
+ ALOGV("%s DONE", __FUNCTION__);
+}
+
+
+#undef LOG_TAG
+#define LOG_TAG "RadioService::Module"
+
+RadioService::Module::Module(radio_hw_device* hwDevice, radio_properties properties)
+ : mHwDevice(hwDevice), mProperties(properties), mMute(true)
+{
+}
+
+RadioService::Module::~Module() {
+ mModuleClients.clear();
+}
+
+status_t RadioService::Module::dump(int fd __unused, const Vector<String16>& args __unused) {
+ String8 result;
+ return NO_ERROR;
+}
+
+sp<RadioService::ModuleClient> RadioService::Module::addClient(const sp<IRadioClient>& client,
+ const struct radio_band_config *config,
+ bool audio)
+{
+ ALOGV("addClient() %p config %p product %s", this, config, mProperties.product);
+ AutoMutex lock(mLock);
+ sp<ModuleClient> moduleClient;
+ int ret;
+
+ for (size_t i = 0; i < mModuleClients.size(); i++) {
+ if (mModuleClients[i]->client() == client) {
+ // client already connected: reject
+ return moduleClient;
+ }
+ }
+ moduleClient = new ModuleClient(this, client, config, audio);
+
+ struct radio_hal_band_config halConfig;
+ halConfig = config->band;
+
+ // Tuner preemption logic:
+ // There is a limited amount of tuners and a limited amount of radio audio sources per module.
+ // The minimum is one tuner and one audio source.
+ // The numbers of tuners and sources are indicated in the module properties.
+ // NOTE: current framework implementation only supports one radio audio source.
+ // It is possible to open more than one tuner at a time but only one tuner can be connected
+ // to the radio audio source (AUDIO_DEVICE_IN_FM_TUNER).
+ // The base rule is that a newly connected tuner always wins, i.e. always gets a tuner
+ // and can use the audio source if requested.
+ // If another client is preempted, it is notified by a callback with RADIO_EVENT_CONTROL
+ // indicating loss of control.
+ // - If the newly connected client requests the audio source (audio == true):
+ // - if an audio source is available
+ // no problem
+ // - if not:
+ // the oldest client in the list using audio is preempted.
+ // - If the newly connected client does not request the audio source (audio == false):
+ // - if a tuner is available
+ // no problem
+ // - if not:
+ // The oldest client not using audio is preempted first and if none is found the
+ // the oldest client using audio is preempted.
+ // Each time a tuner using the audio source is opened or closed, the audio policy manager is
+ // notified of the connection or disconnection of AUDIO_DEVICE_IN_FM_TUNER.
+
+ sp<ModuleClient> oldestTuner;
+ sp<ModuleClient> oldestAudio;
+ size_t allocatedTuners = 0;
+ size_t allocatedAudio = 0;
+ for (size_t i = 0; i < mModuleClients.size(); i++) {
+ if (mModuleClients[i]->getTuner() != NULL) {
+ if (mModuleClients[i]->audio()) {
+ if (oldestAudio == 0) {
+ oldestAudio = mModuleClients[i];
+ }
+ allocatedAudio++;
+ } else {
+ if (oldestTuner == 0) {
+ oldestTuner = mModuleClients[i];
+ }
+ allocatedTuners++;
+ }
+ }
+ }
+
+ const struct radio_tuner *halTuner;
+ sp<ModuleClient> preemtedClient;
+ if (audio) {
+ if (allocatedAudio >= mProperties.num_audio_sources) {
+ ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch");
+ preemtedClient = oldestAudio;
+ }
+ } else {
+ if (allocatedAudio + allocatedTuners >= mProperties.num_tuners) {
+ if (allocatedTuners != 0) {
+ ALOG_ASSERT(oldestTuner != 0, "addClient() allocatedTuners/oldestTuner mismatch");
+ preemtedClient = oldestTuner;
+ } else {
+ ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch");
+ preemtedClient = oldestAudio;
+ }
+ }
+ }
+ if (preemtedClient != 0) {
+ halTuner = preemtedClient->getTuner();
+ preemtedClient->setTuner(NULL);
+ mHwDevice->close_tuner(mHwDevice, halTuner);
+ if (preemtedClient->audio()) {
+ notifyDeviceConnection(false, "");
+ }
+ }
+
+ ret = mHwDevice->open_tuner(mHwDevice, &halConfig, audio,
+ RadioService::callback, moduleClient->callbackThread().get(),
+ &halTuner);
+ if (ret == 0) {
+ ALOGV("addClient() setTuner %p", halTuner);
+ moduleClient->setTuner(halTuner);
+ mModuleClients.add(moduleClient);
+ if (audio) {
+ notifyDeviceConnection(true, "");
+ }
+ } else {
+ moduleClient.clear();
+ }
+
+
+ ALOGV("addClient() DONE moduleClient %p", moduleClient.get());
+
+ return moduleClient;
+}
+
+void RadioService::Module::removeClient(const sp<ModuleClient>& moduleClient) {
+ ALOGV("removeClient()");
+ AutoMutex lock(mLock);
+ int ret;
+ ssize_t index = -1;
+
+ for (size_t i = 0; i < mModuleClients.size(); i++) {
+ if (mModuleClients[i] == moduleClient) {
+ index = i;
+ break;
+ }
+ }
+ if (index == -1) {
+ return;
+ }
+
+ mModuleClients.removeAt(index);
+ const struct radio_tuner *halTuner = moduleClient->getTuner();
+ if (halTuner == NULL) {
+ return;
+ }
+
+ mHwDevice->close_tuner(mHwDevice, halTuner);
+ if (moduleClient->audio()) {
+ notifyDeviceConnection(false, "");
+ }
+
+ mMute = true;
+
+ if (mModuleClients.isEmpty()) {
+ return;
+ }
+
+ // Tuner reallocation logic:
+ // When a client is removed and was controlling a tuner, this tuner will be allocated to a
+ // previously preempted client. This client will be notified by a callback with
+ // RADIO_EVENT_CONTROL indicating gain of control.
+ // - If a preempted client is waiting for an audio source and one becomes available:
+ // Allocate the tuner to the most recently added client waiting for an audio source
+ // - If not:
+ // Allocate the tuner to the most recently added client.
+ // Each time a tuner using the audio source is opened or closed, the audio policy manager is
+ // notified of the connection or disconnection of AUDIO_DEVICE_IN_FM_TUNER.
+
+ sp<ModuleClient> youngestClient;
+ sp<ModuleClient> youngestClientAudio;
+ size_t allocatedTuners = 0;
+ size_t allocatedAudio = 0;
+ for (ssize_t i = mModuleClients.size() - 1; i >= 0; i--) {
+ if (mModuleClients[i]->getTuner() == NULL) {
+ if (mModuleClients[i]->audio()) {
+ if (youngestClientAudio == 0) {
+ youngestClientAudio = mModuleClients[i];
+ }
+ } else {
+ if (youngestClient == 0) {
+ youngestClient = mModuleClients[i];
+ }
+ }
+ } else {
+ if (mModuleClients[i]->audio()) {
+ allocatedAudio++;
+ } else {
+ allocatedTuners++;
+ }
+ }
+ }
+
+ ALOG_ASSERT(allocatedTuners + allocatedAudio < mProperties.num_tuners,
+ "removeClient() removed client but no tuner available");
+
+ ALOG_ASSERT(!moduleClient->audio() || allocatedAudio < mProperties.num_audio_sources,
+ "removeClient() removed audio client but no tuner with audio available");
+
+ if (allocatedAudio < mProperties.num_audio_sources && youngestClientAudio != 0) {
+ youngestClient = youngestClientAudio;
+ }
+
+ ALOG_ASSERT(youngestClient != 0, "removeClient() removed client no candidate found for tuner");
+
+ struct radio_hal_band_config halConfig = youngestClient->halConfig();
+ ret = mHwDevice->open_tuner(mHwDevice, &halConfig, youngestClient->audio(),
+ RadioService::callback, moduleClient->callbackThread().get(),
+ &halTuner);
+
+ if (ret == 0) {
+ youngestClient->setTuner(halTuner);
+ if (youngestClient->audio()) {
+ notifyDeviceConnection(true, "");
+ }
+ }
+}
+
+status_t RadioService::Module::setMute(bool mute)
+{
+ Mutex::Autolock _l(mLock);
+ if (mute != mMute) {
+ mMute = mute;
+ //TODO notifify audio policy manager of media activity on radio audio device
+ }
+ return NO_ERROR;
+}
+
+status_t RadioService::Module::getMute(bool *mute)
+{
+ Mutex::Autolock _l(mLock);
+ *mute = mMute;
+ return NO_ERROR;
+}
+
+
+const struct radio_band_config *RadioService::Module::getDefaultConfig() const
+{
+ if (mProperties.num_bands == 0) {
+ return NULL;
+ }
+ return &mProperties.bands[0];
+}
+
+void RadioService::Module::notifyDeviceConnection(bool connected,
+ const char *address) {
+ int64_t token = IPCThreadState::self()->clearCallingIdentity();
+ AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_FM_TUNER,
+ connected ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE :
+ AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ address, kRadioTunerAudioDeviceName);
+ IPCThreadState::self()->restoreCallingIdentity(token);
+}
+
+#undef LOG_TAG
+#define LOG_TAG "RadioService::ModuleClient"
+
+RadioService::ModuleClient::ModuleClient(const sp<Module>& module,
+ const sp<IRadioClient>& client,
+ const struct radio_band_config *config,
+ bool audio)
+ : mModule(module), mClient(client), mConfig(*config), mAudio(audio), mTuner(NULL)
+{
+}
+
+void RadioService::ModuleClient::onFirstRef()
+{
+ mCallbackThread = new CallbackThread(this);
+ IInterface::asBinder(mClient)->linkToDeath(this);
+}
+
+RadioService::ModuleClient::~ModuleClient() {
+ if (mClient != 0) {
+ IInterface::asBinder(mClient)->unlinkToDeath(this);
+ mClient.clear();
+ }
+ if (mCallbackThread != 0) {
+ mCallbackThread->exit();
+ }
+}
+
+status_t RadioService::ModuleClient::dump(int fd __unused,
+ const Vector<String16>& args __unused) {
+ String8 result;
+ return NO_ERROR;
+}
+
+void RadioService::ModuleClient::detach() {
+ ALOGV("%s", __FUNCTION__);
+ sp<ModuleClient> strongMe = this;
+ {
+ AutoMutex lock(mLock);
+ if (mClient != 0) {
+ IInterface::asBinder(mClient)->unlinkToDeath(this);
+ mClient.clear();
+ }
+ }
+ sp<Module> module = mModule.promote();
+ if (module == 0) {
+ return;
+ }
+ module->removeClient(this);
+}
+
+radio_hal_band_config_t RadioService::ModuleClient::halConfig() const
+{
+ AutoMutex lock(mLock);
+ ALOGV("%s locked", __FUNCTION__);
+ return mConfig.band;
+}
+
+const struct radio_tuner *RadioService::ModuleClient::getTuner() const
+{
+ AutoMutex lock(mLock);
+ ALOGV("%s locked", __FUNCTION__);
+ return mTuner;
+}
+
+void RadioService::ModuleClient::setTuner(const struct radio_tuner *tuner)
+{
+ ALOGV("%s %p", __FUNCTION__, this);
+
+ AutoMutex lock(mLock);
+ mTuner = tuner;
+ ALOGV("%s locked", __FUNCTION__);
+
+ radio_hal_event_t event;
+ event.type = RADIO_EVENT_CONTROL;
+ event.status = 0;
+ event.on = mTuner != NULL;
+ mCallbackThread->sendEvent(&event);
+ ALOGV("%s DONE", __FUNCTION__);
+
+}
+
+status_t RadioService::ModuleClient::setConfiguration(const struct radio_band_config *config)
+{
+ AutoMutex lock(mLock);
+ status_t status = NO_ERROR;
+ ALOGV("%s locked", __FUNCTION__);
+
+ if (mTuner != NULL) {
+ struct radio_hal_band_config halConfig;
+ halConfig = config->band;
+ status = (status_t)mTuner->set_configuration(mTuner, &halConfig);
+ if (status == NO_ERROR) {
+ mConfig = *config;
+ }
+ } else {
+ mConfig = *config;
+ status == INVALID_OPERATION;
+ }
+
+ return status;
+}
+
+status_t RadioService::ModuleClient::getConfiguration(struct radio_band_config *config)
+{
+ AutoMutex lock(mLock);
+ status_t status = NO_ERROR;
+ ALOGV("%s locked", __FUNCTION__);
+
+ if (mTuner != NULL) {
+ struct radio_hal_band_config halConfig;
+ status = (status_t)mTuner->get_configuration(mTuner, &halConfig);
+ if (status == NO_ERROR) {
+ mConfig.band = halConfig;
+ }
+ }
+ *config = mConfig;
+
+ return status;
+}
+
+status_t RadioService::ModuleClient::setMute(bool mute)
+{
+ sp<Module> module;
+ {
+ Mutex::Autolock _l(mLock);
+ ALOGV("%s locked", __FUNCTION__);
+ if (mTuner == NULL || !mAudio) {
+ return INVALID_OPERATION;
+ }
+ module = mModule.promote();
+ if (module == 0) {
+ return NO_INIT;
+ }
+ }
+ module->setMute(mute);
+ return NO_ERROR;
+}
+
+status_t RadioService::ModuleClient::getMute(bool *mute)
+{
+ sp<Module> module;
+ {
+ Mutex::Autolock _l(mLock);
+ ALOGV("%s locked", __FUNCTION__);
+ module = mModule.promote();
+ if (module == 0) {
+ return NO_INIT;
+ }
+ }
+ return module->getMute(mute);
+}
+
+status_t RadioService::ModuleClient::scan(radio_direction_t direction, bool skipSubChannel)
+{
+ AutoMutex lock(mLock);
+ ALOGV("%s locked", __FUNCTION__);
+ status_t status;
+ if (mTuner != NULL) {
+ status = (status_t)mTuner->scan(mTuner, direction, skipSubChannel);
+ } else {
+ status = INVALID_OPERATION;
+ }
+ return status;
+}
+
+status_t RadioService::ModuleClient::step(radio_direction_t direction, bool skipSubChannel)
+{
+ AutoMutex lock(mLock);
+ ALOGV("%s locked", __FUNCTION__);
+ status_t status;
+ if (mTuner != NULL) {
+ status = (status_t)mTuner->step(mTuner, direction, skipSubChannel);
+ } else {
+ status = INVALID_OPERATION;
+ }
+ return status;
+}
+
+status_t RadioService::ModuleClient::tune(unsigned int channel, unsigned int subChannel)
+{
+ AutoMutex lock(mLock);
+ ALOGV("%s locked", __FUNCTION__);
+ status_t status;
+ if (mTuner != NULL) {
+ status = (status_t)mTuner->tune(mTuner, channel, subChannel);
+ } else {
+ status = INVALID_OPERATION;
+ }
+ return status;
+}
+
+status_t RadioService::ModuleClient::cancel()
+{
+ AutoMutex lock(mLock);
+ ALOGV("%s locked", __FUNCTION__);
+ status_t status;
+ if (mTuner != NULL) {
+ status = (status_t)mTuner->cancel(mTuner);
+ } else {
+ status = INVALID_OPERATION;
+ }
+ return status;
+}
+
+status_t RadioService::ModuleClient::getProgramInformation(struct radio_program_info *info)
+{
+ AutoMutex lock(mLock);
+ ALOGV("%s locked", __FUNCTION__);
+ status_t status;
+ if (mTuner != NULL) {
+ status = (status_t)mTuner->get_program_information(mTuner, info);
+ } else {
+ status = INVALID_OPERATION;
+ }
+ return status;
+}
+
+status_t RadioService::ModuleClient::hasControl(bool *hasControl)
+{
+ Mutex::Autolock lock(mLock);
+ ALOGV("%s locked", __FUNCTION__);
+ *hasControl = mTuner != NULL;
+ return NO_ERROR;
+}
+
+void RadioService::ModuleClient::onCallbackEvent(const sp<IMemory>& eventMemory)
+{
+ if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ return;
+ }
+
+ sp<IRadioClient> client;
+ {
+ AutoMutex lock(mLock);
+ ALOGV("%s locked", __FUNCTION__);
+ radio_event_t *event = (radio_event_t *)eventMemory->pointer();
+ switch (event->type) {
+ case RADIO_EVENT_CONFIG:
+ mConfig.band = event->config.band;
+ event->config.region = mConfig.region;
+ break;
+ default:
+ break;
+ }
+
+ client = mClient;
+ }
+ if (client != 0) {
+ client->onEvent(eventMemory);
+ }
+}
+
+
+void RadioService::ModuleClient::binderDied(
+ const wp<IBinder> &who __unused) {
+ ALOGW("client binder died for client %p", this);
+ detach();
+}
+
+}; // namespace android
diff --git a/services/radio/RadioService.h b/services/radio/RadioService.h
new file mode 100644
index 0000000..49feda6
--- /dev/null
+++ b/services/radio/RadioService.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_HARDWARE_RADIO_SERVICE_H
+#define ANDROID_HARDWARE_RADIO_SERVICE_H
+
+#include <utils/Vector.h>
+//#include <binder/AppOpsManager.h>
+#include <binder/MemoryDealer.h>
+#include <binder/BinderService.h>
+#include <binder/IAppOpsCallback.h>
+#include <radio/IRadioService.h>
+#include <radio/IRadio.h>
+#include <radio/IRadioClient.h>
+#include <system/radio.h>
+#include <hardware/radio.h>
+
+namespace android {
+
+class MemoryHeapBase;
+
+class RadioService :
+ public BinderService<RadioService>,
+ public BnRadioService
+{
+ friend class BinderService<RadioService>;
+
+public:
+ class ModuleClient;
+ class Module;
+
+ static char const* getServiceName() { return "media.radio"; }
+
+ RadioService();
+ virtual ~RadioService();
+
+ // IRadioService
+ virtual status_t listModules(struct radio_properties *properties,
+ uint32_t *numModules);
+
+ virtual status_t attach(radio_handle_t handle,
+ const sp<IRadioClient>& client,
+ const struct radio_band_config *config,
+ bool withAudio,
+ sp<IRadio>& radio);
+
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+
+ class Module : public virtual RefBase {
+ public:
+
+ Module(radio_hw_device* hwDevice,
+ struct radio_properties properties);
+
+ virtual ~Module();
+
+ sp<ModuleClient> addClient(const sp<IRadioClient>& client,
+ const struct radio_band_config *config,
+ bool audio);
+
+ void removeClient(const sp<ModuleClient>& moduleClient);
+
+ status_t setMute(bool mute);
+
+ status_t getMute(bool *mute);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ const struct radio_hw_device *hwDevice() const { return mHwDevice; }
+ const struct radio_properties properties() const { return mProperties; }
+ const struct radio_band_config *getDefaultConfig() const ;
+
+ private:
+
+ void notifyDeviceConnection(bool connected, const char *address);
+
+ Mutex mLock; // protects mModuleClients
+ const struct radio_hw_device *mHwDevice; // HAL hardware device
+ const struct radio_properties mProperties; // cached hardware module properties
+ Vector< sp<ModuleClient> > mModuleClients; // list of attached clients
+ bool mMute; // radio audio source state
+ // when unmuted, audio is routed to the
+ // output device selected for media use case.
+ }; // class Module
+
+ class CallbackThread : public Thread {
+ public:
+
+ CallbackThread(const wp<ModuleClient>& moduleClient);
+
+ virtual ~CallbackThread();
+
+
+ // Thread virtuals
+ virtual bool threadLoop();
+
+ // RefBase
+ virtual void onFirstRef();
+
+ void exit();
+
+ void sendEvent(radio_hal_event_t *halEvent);
+ sp<IMemory> prepareEvent(radio_hal_event_t *halEvent);
+
+ private:
+ wp<ModuleClient> mModuleClient; // client module the thread belongs to
+ Condition mCallbackCond; // condition signaled when a new event is posted
+ Mutex mCallbackLock; // protects mEventQueue
+ Vector< sp<IMemory> > mEventQueue; // pending callback events
+ sp<MemoryDealer> mMemoryDealer; // shared memory for callback event
+ }; // class CallbackThread
+
+ class ModuleClient : public BnRadio,
+ public IBinder::DeathRecipient {
+ public:
+
+ ModuleClient(const sp<Module>& module,
+ const sp<IRadioClient>& client,
+ const struct radio_band_config *config,
+ bool audio);
+
+ virtual ~ModuleClient();
+
+ // IRadio
+ virtual void detach();
+
+ virtual status_t setConfiguration(const struct radio_band_config *config);
+
+ virtual status_t getConfiguration(struct radio_band_config *config);
+
+ virtual status_t setMute(bool mute);
+
+ virtual status_t getMute(bool *mute);
+
+ virtual status_t scan(radio_direction_t direction, bool skipSubChannel);
+
+ virtual status_t step(radio_direction_t direction, bool skipSubChannel);
+
+ virtual status_t tune(unsigned int channel, unsigned int subChannel);
+
+ virtual status_t cancel();
+
+ virtual status_t getProgramInformation(struct radio_program_info *info);
+
+ virtual status_t hasControl(bool *hasControl);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ sp<IRadioClient> client() const { return mClient; }
+ wp<Module> module() const { return mModule; }
+ radio_hal_band_config_t halConfig() const;
+ sp<CallbackThread> callbackThread() const { return mCallbackThread; }
+ void setTuner(const struct radio_tuner *tuner);
+ const struct radio_tuner *getTuner() const;
+ bool audio() const { return mAudio; }
+
+ void onCallbackEvent(const sp<IMemory>& event);
+
+ virtual void onFirstRef();
+
+
+ // IBinder::DeathRecipient implementation
+ virtual void binderDied(const wp<IBinder> &who);
+
+ private:
+
+ mutable Mutex mLock; // protects mClient, mConfig and mTuner
+ wp<Module> mModule; // The module this client is attached to
+ sp<IRadioClient> mClient; // event callback binder interface
+ radio_band_config_t mConfig; // current band configuration
+ sp<CallbackThread> mCallbackThread; // event callback thread
+ const bool mAudio;
+ const struct radio_tuner *mTuner; // HAL tuner interface. NULL indicates that
+ // this client does not have control on any
+ // tuner
+ }; // class ModuleClient
+
+
+ static void callback(radio_hal_event_t *halEvent, void *cookie);
+
+private:
+
+ virtual void onFirstRef();
+
+ static void convertProperties(radio_properties_t *properties,
+ const radio_hal_properties_t *halProperties);
+ Mutex mServiceLock; // protects mModules
+ volatile int32_t mNextUniqueId; // for module ID allocation
+ DefaultKeyedVector< radio_handle_t, sp<Module> > mModules;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_RADIO_SERVICE_H