Merge "Add unique audio port IDs to AudioTrack and AudioRecord"
diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp
index 68e73f2..ece64fd 100644
--- a/camera/CameraBase.cpp
+++ b/camera/CameraBase.cpp
@@ -39,7 +39,7 @@
namespace hardware {
-status_t CameraInfo::writeToParcel(Parcel* parcel) const {
+status_t CameraInfo::writeToParcel(android::Parcel* parcel) const {
status_t res;
res = parcel->writeInt32(facing);
if (res != OK) return res;
@@ -47,7 +47,7 @@
return res;
}
-status_t CameraInfo::readFromParcel(const Parcel* parcel) {
+status_t CameraInfo::readFromParcel(const android::Parcel* parcel) {
status_t res;
res = parcel->readInt32(&facing);
if (res != OK) return res;
@@ -55,8 +55,24 @@
return res;
}
+status_t CameraStatus::writeToParcel(android::Parcel* parcel) const {
+ status_t res;
+ res = parcel->writeString8(cameraId);
+ if (res != OK) return res;
+ res = parcel->writeInt32(status);
+ return res;
}
+status_t CameraStatus::readFromParcel(const android::Parcel* parcel) {
+ status_t res;
+ res = parcel->readString8(&cameraId);
+ if (res != OK) return res;
+ res = parcel->readInt32(&status);
+ return res;
+}
+
+} // namespace hardware
+
namespace {
sp<::android::hardware::ICameraService> gCameraService;
const int kCameraServicePollDelay = 500000; // 0.5s
@@ -239,24 +255,6 @@
return res.isOk() ? OK : res.serviceSpecificErrorCode();
}
-template <typename TCam, typename TCamTraits>
-status_t CameraBase<TCam, TCamTraits>::addServiceListener(
- const sp<::android::hardware::ICameraServiceListener>& listener) {
- const sp<::android::hardware::ICameraService>& cs = getCameraService();
- if (cs == 0) return UNKNOWN_ERROR;
- binder::Status res = cs->addListener(listener);
- return res.isOk() ? OK : res.serviceSpecificErrorCode();
-}
-
-template <typename TCam, typename TCamTraits>
-status_t CameraBase<TCam, TCamTraits>::removeServiceListener(
- const sp<::android::hardware::ICameraServiceListener>& listener) {
- const sp<::android::hardware::ICameraService>& cs = getCameraService();
- if (cs == 0) return UNKNOWN_ERROR;
- binder::Status res = cs->removeListener(listener);
- return res.isOk() ? OK : res.serviceSpecificErrorCode();
-}
-
template class CameraBase<Camera>;
} // namespace android
diff --git a/camera/CaptureResult.cpp b/camera/CaptureResult.cpp
index 0a447e7..e6c0d00 100644
--- a/camera/CaptureResult.cpp
+++ b/camera/CaptureResult.cpp
@@ -26,7 +26,7 @@
return requestId >= 0;
}
-status_t CaptureResultExtras::readFromParcel(const Parcel *parcel) {
+status_t CaptureResultExtras::readFromParcel(const android::Parcel *parcel) {
if (parcel == NULL) {
ALOGE("%s: Null parcel", __FUNCTION__);
return BAD_VALUE;
@@ -43,7 +43,7 @@
return OK;
}
-status_t CaptureResultExtras::writeToParcel(Parcel *parcel) const {
+status_t CaptureResultExtras::writeToParcel(android::Parcel *parcel) const {
if (parcel == NULL) {
ALOGE("%s: Null parcel", __FUNCTION__);
return BAD_VALUE;
@@ -69,7 +69,7 @@
mMetadata = otherResult.mMetadata;
}
-status_t CaptureResult::readFromParcel(Parcel *parcel) {
+status_t CaptureResult::readFromParcel(android::Parcel *parcel) {
ALOGV("%s: parcel = %p", __FUNCTION__, parcel);
@@ -100,7 +100,7 @@
return OK;
}
-status_t CaptureResult::writeToParcel(Parcel *parcel) const {
+status_t CaptureResult::writeToParcel(android::Parcel *parcel) const {
ALOGV("%s: parcel = %p", __FUNCTION__, parcel);
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index f3b3dbb..ed09b60 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -96,7 +96,7 @@
mVendorOps = src.mVendorOps;
}
-status_t VendorTagDescriptor::readFromParcel(const Parcel* parcel) {
+status_t VendorTagDescriptor::readFromParcel(const android::Parcel* parcel) {
status_t res = OK;
if (parcel == NULL) {
ALOGE("%s: parcel argument was NULL.", __FUNCTION__);
@@ -244,7 +244,7 @@
return mTagToTypeMap.valueFor(tag);
}
-status_t VendorTagDescriptor::writeToParcel(Parcel* parcel) const {
+status_t VendorTagDescriptor::writeToParcel(android::Parcel* parcel) const {
status_t res = OK;
if (parcel == NULL) {
ALOGE("%s: parcel argument was NULL.", __FUNCTION__);
diff --git a/camera/aidl/android/hardware/CameraStatus.aidl b/camera/aidl/android/hardware/CameraStatus.aidl
new file mode 100644
index 0000000..2089b8b
--- /dev/null
+++ b/camera/aidl/android/hardware/CameraStatus.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/** @hide */
+parcelable CameraStatus cpp_header "camera/CameraBase.h";
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index e94fd0c..99c479c 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -24,6 +24,7 @@
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.ICameraServiceListener;
import android.hardware.CameraInfo;
+import android.hardware.CameraStatus;
/**
* Binder interface for the native camera service running in mediaserver.
@@ -83,7 +84,7 @@
* Only supported for device HAL versions >= 3.2
*/
ICameraDeviceUser connectDevice(ICameraDeviceCallbacks callbacks,
- int cameraId,
+ String cameraId,
String opPackageName,
int clientUid);
@@ -102,16 +103,24 @@
int clientUid);
/**
- * Add/remove listeners for changes to camera device and flashlight state
+ * Add listener for changes to camera device and flashlight state.
+ *
+ * Also returns the set of currently-known camera IDs and state of each device.
+ * Adding a listener will trigger the torch status listener to fire for all
+ * devices that have a flash unit
*/
- void addListener(ICameraServiceListener listener);
+ CameraStatus[] addListener(ICameraServiceListener listener);
+
+ /**
+ * Remove listener for changes to camera device and flashlight state.
+ */
void removeListener(ICameraServiceListener listener);
/**
* Read the static camera metadata for a camera device.
* Only supported for device HAL versions >= 3.2
*/
- CameraMetadataNative getCameraCharacteristics(int cameraId);
+ CameraMetadataNative getCameraCharacteristics(String cameraId);
/**
* Read in the vendor tag descriptors from the camera module HAL.
@@ -132,9 +141,9 @@
const int API_VERSION_2 = 2;
// Determines if a particular API version is supported directly
- boolean supportsCameraApi(int cameraId, int apiVersion);
+ boolean supportsCameraApi(String cameraId, int apiVersion);
- void setTorchMode(String CameraId, boolean enabled, IBinder clientBinder);
+ void setTorchMode(String cameraId, boolean enabled, IBinder clientBinder);
/**
* Notify the camera service of a system event. Should only be called from system_server.
diff --git a/camera/aidl/android/hardware/ICameraServiceListener.aidl b/camera/aidl/android/hardware/ICameraServiceListener.aidl
index 4e2a8c7..f871ce4 100644
--- a/camera/aidl/android/hardware/ICameraServiceListener.aidl
+++ b/camera/aidl/android/hardware/ICameraServiceListener.aidl
@@ -40,7 +40,7 @@
*/
// Device physically unplugged
const int STATUS_NOT_PRESENT = 0;
- // Device physically has been plugged in and the camera can be used exlusively
+ // Device physically has been plugged in and the camera can be used exclusively
const int STATUS_PRESENT = 1;
// Device physically has been plugged in but it will not be connect-able until enumeration is
// complete
@@ -51,7 +51,7 @@
// Use to initialize variables only
const int STATUS_UNKNOWN = -1;
- oneway void onStatusChanged(int status, int cameraId);
+ oneway void onStatusChanged(int status, String cameraId);
/**
* The torch mode status of a camera.
diff --git a/camera/camera2/CaptureRequest.cpp b/camera/camera2/CaptureRequest.cpp
index 0d689a6..4daf35b 100644
--- a/camera/camera2/CaptureRequest.cpp
+++ b/camera/camera2/CaptureRequest.cpp
@@ -28,7 +28,7 @@
namespace hardware {
namespace camera2 {
-status_t CaptureRequest::readFromParcel(const Parcel* parcel) {
+status_t CaptureRequest::readFromParcel(const android::Parcel* parcel) {
if (parcel == NULL) {
ALOGE("%s: Null parcel", __FUNCTION__);
return BAD_VALUE;
@@ -90,7 +90,7 @@
return OK;
}
-status_t CaptureRequest::writeToParcel(Parcel* parcel) const {
+status_t CaptureRequest::writeToParcel(android::Parcel* parcel) const {
if (parcel == NULL) {
ALOGE("%s: Null parcel", __FUNCTION__);
return BAD_VALUE;
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
index 38e1c01..12d0da8 100644
--- a/camera/camera2/OutputConfiguration.cpp
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -62,13 +62,13 @@
mHeight(0) {
}
-OutputConfiguration::OutputConfiguration(const Parcel& parcel) :
+OutputConfiguration::OutputConfiguration(const android::Parcel& parcel) :
mRotation(INVALID_ROTATION),
mSurfaceSetID(INVALID_SET_ID) {
readFromParcel(&parcel);
}
-status_t OutputConfiguration::readFromParcel(const Parcel* parcel) {
+status_t OutputConfiguration::readFromParcel(const android::Parcel* parcel) {
status_t err = OK;
int rotation = 0;
@@ -138,7 +138,7 @@
mSurfaceSetID = surfaceSetID;
}
-status_t OutputConfiguration::writeToParcel(Parcel* parcel) const {
+status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const {
if (parcel == nullptr) return BAD_VALUE;
status_t err = OK;
diff --git a/camera/camera2/SubmitInfo.cpp b/camera/camera2/SubmitInfo.cpp
index d739c79..6ebd810 100644
--- a/camera/camera2/SubmitInfo.cpp
+++ b/camera/camera2/SubmitInfo.cpp
@@ -22,7 +22,7 @@
namespace camera2 {
namespace utils {
-status_t SubmitInfo::writeToParcel(Parcel *parcel) const {
+status_t SubmitInfo::writeToParcel(android::Parcel *parcel) const {
status_t res;
if (parcel == nullptr) return BAD_VALUE;
@@ -33,7 +33,7 @@
return res;
}
-status_t SubmitInfo::readFromParcel(const Parcel *parcel) {
+status_t SubmitInfo::readFromParcel(const android::Parcel *parcel) {
status_t res;
if (parcel == nullptr) return BAD_VALUE;
diff --git a/camera/cameraserver/Android.mk b/camera/cameraserver/Android.mk
index 36e3927..8c06833 100644
--- a/camera/cameraserver/Android.mk
+++ b/camera/cameraserver/Android.mk
@@ -24,6 +24,7 @@
liblog \
libutils \
libbinder \
+ android.hardware.camera.common@1.0
LOCAL_MODULE:= cameraserver
LOCAL_32_BIT_ONLY := true
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index 1609da1..f656008 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -28,11 +28,6 @@
using namespace android;
-//constants shared between ACameraManager and CameraManagerGlobal
-namespace {
- const int kMaxCameraIdLen = 32;
-}
-
namespace android {
// Static member definitions
const char* CameraManagerGlobal::kCameraIdKey = "CameraId";
@@ -125,7 +120,11 @@
if (mCameraServiceListener == nullptr) {
mCameraServiceListener = new CameraServiceListener(this);
}
- mCameraService->addListener(mCameraServiceListener);
+ std::vector<hardware::CameraStatus> cameraStatuses{};
+ mCameraService->addListener(mCameraServiceListener, &cameraStatuses);
+ for (auto& c : cameraStatuses) {
+ onStatusChangedLocked(c.status, c.cameraId);
+ }
// setup vendor tags
sp<VendorTagDescriptor> desc = new VendorTagDescriptor();
@@ -157,8 +156,8 @@
sp<CameraManagerGlobal> cm = mCameraManager.promote();
if (cm != nullptr) {
AutoMutex lock(cm->mLock);
- for (auto pair : cm->mDeviceStatusMap) {
- int32_t cameraId = pair.first;
+ for (auto& pair : cm->mDeviceStatusMap) {
+ const String8 &cameraId = pair.first;
cm->onStatusChangedLocked(
CameraServiceListener::STATUS_NOT_PRESENT, cameraId);
}
@@ -174,8 +173,8 @@
auto pair = mCallbacks.insert(cb);
// Send initial callbacks if callback is newly registered
if (pair.second) {
- for (auto pair : mDeviceStatusMap) {
- int32_t cameraId = pair.first;
+ for (auto& pair : mDeviceStatusMap) {
+ const String8& cameraId = pair.first;
int32_t status = pair.second;
sp<AMessage> msg = new AMessage(kWhatSendSingleCallback, mHandler);
@@ -183,7 +182,7 @@
callback->onCameraAvailable : callback->onCameraUnavailable;
msg->setPointer(kCallbackFpKey, (void *) cb);
msg->setPointer(kContextKey, callback->context);
- msg->setInt32(kCameraIdKey, cameraId);
+ msg->setString(kCameraIdKey, AString(cameraId));
msg->post();
}
}
@@ -196,6 +195,26 @@
mCallbacks.erase(cb);
}
+void CameraManagerGlobal::getCameraIdList(std::vector<String8> *cameraIds) {
+ // Ensure that we have initialized/refreshed the list of available devices
+ auto cs = getCameraService();
+ Mutex::Autolock _l(mLock);
+
+ for(auto& deviceStatus : mDeviceStatusMap) {
+ if (deviceStatus.second == hardware::ICameraServiceListener::STATUS_NOT_PRESENT ||
+ deviceStatus.second == hardware::ICameraServiceListener::STATUS_ENUMERATING) {
+ continue;
+ }
+ bool camera2Support = false;
+ binder::Status serviceRet = cs->supportsCameraApi(String16(deviceStatus.first),
+ hardware::ICameraService::API_VERSION_2, &camera2Support);
+ if (!serviceRet.isOk() || !camera2Support) {
+ continue;
+ }
+ cameraIds->push_back(deviceStatus.first);
+ }
+}
+
bool CameraManagerGlobal::validStatus(int32_t status) {
switch (status) {
case hardware::ICameraServiceListener::STATUS_NOT_PRESENT:
@@ -217,14 +236,6 @@
}
}
-void CameraManagerGlobal::CallbackHandler::sendSingleCallback(
- int32_t cameraId, void* context,
- ACameraManager_AvailabilityCallback cb) const {
- char cameraIdStr[kMaxCameraIdLen];
- snprintf(cameraIdStr, sizeof(cameraIdStr), "%d", cameraId);
- (*cb)(context, cameraIdStr);
-}
-
void CameraManagerGlobal::CallbackHandler::onMessageReceived(
const sp<AMessage> &msg) {
switch (msg->what()) {
@@ -232,7 +243,7 @@
{
ACameraManager_AvailabilityCallback cb;
void* context;
- int32_t cameraId;
+ AString cameraId;
bool found = msg->findPointer(kCallbackFpKey, (void**) &cb);
if (!found) {
ALOGE("%s: Cannot find camera callback fp!", __FUNCTION__);
@@ -243,12 +254,12 @@
ALOGE("%s: Cannot find callback context!", __FUNCTION__);
return;
}
- found = msg->findInt32(kCameraIdKey, &cameraId);
+ found = msg->findString(kCameraIdKey, &cameraId);
if (!found) {
ALOGE("%s: Cannot find camera ID!", __FUNCTION__);
return;
}
- sendSingleCallback(cameraId, context, cb);
+ (*cb)(context, cameraId.c_str());
break;
}
default:
@@ -258,10 +269,10 @@
}
binder::Status CameraManagerGlobal::CameraServiceListener::onStatusChanged(
- int32_t status, int32_t cameraId) {
+ int32_t status, const String16& cameraId) {
sp<CameraManagerGlobal> cm = mCameraManager.promote();
if (cm != nullptr) {
- cm->onStatusChanged(status, cameraId);
+ cm->onStatusChanged(status, String8(cameraId));
} else {
ALOGE("Cannot deliver status change. Global camera manager died");
}
@@ -269,40 +280,40 @@
}
void CameraManagerGlobal::onStatusChanged(
- int32_t status, int32_t cameraId) {
+ int32_t status, const String8& cameraId) {
Mutex::Autolock _l(mLock);
onStatusChangedLocked(status, cameraId);
}
void CameraManagerGlobal::onStatusChangedLocked(
- int32_t status, int32_t cameraId) {
- if (!validStatus(status)) {
- ALOGE("%s: Invalid status %d", __FUNCTION__, status);
- return;
- }
+ int32_t status, const String8& cameraId) {
+ if (!validStatus(status)) {
+ ALOGE("%s: Invalid status %d", __FUNCTION__, status);
+ return;
+ }
- bool firstStatus = (mDeviceStatusMap.count(cameraId) == 0);
- int32_t oldStatus = firstStatus ?
- status : // first status
- mDeviceStatusMap[cameraId];
+ bool firstStatus = (mDeviceStatusMap.count(cameraId) == 0);
+ int32_t oldStatus = firstStatus ?
+ status : // first status
+ mDeviceStatusMap[cameraId];
- if (!firstStatus &&
- isStatusAvailable(status) == isStatusAvailable(oldStatus)) {
- // No status update. No need to send callback
- return;
- }
+ if (!firstStatus &&
+ isStatusAvailable(status) == isStatusAvailable(oldStatus)) {
+ // No status update. No need to send callback
+ return;
+ }
- // Iterate through all registered callbacks
- mDeviceStatusMap[cameraId] = status;
- for (auto cb : mCallbacks) {
- sp<AMessage> msg = new AMessage(kWhatSendSingleCallback, mHandler);
- ACameraManager_AvailabilityCallback cbFp = isStatusAvailable(status) ?
- cb.mAvailable : cb.mUnavailable;
- msg->setPointer(kCallbackFpKey, (void *) cbFp);
- msg->setPointer(kContextKey, cb.mContext);
- msg->setInt32(kCameraIdKey, cameraId);
- msg->post();
- }
+ // Iterate through all registered callbacks
+ mDeviceStatusMap[cameraId] = status;
+ for (auto cb : mCallbacks) {
+ sp<AMessage> msg = new AMessage(kWhatSendSingleCallback, mHandler);
+ ACameraManager_AvailabilityCallback cbFp = isStatusAvailable(status) ?
+ cb.mAvailable : cb.mUnavailable;
+ msg->setPointer(kCallbackFpKey, (void *) cbFp);
+ msg->setPointer(kContextKey, cb.mContext);
+ msg->setString(kCameraIdKey, AString(cameraId));
+ msg->post();
+ }
}
} // namespace android
@@ -311,94 +322,32 @@
* ACameraManger Implementation
*/
camera_status_t
-ACameraManager::getOrCreateCameraIdListLocked(ACameraIdList** cameraIdList) {
- if (mCachedCameraIdList.numCameras == kCameraIdListNotInit) {
- if (isCameraServiceDisabled()) {
- mCachedCameraIdList.numCameras = 0;
- mCachedCameraIdList.cameraIds = new const char*[0];
- *cameraIdList = &mCachedCameraIdList;
- return ACAMERA_OK;
- }
-
- int numCameras = 0;
- Vector<char *> cameraIds;
- sp<hardware::ICameraService> cs = CameraManagerGlobal::getInstance().getCameraService();
- if (cs == nullptr) {
- ALOGE("%s: Cannot reach camera service!", __FUNCTION__);
- return ACAMERA_ERROR_CAMERA_DISCONNECTED;
- }
- // Get number of cameras
- int numAllCameras = 0;
- binder::Status serviceRet = cs->getNumberOfCameras(hardware::ICameraService::CAMERA_TYPE_ALL,
- &numAllCameras);
- if (!serviceRet.isOk()) {
- ALOGE("%s: Error getting camera count: %s", __FUNCTION__,
- serviceRet.toString8().string());
- numAllCameras = 0;
- }
- // Filter API2 compatible cameras and push to cameraIds
- for (int i = 0; i < numAllCameras; i++) {
- // TODO: Only suppot HALs that supports API2 directly now
- bool camera2Support = false;
- serviceRet = cs->supportsCameraApi(i, hardware::ICameraService::API_VERSION_2,
- &camera2Support);
- char buf[kMaxCameraIdLen];
- if (camera2Support) {
- numCameras++;
- mCameraIds.insert(i);
- snprintf(buf, sizeof(buf), "%d", i);
- size_t cameraIdSize = strlen(buf) + 1;
- char *cameraId = new char[cameraIdSize];
- if (!cameraId) {
- ALOGE("Allocate memory for ACameraIdList failed!");
- return ACAMERA_ERROR_NOT_ENOUGH_MEMORY;
- }
- strlcpy(cameraId, buf, cameraIdSize);
- cameraIds.push(cameraId);
- }
- }
- mCachedCameraIdList.numCameras = numCameras;
- mCachedCameraIdList.cameraIds = new const char*[numCameras];
- if (!mCachedCameraIdList.cameraIds) {
- ALOGE("Allocate memory for ACameraIdList failed!");
- return ACAMERA_ERROR_NOT_ENOUGH_MEMORY;
- }
- for (int i = 0; i < numCameras; i++) {
- mCachedCameraIdList.cameraIds[i] = cameraIds[i];
- }
- }
- *cameraIdList = &mCachedCameraIdList;
- return ACAMERA_OK;
-}
-
-camera_status_t
ACameraManager::getCameraIdList(ACameraIdList** cameraIdList) {
Mutex::Autolock _l(mLock);
- ACameraIdList* cachedList;
- camera_status_t ret = getOrCreateCameraIdListLocked(&cachedList);
- if (ret != ACAMERA_OK) {
- ALOGE("Get camera ID list failed! err: %d", ret);
- return ret;
- }
- int numCameras = cachedList->numCameras;
+ std::vector<String8> idList;
+ CameraManagerGlobal::getInstance().getCameraIdList(&idList);
+
+ int numCameras = idList.size();
ACameraIdList *out = new ACameraIdList;
if (!out) {
ALOGE("Allocate memory for ACameraIdList failed!");
return ACAMERA_ERROR_NOT_ENOUGH_MEMORY;
}
out->numCameras = numCameras;
- out->cameraIds = new const char*[numCameras];
+ out->cameraIds = new const char*[numCameras] {nullptr};
if (!out->cameraIds) {
ALOGE("Allocate memory for ACameraIdList failed!");
+ deleteCameraIdList(out);
return ACAMERA_ERROR_NOT_ENOUGH_MEMORY;
}
for (int i = 0; i < numCameras; i++) {
- const char* src = cachedList->cameraIds[i];
+ const char* src = idList[i].string();
size_t dstSize = strlen(src) + 1;
char* dst = new char[dstSize];
if (!dst) {
ALOGE("Allocate memory for ACameraIdList failed!");
+ deleteCameraIdList(out);
return ACAMERA_ERROR_NOT_ENOUGH_MEMORY;
}
strlcpy(dst, src, dstSize);
@@ -413,7 +362,9 @@
if (cameraIdList != nullptr) {
if (cameraIdList->cameraIds != nullptr) {
for (int i = 0; i < cameraIdList->numCameras; i ++) {
- delete[] cameraIdList->cameraIds[i];
+ if (cameraIdList->cameraIds[i] != nullptr) {
+ delete[] cameraIdList->cameraIds[i];
+ }
}
delete[] cameraIdList->cameraIds;
}
@@ -424,29 +375,27 @@
camera_status_t ACameraManager::getCameraCharacteristics(
const char *cameraIdStr, ACameraMetadata **characteristics) {
Mutex::Autolock _l(mLock);
- ACameraIdList* cachedList;
- // Make sure mCameraIds is initialized
- camera_status_t ret = getOrCreateCameraIdListLocked(&cachedList);
- if (ret != ACAMERA_OK) {
- ALOGE("%s: Get camera ID list failed! err: %d", __FUNCTION__, ret);
- return ret;
- }
- int cameraId = atoi(cameraIdStr);
- if (mCameraIds.count(cameraId) == 0) {
- ALOGE("%s: Camera ID %s does not exist!", __FUNCTION__, cameraIdStr);
- return ACAMERA_ERROR_INVALID_PARAMETER;
- }
+
sp<hardware::ICameraService> cs = CameraManagerGlobal::getInstance().getCameraService();
if (cs == nullptr) {
ALOGE("%s: Cannot reach camera service!", __FUNCTION__);
return ACAMERA_ERROR_CAMERA_DISCONNECTED;
}
CameraMetadata rawMetadata;
- binder::Status serviceRet = cs->getCameraCharacteristics(cameraId, &rawMetadata);
+ binder::Status serviceRet = cs->getCameraCharacteristics(String16(cameraIdStr), &rawMetadata);
if (!serviceRet.isOk()) {
- ALOGE("Get camera characteristics from camera service failed: %s",
- serviceRet.toString8().string());
- return ACAMERA_ERROR_UNKNOWN; // should not reach here
+ switch(serviceRet.serviceSpecificErrorCode()) {
+ case hardware::ICameraService::ERROR_DISCONNECTED:
+ ALOGE("%s: Camera %s has been disconnected", __FUNCTION__, cameraIdStr);
+ return ACAMERA_ERROR_CAMERA_DISCONNECTED;
+ case hardware::ICameraService::ERROR_ILLEGAL_ARGUMENT:
+ ALOGE("%s: Camera ID %s does not exist!", __FUNCTION__, cameraIdStr);
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ default:
+ ALOGE("Get camera characteristics from camera service failed: %s",
+ serviceRet.toString8().string());
+ return ACAMERA_ERROR_UNKNOWN; // should not reach here
+ }
}
*characteristics = new ACameraMetadata(
@@ -479,13 +428,12 @@
return ACAMERA_ERROR_CAMERA_DISCONNECTED;
}
- int id = atoi(cameraId);
sp<hardware::camera2::ICameraDeviceCallbacks> callbacks = device->getServiceCallback();
sp<hardware::camera2::ICameraDeviceUser> deviceRemote;
// No way to get package name from native.
// Send a zero length package name and let camera service figure it out from UID
binder::Status serviceRet = cs->connectDevice(
- callbacks, id, String16(""),
+ callbacks, String16(cameraId), String16(""),
hardware::ICameraService::USE_CALLING_UID, /*out*/&deviceRemote);
if (!serviceRet.isOk()) {
@@ -534,11 +482,5 @@
}
ACameraManager::~ACameraManager() {
- Mutex::Autolock _l(mLock);
- if (mCachedCameraIdList.numCameras != kCameraIdListNotInit) {
- for (int i = 0; i < mCachedCameraIdList.numCameras; i++) {
- delete[] mCachedCameraIdList.cameraIds[i];
- }
- delete[] mCachedCameraIdList.cameraIds;
- }
+
}
diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h
index 5b88904..97e4fd9 100644
--- a/camera/ndk/impl/ACameraManager.h
+++ b/camera/ndk/impl/ACameraManager.h
@@ -40,7 +40,7 @@
* instances. Created when first ACameraManager is created and destroyed when
* all ACameraManager instances are deleted.
*
- * TODO: maybe CameraManagerGlobal is better sutied in libcameraclient?
+ * TODO: maybe CameraManagerGlobal is better suited in libcameraclient?
*/
class CameraManagerGlobal final : public RefBase {
public:
@@ -52,6 +52,11 @@
void unregisterAvailabilityCallback(
const ACameraManager_AvailabilityCallbacks *callback);
+ /**
+ * Return camera IDs that support camera2
+ */
+ void getCameraIdList(std::vector<String8> *cameraIds);
+
private:
sp<hardware::ICameraService> mCameraService;
const int kCameraServicePollDelay = 500000; // 0.5s
@@ -72,7 +77,7 @@
class CameraServiceListener final : public hardware::BnCameraServiceListener {
public:
explicit CameraServiceListener(CameraManagerGlobal* cm) : mCameraManager(cm) {}
- virtual binder::Status onStatusChanged(int32_t status, int32_t cameraId);
+ virtual binder::Status onStatusChanged(int32_t status, const String16& cameraId);
// Torch API not implemented yet
virtual binder::Status onTorchStatusChanged(int32_t, const String16&) {
@@ -125,22 +130,18 @@
public:
CallbackHandler() {}
void onMessageReceived(const sp<AMessage> &msg) override;
- private:
- inline void sendSingleCallback(
- int32_t cameraId, void* context,
- ACameraManager_AvailabilityCallback cb) const;
};
sp<CallbackHandler> mHandler;
sp<ALooper> mCbLooper; // Looper thread where callbacks actually happen on
- void onStatusChanged(int32_t status, int32_t cameraId);
- void onStatusChangedLocked(int32_t status, int32_t cameraId);
+ void onStatusChanged(int32_t status, const String8& cameraId);
+ void onStatusChangedLocked(int32_t status, const String8& cameraId);
// Utils for status
static bool validStatus(int32_t status);
static bool isStatusAvailable(int32_t status);
// Map camera_id -> status
- std::map<int32_t, int32_t> mDeviceStatusMap;
+ std::map<String8, int32_t> mDeviceStatusMap;
// For the singleton instance
static Mutex sLock;
@@ -157,7 +158,6 @@
*/
struct ACameraManager {
ACameraManager() :
- mCachedCameraIdList({kCameraIdListNotInit, nullptr}),
mGlobalManager(&(android::CameraManagerGlobal::getInstance())) {}
~ACameraManager();
camera_status_t getCameraIdList(ACameraIdList** cameraIdList);
@@ -170,14 +170,10 @@
/*out*/ACameraDevice** device);
private:
- camera_status_t getOrCreateCameraIdListLocked(ACameraIdList** cameraIdList);
-
enum {
kCameraIdListNotInit = -1
};
android::Mutex mLock;
- std::set<int> mCameraIds; // Init by getOrCreateCameraIdListLocked
- ACameraIdList mCachedCameraIdList; // Init by getOrCreateCameraIdListLocked
android::sp<android::CameraManagerGlobal> mGlobalManager;
};
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index b91e0f3..946e3b8 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -65,14 +65,14 @@
// Stub listener implementation
class TestCameraServiceListener : public hardware::BnCameraServiceListener {
std::map<String16, int32_t> mCameraTorchStatuses;
- std::map<int32_t, int32_t> mCameraStatuses;
+ std::map<String16, int32_t> mCameraStatuses;
mutable Mutex mLock;
mutable Condition mCondition;
mutable Condition mTorchCondition;
public:
virtual ~TestCameraServiceListener() {};
- virtual binder::Status onStatusChanged(int32_t status, int32_t cameraId) {
+ virtual binder::Status onStatusChanged(int32_t status, const String16& cameraId) {
Mutex::Autolock l(mLock);
mCameraStatuses[cameraId] = status;
mCondition.broadcast();
@@ -130,7 +130,7 @@
return iter->second;
};
- int32_t getStatus(int32_t cameraId) const {
+ int32_t getStatus(const String16& cameraId) const {
Mutex::Autolock l(mLock);
const auto& iter = mCameraStatuses.find(cameraId);
if (iter == mCameraStatuses.end()) {
@@ -310,14 +310,16 @@
// Check listener binder calls
sp<TestCameraServiceListener> listener(new TestCameraServiceListener());
- res = service->addListener(listener);
+ std::vector<hardware::CameraStatus> statuses;
+ res = service->addListener(listener, &statuses);
EXPECT_TRUE(res.isOk()) << res;
- EXPECT_TRUE(listener->waitForNumCameras(numCameras));
+ EXPECT_EQ(numCameras, static_cast<const int>(statuses.size()));
for (int32_t i = 0; i < numCameras; i++) {
+ String16 cameraId = String16(String8::format("%d", i));
bool isSupported = false;
- res = service->supportsCameraApi(i,
+ res = service->supportsCameraApi(cameraId,
hardware::ICameraService::API_VERSION_2, &isSupported);
EXPECT_TRUE(res.isOk()) << res;
@@ -328,12 +330,12 @@
// Check metadata binder call
CameraMetadata metadata;
- res = service->getCameraCharacteristics(i, &metadata);
+ res = service->getCameraCharacteristics(cameraId, &metadata);
EXPECT_TRUE(res.isOk()) << res;
EXPECT_FALSE(metadata.isEmpty());
// Make sure we're available, or skip device tests otherwise
- int32_t s = listener->getStatus(i);
+ int32_t s = listener->getStatus(cameraId);
EXPECT_EQ(::android::hardware::ICameraServiceListener::STATUS_PRESENT, s);
if (s != ::android::hardware::ICameraServiceListener::STATUS_PRESENT) {
continue;
@@ -342,7 +344,7 @@
// Check connect binder calls
sp<TestCameraDeviceCallbacks> callbacks(new TestCameraDeviceCallbacks());
sp<hardware::camera2::ICameraDeviceUser> device;
- res = service->connectDevice(callbacks, i, String16("meeeeeeeee!"),
+ res = service->connectDevice(callbacks, cameraId, String16("meeeeeeeee!"),
hardware::ICameraService::USE_CALLING_UID, /*out*/&device);
EXPECT_TRUE(res.isOk()) << res;
ASSERT_NE(nullptr, device.get());
@@ -352,12 +354,12 @@
int32_t torchStatus = listener->getTorchStatus(i);
if (torchStatus == hardware::ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF) {
// Check torch calls
- res = service->setTorchMode(String16(String8::format("%d", i)),
+ res = service->setTorchMode(cameraId,
/*enabled*/true, callbacks);
EXPECT_TRUE(res.isOk()) << res;
EXPECT_TRUE(listener->waitForTorchState(
hardware::ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON, i));
- res = service->setTorchMode(String16(String8::format("%d", i)),
+ res = service->setTorchMode(cameraId,
/*enabled*/false, callbacks);
EXPECT_TRUE(res.isOk()) << res;
EXPECT_TRUE(listener->waitForTorchState(
@@ -379,7 +381,7 @@
sp<TestCameraServiceListener> serviceListener;
std::pair<sp<TestCameraDeviceCallbacks>, sp<hardware::camera2::ICameraDeviceUser>>
- openNewDevice(int deviceId) {
+ openNewDevice(const String16& deviceId) {
sp<TestCameraDeviceCallbacks> callbacks(new TestCameraDeviceCallbacks());
sp<hardware::camera2::ICameraDeviceUser> device;
{
@@ -415,7 +417,8 @@
sp<IBinder> binder = sm->getService(String16("media.camera"));
service = interface_cast<hardware::ICameraService>(binder);
serviceListener = new TestCameraServiceListener();
- service->addListener(serviceListener);
+ std::vector<hardware::CameraStatus> statuses;
+ service->addListener(serviceListener, &statuses);
service->getNumberOfCameras(hardware::ICameraService::CAMERA_TYPE_BACKWARD_COMPATIBLE,
&numCameras);
}
@@ -435,14 +438,14 @@
EXPECT_TRUE(serviceListener->waitForNumCameras(numCameras));
for (int32_t i = 0; i < numCameras; i++) {
// Make sure we're available, or skip device tests otherwise
- int32_t s = serviceListener->getStatus(i);
+ String16 cameraId(String8::format("%d",i));
+ int32_t s = serviceListener->getStatus(cameraId);
EXPECT_EQ(hardware::ICameraServiceListener::STATUS_PRESENT, s);
if (s != hardware::ICameraServiceListener::STATUS_PRESENT) {
continue;
}
binder::Status res;
-
- auto p = openNewDevice(i);
+ auto p = openNewDevice(cameraId);
sp<TestCameraDeviceCallbacks> callbacks = p.first;
sp<hardware::camera2::ICameraDeviceUser> device = p.second;
diff --git a/cmds/stagefright/SimplePlayer.h b/cmds/stagefright/SimplePlayer.h
index 918fd24..1269162 100644
--- a/cmds/stagefright/SimplePlayer.h
+++ b/cmds/stagefright/SimplePlayer.h
@@ -22,7 +22,7 @@
struct ABuffer;
struct ALooper;
-struct AudioTrack;
+class AudioTrack;
class IGraphicBufferProducer;
struct MediaCodec;
class MediaCodecBuffer;
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
index 86bf047..8356bcc 100644
--- a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
@@ -75,6 +75,8 @@
}
status_t res = session->provideKeyResponse(response);
if (res == android::OK) {
+ // This is for testing AMediaDrm_setOnEventListener only.
+ sendEvent(kDrmPluginEventVendorDefined, 0, &scope, NULL);
keySetId.clear();
}
return res;
@@ -90,6 +92,8 @@
value = "ClearKey CDM";
} else if (name == "algorithms") {
value = "";
+ } else if (name == "listenerTestSupport") {
+ value = "true";
} else {
ALOGE("App requested unknown string property %s", name.string());
return android::ERROR_DRM_CANNOT_HANDLE;
diff --git a/include/camera/CameraBase.h b/include/camera/CameraBase.h
index 0692a27..03fbdfd 100644
--- a/include/camera/CameraBase.h
+++ b/include/camera/CameraBase.h
@@ -17,7 +17,10 @@
#ifndef ANDROID_HARDWARE_CAMERA_BASE_H
#define ANDROID_HARDWARE_CAMERA_BASE_H
+#include <android/hardware/ICameraServiceListener.h>
+
#include <utils/Mutex.h>
+#include <binder/BinderService.h>
struct camera_frame_metadata;
@@ -29,6 +32,13 @@
class ICameraService;
class ICameraServiceListener;
+enum {
+ /** The facing of the camera is opposite to that of the screen. */
+ CAMERA_FACING_BACK = 0,
+ /** The facing of the camera is the same as that of the screen. */
+ CAMERA_FACING_FRONT = 1,
+};
+
struct CameraInfo : public android::Parcelable {
/**
* The direction that the camera faces to. It should be CAMERA_FACING_BACK
@@ -50,11 +60,33 @@
*/
int orientation;
- virtual status_t writeToParcel(Parcel* parcel) const;
- virtual status_t readFromParcel(const Parcel* parcel);
+ virtual status_t writeToParcel(android::Parcel* parcel) const;
+ virtual status_t readFromParcel(const android::Parcel* parcel);
};
+/**
+ * Basic status information about a camera device - its name and its current
+ * state.
+ */
+struct CameraStatus : public android::Parcelable {
+ /**
+ * The name of the camera device
+ */
+ String8 cameraId;
+
+ /**
+ * Its current status, one of the ICameraService::STATUS_* fields
+ */
+ int32_t status;
+
+ virtual status_t writeToParcel(android::Parcel* parcel) const;
+ virtual status_t readFromParcel(const android::Parcel* parcel);
+
+ CameraStatus(String8 id, int32_t s) : cameraId(id), status(s) {}
+ CameraStatus() : status(ICameraServiceListener::STATUS_PRESENT) {}
+};
+
} // namespace hardware
using hardware::CameraInfo;
@@ -86,12 +118,6 @@
/*out*/
struct hardware::CameraInfo* cameraInfo);
- static status_t addServiceListener(
- const sp<::android::hardware::ICameraServiceListener>& listener);
-
- static status_t removeServiceListener(
- const sp<::android::hardware::ICameraServiceListener>& listener);
-
sp<TCamUser> remote();
// Status is set to 'UNKNOWN_ERROR' after successful (re)connection
diff --git a/include/camera/CameraParameters2.h b/include/camera/CameraParameters2.h
index 88ad812..f691cd6 100644
--- a/include/camera/CameraParameters2.h
+++ b/include/camera/CameraParameters2.h
@@ -151,7 +151,7 @@
}
ssize_t removeItem(const KeyT& key) {
- size_t vectorIdx = (size_t) indexOfKey(key);
+ ssize_t vectorIdx = indexOfKey(key);
if (vectorIdx < 0) {
return vectorIdx;
diff --git a/include/camera/CaptureResult.h b/include/camera/CaptureResult.h
index 45e4518..917d953 100644
--- a/include/camera/CaptureResult.h
+++ b/include/camera/CaptureResult.h
@@ -88,8 +88,8 @@
*/
bool isValid();
- virtual status_t readFromParcel(const Parcel* parcel) override;
- virtual status_t writeToParcel(Parcel* parcel) const override;
+ virtual status_t readFromParcel(const android::Parcel* parcel) override;
+ virtual status_t writeToParcel(android::Parcel* parcel) const override;
};
} // namespace impl
} // namespace camera2
@@ -105,8 +105,8 @@
CaptureResult(const CaptureResult& otherResult);
- status_t readFromParcel(Parcel* parcel);
- status_t writeToParcel(Parcel* parcel) const;
+ status_t readFromParcel(android::Parcel* parcel);
+ status_t writeToParcel(android::Parcel* parcel) const;
};
}
diff --git a/include/camera/VendorTagDescriptor.h b/include/camera/VendorTagDescriptor.h
index bfc8c96..adfc8c7 100644
--- a/include/camera/VendorTagDescriptor.h
+++ b/include/camera/VendorTagDescriptor.h
@@ -76,7 +76,7 @@
*/
virtual status_t writeToParcel(
/*out*/
- Parcel* parcel) const override;
+ android::Parcel* parcel) const override;
/**
* Convenience method to get a vector containing all vendor tag
@@ -103,7 +103,7 @@
*
* Returns OK on success, or a negative error code.
*/
- virtual status_t readFromParcel(const Parcel* parcel) override;
+ virtual status_t readFromParcel(const android::Parcel* parcel) override;
protected:
KeyedVector<String8, KeyedVector<String8, uint32_t>*> mReverseMapping;
diff --git a/include/camera/android/hardware/ICamera.h b/include/camera/android/hardware/ICamera.h
index 3b12afe..315669e 100644
--- a/include/camera/android/hardware/ICamera.h
+++ b/include/camera/android/hardware/ICamera.h
@@ -33,7 +33,7 @@
class ICameraClient;
-class ICamera: public IInterface
+class ICamera: public android::IInterface
{
/**
* Keep up-to-date with ICamera.aidl in frameworks/base
@@ -139,7 +139,7 @@
// ----------------------------------------------------------------------------
-class BnCamera: public BnInterface<ICamera>
+class BnCamera: public android::BnInterface<ICamera>
{
public:
virtual status_t onTransact( uint32_t code,
diff --git a/include/camera/android/hardware/ICameraClient.h b/include/camera/android/hardware/ICameraClient.h
index 3f835a9..f6ee311 100644
--- a/include/camera/android/hardware/ICameraClient.h
+++ b/include/camera/android/hardware/ICameraClient.h
@@ -27,7 +27,7 @@
namespace android {
namespace hardware {
-class ICameraClient: public IInterface
+class ICameraClient: public android::IInterface
{
public:
DECLARE_META_INTERFACE(CameraClient);
@@ -45,7 +45,7 @@
// ----------------------------------------------------------------------------
-class BnCameraClient: public BnInterface<ICameraClient>
+class BnCameraClient: public android::BnInterface<ICameraClient>
{
public:
virtual status_t onTransact( uint32_t code,
diff --git a/include/camera/camera2/CaptureRequest.h b/include/camera/camera2/CaptureRequest.h
index c989f26..978f48d 100644
--- a/include/camera/camera2/CaptureRequest.h
+++ b/include/camera/camera2/CaptureRequest.h
@@ -37,8 +37,8 @@
/**
* Keep impl up-to-date with CaptureRequest.java in frameworks/base
*/
- status_t readFromParcel(const Parcel* parcel) override;
- status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
};
} // namespace camera2
diff --git a/include/camera/camera2/OutputConfiguration.h b/include/camera/camera2/OutputConfiguration.h
index cf8f3c6..cb04c0e 100644
--- a/include/camera/camera2/OutputConfiguration.h
+++ b/include/camera/camera2/OutputConfiguration.h
@@ -47,9 +47,9 @@
/**
* Keep impl up-to-date with OutputConfiguration.java in frameworks/base
*/
- virtual status_t writeToParcel(Parcel* parcel) const override;
+ virtual status_t writeToParcel(android::Parcel* parcel) const override;
- virtual status_t readFromParcel(const Parcel* parcel) override;
+ virtual status_t readFromParcel(const android::Parcel* parcel) override;
// getGraphicBufferProducer will be NULL
// getRotation will be INVALID_ROTATION
@@ -59,7 +59,7 @@
// getGraphicBufferProducer will be NULL if error occurred
// getRotation will be INVALID_ROTATION if error occurred
// getSurfaceSetID will be INVALID_SET_ID if error occurred
- OutputConfiguration(const Parcel& parcel);
+ OutputConfiguration(const android::Parcel& parcel);
OutputConfiguration(sp<IGraphicBufferProducer>& gbp, int rotation,
int surfaceSetID = INVALID_SET_ID);
@@ -105,7 +105,7 @@
int mWidth;
int mHeight;
// helper function
- static String16 readMaybeEmptyString16(const Parcel* parcel);
+ static String16 readMaybeEmptyString16(const android::Parcel* parcel);
};
} // namespace params
} // namespace camera2
diff --git a/include/camera/camera2/SubmitInfo.h b/include/camera/camera2/SubmitInfo.h
index 3b47b32..8f271c0 100644
--- a/include/camera/camera2/SubmitInfo.h
+++ b/include/camera/camera2/SubmitInfo.h
@@ -31,8 +31,8 @@
int32_t mRequestId;
int64_t mLastFrameNumber;
- virtual status_t writeToParcel(Parcel *parcel) const override;
- virtual status_t readFromParcel(const Parcel* parcel) override;
+ virtual status_t writeToParcel(android::Parcel *parcel) const override;
+ virtual status_t readFromParcel(const android::Parcel* parcel) override;
};
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index c69c11d..bfc068b 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -462,7 +462,7 @@
}
// IBinder::DeathRecipient
- virtual void binderDied(const wp<IBinder>& who) {
+ virtual void binderDied(const wp<IBinder>& /*who*/) {
sp<AudioEffect> effect = mEffect.promote();
if (effect != 0) {
effect->binderDied();
diff --git a/include/media/ExtendedAudioBufferProvider.h b/include/media/ExtendedAudioBufferProvider.h
index 168ceed..51e4713 100644
--- a/include/media/ExtendedAudioBufferProvider.h
+++ b/include/media/ExtendedAudioBufferProvider.h
@@ -31,7 +31,7 @@
// Invoked by buffer consumer when a new timestamp is available.
// Default implementation ignores the timestamp.
- virtual void onTimestamp(const ExtendedTimestamp& timestamp) { }
+ virtual void onTimestamp(const ExtendedTimestamp& /*timestamp*/) { }
};
} // namespace android
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 56cecea..8c5e61a 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -198,6 +198,7 @@
audio_io_handle_t output,
audio_session_t sessionId,
const String16& callingPackage,
+ pid_t pid,
status_t *status,
int *id,
int *enabled) = 0;
diff --git a/include/media/IMediaExtractor.h b/include/media/IMediaExtractor.h
index 34b15e9..e0a81f1 100644
--- a/include/media/IMediaExtractor.h
+++ b/include/media/IMediaExtractor.h
@@ -54,8 +54,6 @@
virtual uint32_t flags() const = 0;
// for DRM
- virtual void setDrmFlag(bool flag) = 0;
- virtual bool getDrmFlag() = 0;
virtual char* getDrmTrackInfo(size_t trackID, int *len) = 0;
virtual void setUID(uid_t uid) = 0;
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index 8266b0b..f21bb3a 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -32,7 +32,7 @@
namespace android {
struct IHDCP;
-struct IMediaCodecList;
+class IMediaCodecList;
struct IMediaHTTPService;
class IMediaRecorder;
class IOMX;
diff --git a/include/media/MediaCodecInfo.h b/include/media/MediaCodecInfo.h
index 48d0407..6b50f22 100644
--- a/include/media/MediaCodecInfo.h
+++ b/include/media/MediaCodecInfo.h
@@ -74,7 +74,7 @@
DISALLOW_EVIL_CONSTRUCTORS(Capabilities);
- friend class MediaCodecInfo;
+ friend struct MediaCodecInfo;
};
// Use a subclass to allow setting fields on construction without allowing
@@ -136,7 +136,7 @@
DISALLOW_EVIL_CONSTRUCTORS(MediaCodecInfo);
- friend class MediaCodecList;
+ friend struct MediaCodecList;
friend class MediaCodecListOverridesTest;
};
diff --git a/include/media/MediaMetadataRetrieverInterface.h b/include/media/MediaMetadataRetrieverInterface.h
index bce6ee3..a5e1350 100644
--- a/include/media/MediaMetadataRetrieverInterface.h
+++ b/include/media/MediaMetadataRetrieverInterface.h
@@ -54,9 +54,9 @@
MediaMetadataRetrieverInterface() {}
virtual ~MediaMetadataRetrieverInterface() {}
- virtual VideoFrame* getFrameAtTime(int64_t timeUs, int option) { return NULL; }
+ virtual VideoFrame* getFrameAtTime(int64_t /*timeUs*/, int /*option*/) { return NULL; }
virtual MediaAlbumArt* extractAlbumArt() { return NULL; }
- virtual const char* extractMetadata(int keyCode) { return NULL; }
+ virtual const char* extractMetadata(int /*keyCode*/) { return NULL; }
};
}; // namespace android
diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h
index 59dfd18..b9105b1 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -45,7 +45,7 @@
const sp<ICameraRecordingProxy>& proxy) = 0;
virtual status_t setPreviewSurface(const sp<IGraphicBufferProducer>& surface) = 0;
virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0;
- virtual status_t setOutputFileAuxiliary(int fd) {return INVALID_OPERATION;}
+ virtual status_t setOutputFileAuxiliary(int /*fd*/) {return INVALID_OPERATION;}
virtual status_t setParameters(const String8& params) = 0;
virtual status_t setListener(const sp<IMediaRecorderClient>& listener) = 0;
virtual status_t setClientName(const String16& clientName) = 0;
diff --git a/include/media/nbaio/NBAIO.h b/include/media/nbaio/NBAIO.h
index 3fd97ac..9dd9125 100644
--- a/include/media/nbaio/NBAIO.h
+++ b/include/media/nbaio/NBAIO.h
@@ -215,7 +215,7 @@
// Returns NO_ERROR if a timestamp is available. The timestamp includes the total number
// of frames presented to an external observer, together with the value of CLOCK_MONOTONIC
// as of this presentation count. The timestamp parameter is undefined if error is returned.
- virtual status_t getTimestamp(ExtendedTimestamp ×tamp) { return INVALID_OPERATION; }
+ virtual status_t getTimestamp(ExtendedTimestamp& /*timestamp*/) { return INVALID_OPERATION; }
protected:
NBAIO_Sink(const NBAIO_Format& format = Format_Invalid) : NBAIO_Port(format), mFramesWritten(0)
@@ -313,7 +313,7 @@
// Invoked asynchronously by corresponding sink when a new timestamp is available.
// Default implementation ignores the timestamp.
- virtual void onTimestamp(const ExtendedTimestamp& timestamp) { }
+ virtual void onTimestamp(const ExtendedTimestamp& /*timestamp*/) { }
protected:
NBAIO_Source(const NBAIO_Format& format = Format_Invalid) : NBAIO_Port(format), mFramesRead(0)
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index cde188c..3420617 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -37,7 +37,7 @@
struct ABuffer;
class ACodecBufferChannel;
class MediaCodecBuffer;
-struct MemoryDealer;
+class MemoryDealer;
struct DescribeColorFormat2Params;
struct DataConverter;
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index c2e75a6..c604f2d 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -216,10 +216,10 @@
// Returns true if need to skip the current frame.
// Called from dataCallbackTimestamp.
- virtual bool skipCurrentFrame(int64_t timestampUs) {return false;}
+ virtual bool skipCurrentFrame(int64_t /*timestampUs*/) {return false;}
// Callback called when still camera raw data is available.
- virtual void dataCallback(int32_t msgType, const sp<IMemory> &data) {}
+ virtual void dataCallback(int32_t /*msgType*/, const sp<IMemory>& /*data*/) {}
virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
const sp<IMemory> &data);
diff --git a/include/media/stagefright/CodecBase.h b/include/media/stagefright/CodecBase.h
index 52daa9e..cfbaea4 100644
--- a/include/media/stagefright/CodecBase.h
+++ b/include/media/stagefright/CodecBase.h
@@ -209,10 +209,10 @@
virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
virtual status_t queryCapabilities(
- const AString &name, const AString &mime, bool isEncoder,
- sp<MediaCodecInfo::Capabilities> *caps /* nonnull */) { return INVALID_OPERATION; }
+ const AString& /*name*/, const AString& /*mime*/, bool /*isEncoder*/,
+ sp<MediaCodecInfo::Capabilities>* /*caps*/ /* nonnull */) { return INVALID_OPERATION; }
- virtual status_t setSurface(const sp<Surface> &surface) { return INVALID_OPERATION; }
+ virtual status_t setSurface(const sp<Surface>& /*surface*/) { return INVALID_OPERATION; }
virtual void signalFlush() = 0;
virtual void signalResume() = 0;
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index a8dcbe0..e7135a2 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -97,17 +97,17 @@
return String8("<unspecified>");
}
- virtual status_t reconnectAtOffset(off64_t offset) {
+ virtual status_t reconnectAtOffset(off64_t /*offset*/) {
return ERROR_UNSUPPORTED;
}
////////////////////////////////////////////////////////////////////////////
// for DRM
- virtual sp<DecryptHandle> DrmInitialization(const char *mime = NULL) {
+ virtual sp<DecryptHandle> DrmInitialization(const char * /*mime*/ = NULL) {
return NULL;
}
- virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) {};
+ virtual void getDrmInfo(sp<DecryptHandle> &/*handle*/, DrmManagerClient ** /*client*/) {};
virtual String8 getUri() {
return String8();
diff --git a/include/media/stagefright/FrameRenderTracker.h b/include/media/stagefright/FrameRenderTracker.h
index 6c572b8..6cbf85d 100644
--- a/include/media/stagefright/FrameRenderTracker.h
+++ b/include/media/stagefright/FrameRenderTracker.h
@@ -82,7 +82,7 @@
sp<GraphicBuffer> mGraphicBuffer;
sp<Fence> mFence;
- friend class FrameRenderTracker;
+ friend struct FrameRenderTracker;
};
struct FrameRenderTracker {
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 9434047..d415b8b 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -27,7 +27,7 @@
namespace android {
-class AMessage;
+struct AMessage;
class MediaBuffer;
class MetaData;
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 50fb312..2e367bf 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -44,7 +44,7 @@
class IResourceManagerClient;
class IResourceManagerService;
struct PersistentSurface;
-struct SoftwareRenderer;
+class SoftwareRenderer;
class Surface;
struct MediaCodec : public AHandler {
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
index e5ee72e..fbb4a67 100644
--- a/include/media/stagefright/MediaExtractor.h
+++ b/include/media/stagefright/MediaExtractor.h
@@ -24,7 +24,7 @@
namespace android {
class DataSource;
-class MediaSource;
+struct MediaSource;
class MetaData;
class MediaExtractor : public BnMediaExtractor {
@@ -59,16 +59,10 @@
virtual uint32_t flags() const;
// for DRM
- void setDrmFlag(bool flag) {
- mIsDrm = flag;
- };
- bool getDrmFlag() {
- return mIsDrm;
- }
- virtual char* getDrmTrackInfo(size_t trackID, int *len) {
+ virtual char* getDrmTrackInfo(size_t /*trackID*/, int * /*len*/) {
return NULL;
}
- virtual void setUID(uid_t uid) {
+ virtual void setUID(uid_t /*uid*/) {
}
virtual const char * name() { return "<unspecified>"; }
diff --git a/include/media/stagefright/MediaFilter.h b/include/media/stagefright/MediaFilter.h
index 0e4539b..0c10d11 100644
--- a/include/media/stagefright/MediaFilter.h
+++ b/include/media/stagefright/MediaFilter.h
@@ -23,7 +23,7 @@
class ACodecBufferChannel;
struct GraphicBufferListener;
-struct MemoryDealer;
+class MemoryDealer;
struct SimpleFilter;
struct MediaFilter : public CodecBase {
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index b6476c9..2b19523 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -44,11 +44,11 @@
mListener = listener;
}
- virtual status_t dump(int fd, const Vector<String16>& args) {
+ virtual status_t dump(int /*fd*/, const Vector<String16>& /*args*/) {
return OK;
}
- virtual void setStartTimeOffsetMs(int ms) {}
+ virtual void setStartTimeOffsetMs(int /*ms*/) {}
virtual int32_t getStartTimeOffsetMs() const { return 0; }
protected:
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
index a8aca5a..e414757 100644
--- a/include/media/stagefright/NuMediaExtractor.h
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -34,7 +34,7 @@
class DataSource;
struct IMediaHTTPService;
class MediaBuffer;
-struct MediaExtractor;
+class MediaExtractor;
struct MediaSource;
class MetaData;
diff --git a/include/media/stagefright/SimpleDecodingSource.h b/include/media/stagefright/SimpleDecodingSource.h
index 534097b..e6aee6a 100644
--- a/include/media/stagefright/SimpleDecodingSource.h
+++ b/include/media/stagefright/SimpleDecodingSource.h
@@ -71,12 +71,13 @@
// Construct this using a codec, source and looper.
SimpleDecodingSource(
const sp<MediaCodec> &codec, const sp<IMediaSource> &source, const sp<ALooper> &looper,
- bool usingSurface, const sp<AMessage> &format);
+ bool usingSurface, bool isVorbis, const sp<AMessage> &format);
sp<MediaCodec> mCodec;
sp<IMediaSource> mSource;
sp<ALooper> mLooper;
bool mUsingSurface;
+ bool mIsVorbis;
enum State {
INIT,
STARTED,
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index 590952f..a5f9ab6 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -128,9 +128,11 @@
mDescriptor.uuid = *(uuid != NULL ? uuid : EFFECT_UUID_NULL);
mIEffectClient = new EffectClient(this);
+ mClientPid = IPCThreadState::self()->getCallingPid();
iEffect = audioFlinger->createEffect((effect_descriptor_t *)&mDescriptor,
- mIEffectClient, priority, io, mSessionId, mOpPackageName, &mStatus, &mId, &enabled);
+ mIEffectClient, priority, io, mSessionId, mOpPackageName, mClientPid,
+ &mStatus, &mId, &enabled);
if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
ALOGE("set(): AudioFlinger could not create effect, status: %d", mStatus);
@@ -156,7 +158,6 @@
mCblk->buffer = (uint8_t *)mCblk + bufOffset;
IInterface::asBinder(iEffect)->linkToDeath(mIEffectClient);
- mClientPid = IPCThreadState::self()->getCallingPid();
ALOGV("set() %p OK effect: %s id: %d status %d enabled %d pid %d", this, mDescriptor.name, mId,
mStatus, mEnabled, mClientPid);
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index b532deb..255e350 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -720,6 +720,7 @@
audio_io_handle_t output,
audio_session_t sessionId,
const String16& opPackageName,
+ pid_t pid,
status_t *status,
int *id,
int *enabled)
@@ -741,6 +742,7 @@
data.writeInt32((int32_t) output);
data.writeInt32(sessionId);
data.writeString16(opPackageName);
+ data.writeInt32((int32_t) pid);
status_t lStatus = remote()->transact(CREATE_EFFECT, data, &reply);
if (lStatus != NO_ERROR) {
@@ -1300,12 +1302,14 @@
audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
audio_session_t sessionId = (audio_session_t) data.readInt32();
const String16 opPackageName = data.readString16();
+ pid_t pid = (pid_t)data.readInt32();
+
status_t status = NO_ERROR;
int id = 0;
int enabled = 0;
sp<IEffect> effect = createEffect(&desc, client, priority, output, sessionId,
- opPackageName, &status, &id, &enabled);
+ opPackageName, pid, &status, &id, &enabled);
reply->writeInt32(status);
reply->writeInt32(id);
reply->writeInt32(enabled);
diff --git a/media/libaudiohal/Android.mk b/media/libaudiohal/Android.mk
index 63aa9c6..e82766f 100644
--- a/media/libaudiohal/Android.mk
+++ b/media/libaudiohal/Android.mk
@@ -29,9 +29,8 @@
android.hardware.audio@2.0 \
android.hardware.audio.common@2.0 \
android.hardware.audio.common@2.0-util \
- android.hardware.audio.effect@2.0
-
-LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper
+ android.hardware.audio.effect@2.0 \
+ libmedia_helper
else # if !ENABLE_TREBLE
diff --git a/media/libaudiohal/ConversionHelperHidl.cpp b/media/libaudiohal/ConversionHelperHidl.cpp
index ebbc02c..1fabfbe 100644
--- a/media/libaudiohal/ConversionHelperHidl.cpp
+++ b/media/libaudiohal/ConversionHelperHidl.cpp
@@ -82,17 +82,25 @@
}
}
+// static
+void ConversionHelperHidl::crashIfHalIsDead(const Status& status) {
+ LOG_ALWAYS_FATAL_IF(
+ status.transactionError() == DEAD_OBJECT, "HAL server crashed, need to restart");
+}
+
status_t ConversionHelperHidl::processReturn(const char* funcName, const Status& status) {
const status_t st = status.transactionError();
ALOGE_IF(st, "%s %p %s: %s (from rpc)", mClassName, this, funcName, strerror(-st));
+ crashIfHalIsDead(status);
return st;
}
status_t ConversionHelperHidl::processReturn(
const char* funcName, const Status& status, hardware::audio::V2_0::Result retval) {
const status_t st = status.isOk() ? analyzeResult(retval) : status.transactionError();
- ALOGE_IF(st, "%s %p %s: %s (from %s)",
- mClassName, this, funcName, strerror(-st), status.isOk() ? "hal" : "rpc");
+ ALOGE_IF(!status.isOk() && st, "%s %p %s: %s (from rpc)",
+ mClassName, this, funcName, strerror(-st));
+ crashIfHalIsDead(status);
return st;
}
diff --git a/media/libaudiohal/ConversionHelperHidl.h b/media/libaudiohal/ConversionHelperHidl.h
index 628913a..428daf2 100644
--- a/media/libaudiohal/ConversionHelperHidl.h
+++ b/media/libaudiohal/ConversionHelperHidl.h
@@ -30,6 +30,9 @@
namespace android {
class ConversionHelperHidl {
+ public:
+ static void crashIfHalIsDead(const Status& status);
+
protected:
static status_t keysFromHal(const String8& keys, hidl_vec<hidl_string> *hidlKeys);
static status_t parametersFromHal(const String8& kvPairs, hidl_vec<ParameterValue> *hidlParams);
diff --git a/media/libaudiohal/DevicesFactoryHalHidl.cpp b/media/libaudiohal/DevicesFactoryHalHidl.cpp
index 155b1a8..efcc089 100644
--- a/media/libaudiohal/DevicesFactoryHalHidl.cpp
+++ b/media/libaudiohal/DevicesFactoryHalHidl.cpp
@@ -22,6 +22,7 @@
#include <android/hardware/audio/2.0/IDevice.h>
#include <utils/Log.h>
+#include "ConversionHelperHidl.h"
#include "DeviceHalHidl.h"
#include "DevicesFactoryHalHidl.h"
@@ -82,6 +83,7 @@
else if (retval == Result::INVALID_ARGUMENTS) return BAD_VALUE;
else return NO_INIT;
}
+ ConversionHelperHidl::crashIfHalIsDead(ret.getStatus());
return ret.getStatus().transactionError();
}
diff --git a/media/libaudiohal/EffectHalHidl.cpp b/media/libaudiohal/EffectHalHidl.cpp
index b508cb5..1cd1997 100644
--- a/media/libaudiohal/EffectHalHidl.cpp
+++ b/media/libaudiohal/EffectHalHidl.cpp
@@ -20,6 +20,7 @@
#include <media/EffectsFactoryApi.h>
#include <utils/Log.h>
+#include "ConversionHelperHidl.h"
#include "EffectHalHidl.h"
#include "HidlUtils.h"
@@ -102,6 +103,7 @@
effectDescriptorToHal(result, pDescriptor);
}
});
+ ConversionHelperHidl::crashIfHalIsDead(ret.getStatus());
return ret.getStatus().isOk() ? analyzeResult(retval) : ret.getStatus().transactionError();
}
diff --git a/media/libaudiohal/EffectsFactoryHalHidl.cpp b/media/libaudiohal/EffectsFactoryHalHidl.cpp
index 4f2eef0..bacbe4e 100644
--- a/media/libaudiohal/EffectsFactoryHalHidl.cpp
+++ b/media/libaudiohal/EffectsFactoryHalHidl.cpp
@@ -20,6 +20,7 @@
#include <cutils/native_handle.h>
#include <media/EffectsFactoryApi.h>
+#include "ConversionHelperHidl.h"
#include "EffectHalHidl.h"
#include "EffectsFactoryHalHidl.h"
#include "HidlUtils.h"
@@ -63,6 +64,7 @@
return retval == Result::OK ? OK : NO_INIT;
}
mLastDescriptors.resize(0);
+ ConversionHelperHidl::crashIfHalIsDead(ret.getStatus());
return ret.getStatus().transactionError();
}
@@ -107,6 +109,7 @@
else if (retval == Result::INVALID_ARGUMENTS) return NAME_NOT_FOUND;
else return NO_INIT;
}
+ ConversionHelperHidl::crashIfHalIsDead(ret.getStatus());
return ret.getStatus().transactionError();
}
@@ -130,6 +133,7 @@
else if (retval == Result::INVALID_ARGUMENTS) return NAME_NOT_FOUND;
else return NO_INIT;
}
+ ConversionHelperHidl::crashIfHalIsDead(ret.getStatus());
return ret.getStatus().transactionError();
}
@@ -139,6 +143,7 @@
hidlHandle->data[0] = fd;
Return<void> ret = mEffectsFactory->debugDump(hidlHandle);
native_handle_delete(hidlHandle);
+ ConversionHelperHidl::crashIfHalIsDead(ret.getStatus());
return ret.getStatus().transactionError();
}
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 6e28ba9..4b14543 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -1,4 +1,4 @@
-cc_library_static {
+cc_library {
name: "libmedia_helper",
srcs: ["AudioParameter.cpp", "TypeConverter.cpp"],
cflags: [
@@ -6,5 +6,8 @@
"-Wno-error=deprecated-declarations",
"-Wall",
],
+ shared: {
+ shared_libs: ["libutils", "liblog"],
+ },
clang: true,
}
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 02947b0..8fff414 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -59,12 +59,11 @@
LOCAL_SHARED_LIBRARIES := \
libui liblog libcutils libutils libbinder libsonivox libicuuc libicui18n libexpat \
libcamera_client libstagefright_foundation \
- libgui libdl libaudioutils libaudioclient
+ libgui libdl libaudioutils libaudioclient \
+ libmedia_helper
LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder libsonivox
-LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper
-
# for memory heap analysis
LOCAL_STATIC_LIBRARIES := libc_malloc_debug_backtrace libc_logging
diff --git a/media/libmedia/IMediaExtractor.cpp b/media/libmedia/IMediaExtractor.cpp
index 94c96f6..7776313 100644
--- a/media/libmedia/IMediaExtractor.cpp
+++ b/media/libmedia/IMediaExtractor.cpp
@@ -34,8 +34,6 @@
GETTRACKMETADATA,
GETMETADATA,
FLAGS,
- SETDRMFLAG,
- GETDRMFLAG,
GETDRMTRACKINFO,
SETUID,
NAME
@@ -101,13 +99,6 @@
return 0;
}
- virtual void setDrmFlag(bool flag __unused) {
- ALOGV("setDrmFlag NOT IMPLEMENTED");
- }
- virtual bool getDrmFlag() {
- ALOGV("getDrmFlag NOT IMPLEMENTED");
- return false;
- }
virtual char* getDrmTrackInfo(size_t trackID __unused, int *len __unused) {
ALOGV("getDrmTrackInfo NOT IMPLEMENTED");
return NULL;
diff --git a/media/libmediaplayerservice/ActivityManager.cpp b/media/libmediaplayerservice/ActivityManager.cpp
index 0e6cf7b..438d422 100644
--- a/media/libmediaplayerservice/ActivityManager.cpp
+++ b/media/libmediaplayerservice/ActivityManager.cpp
@@ -14,18 +14,14 @@
* limitations under the License.
*/
-#include <unistd.h>
+#include <binder/IActivityManager.h>
#include <binder/IBinder.h>
#include <binder/IServiceManager.h>
-#include <binder/Parcel.h>
-#include <utils/String8.h>
#include "ActivityManager.h"
namespace android {
-const uint32_t OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION;
-
// Perform ContentProvider.openFile() on the given URI, returning
// the resulting native file descriptor. Returns < 0 on error.
int openContentProviderFile(const String16& uri)
@@ -33,26 +29,10 @@
int fd = -1;
sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> am = sm->getService(String16("activity"));
+ sp<IBinder> binder = sm->getService(String16("activity"));
+ sp<IActivityManager> am = interface_cast<IActivityManager>(binder);
if (am != NULL) {
- Parcel data, reply;
- data.writeInterfaceToken(String16("android.app.IActivityManager"));
- data.writeString16(uri);
- status_t ret = am->transact(OPEN_CONTENT_URI_TRANSACTION, data, &reply);
- if (ret == NO_ERROR) {
- int32_t exceptionCode = reply.readExceptionCode();
- if (!exceptionCode) {
- // Success is indicated here by a nonzero int followed by the fd;
- // failure by a zero int with no data following.
- if (reply.readInt32() != 0) {
- fd = dup(reply.readFileDescriptor());
- }
- } else {
- // An exception was thrown back; fall through to return failure
- ALOGD("openContentUri(%s) caught exception %d\n",
- String8(uri).string(), exceptionCode);
- }
- }
+ fd = am->openContentUri(uri);
}
return fd;
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 3ad461c..065738e 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -577,7 +577,7 @@
mLoop = false;
mStatus = NO_INIT;
mAudioSessionId = audioSessionId;
- mUID = uid;
+ mUid = uid;
mRetransmitEndpointValid = false;
mAudioAttributes = NULL;
@@ -643,7 +643,7 @@
}
if (p != NULL) {
- p->setUID(mUID);
+ p->setUID(mUid);
}
return p;
@@ -1422,7 +1422,7 @@
#undef LOG_TAG
#define LOG_TAG "AudioSink"
-MediaPlayerService::AudioOutput::AudioOutput(audio_session_t sessionId, int uid, int pid,
+MediaPlayerService::AudioOutput::AudioOutput(audio_session_t sessionId, uid_t uid, int pid,
const audio_attributes_t* attr)
: mCallback(NULL),
mCallbackCookie(NULL),
@@ -2384,7 +2384,7 @@
return;
}
- int uid = IPCThreadState::self()->getCallingUid();
+ uid_t uid = IPCThreadState::self()->getCallingUid();
if (uid == AID_MEDIA) {
return;
}
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 8a6ada0..db182b2 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -75,7 +75,7 @@
class CallbackData;
public:
- AudioOutput(audio_session_t sessionId, int uid, int pid,
+ AudioOutput(audio_session_t sessionId, uid_t uid, int pid,
const audio_attributes_t * attr);
virtual ~AudioOutput();
@@ -152,7 +152,7 @@
float mMsecsPerFrame;
size_t mFrameSize;
audio_session_t mSessionId;
- int mUid;
+ uid_t mUid;
int mPid;
float mSendLevel;
int mAuxEffectId;
@@ -407,7 +407,7 @@
int32_t mConnId;
audio_session_t mAudioSessionId;
audio_attributes_t * mAudioAttributes;
- uid_t mUID;
+ uid_t mUid;
sp<ANativeWindow> mConnectedWindow;
sp<IBinder> mConnectedWindowBinder;
struct sockaddr_in mRetransmitEndpoint;
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index 12656cf..83ef80a 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -22,7 +22,7 @@
namespace android {
-class MediaRecorderBase;
+struct MediaRecorderBase;
class MediaPlayerService;
class ICameraRecordingProxy;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 8761e9d..d1d1077 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -145,10 +145,6 @@
return UNKNOWN_ERROR;
}
- if (extractor->getDrmFlag()) {
- checkDrmStatus(mDataSource);
- }
-
mFileMeta = extractor->getMetaData();
if (mFileMeta != NULL) {
int64_t duration;
@@ -262,18 +258,6 @@
return OK;
}
-void NuPlayer::GenericSource::checkDrmStatus(const sp<DataSource>& dataSource) {
- dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
- if (mDecryptHandle != NULL) {
- CHECK(mDrmManagerClient);
- if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
- sp<AMessage> msg = dupNotify();
- msg->setInt32("what", kWhatDrmNoLicense);
- msg->post();
- }
- }
-}
-
int64_t NuPlayer::GenericSource::getLastReadPosition() {
if (mAudioTrack.mSource != NULL) {
return mAudioTimeUs;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 38d8616..a14056f 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -234,7 +234,6 @@
void resetDataSource();
status_t initFromDataSource();
- void checkDrmStatus(const sp<DataSource>& dataSource);
int64_t getLastReadPosition();
void setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position);
diff --git a/media/liboboe/Android.bp b/media/liboboe/Android.bp
new file mode 100644
index 0000000..0d22e65
--- /dev/null
+++ b/media/liboboe/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2016 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.
+
+ndk_headers {
+ name: "libOboe_headers",
+ from: "include",
+ to: "",
+ srcs: ["include/oboe/*.h"],
+ license: "include/oboe/NOTICE",
+}
+
+ndk_library {
+ name: "liboboe.ndk",
+ symbol_file: "liboboe.map.txt",
+ first_version: "26",
+}
diff --git a/media/liboboe/Android.mk b/media/liboboe/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/media/liboboe/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/media/liboboe/include/oboe/NOTICE b/media/liboboe/include/oboe/NOTICE
new file mode 100644
index 0000000..d6c0922
--- /dev/null
+++ b/media/liboboe/include/oboe/NOTICE
@@ -0,0 +1,13 @@
+Copyright (C) 2016 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.
diff --git a/media/liboboe/include/oboe/OboeAudio.h b/media/liboboe/include/oboe/OboeAudio.h
index 6804ce8..788cf5f 100644
--- a/media/liboboe/include/oboe/OboeAudio.h
+++ b/media/liboboe/include/oboe/OboeAudio.h
@@ -29,11 +29,14 @@
typedef int32_t OboeDeviceId;
typedef oboe_handle_t OboeStream;
typedef oboe_handle_t OboeStreamBuilder;
-typedef oboe_handle_t OboeThread;
#define OBOE_STREAM_NONE ((OboeStream)OBOE_HANDLE_INVALID)
#define OBOE_STREAM_BUILDER_NONE ((OboeStreamBuilder)OBOE_HANDLE_INVALID)
+/* OBOE_API will probably get defined in a Makefile for a specific platform. */
+#ifndef OBOE_API
+#define OBOE_API /* for exporting symbols */
+#endif
// ============================================================
// Audio System
@@ -42,7 +45,7 @@
/**
* @return time in the same clock domain as the timestamps
*/
-oboe_nanoseconds_t Oboe_getNanoseconds(oboe_clockid_t clockid);
+OBOE_API oboe_nanoseconds_t Oboe_getNanoseconds(oboe_clockid_t clockid);
/**
* The text is the ASCII symbol corresponding to the returnCode,
@@ -52,7 +55,7 @@
*
* @return pointer to a text representation of an Oboe result code.
*/
-const char * Oboe_convertResultToText(oboe_result_t returnCode);
+OBOE_API const char * Oboe_convertResultToText(oboe_result_t returnCode);
/**
* The text is the ASCII symbol corresponding to the stream state,
@@ -62,7 +65,7 @@
*
* @return pointer to a text representation of an Oboe state.
*/
-const char * Oboe_convertStreamStateToText(oboe_stream_state_t state);
+OBOE_API const char * Oboe_convertStreamStateToText(oboe_stream_state_t state);
// ============================================================
// StreamBuilder
@@ -80,7 +83,7 @@
*
* OboeStreamBuilder_delete() must be called when you are done using the builder.
*/
-oboe_result_t Oboe_createStreamBuilder(OboeStreamBuilder *builder);
+OBOE_API oboe_result_t Oboe_createStreamBuilder(OboeStreamBuilder *builder);
/**
* Request an audio device identified device using an ID.
@@ -91,7 +94,8 @@
*
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_setDeviceId(OboeStreamBuilder builder, OboeDeviceId deviceId);
+OBOE_API oboe_result_t OboeStreamBuilder_setDeviceId(OboeStreamBuilder builder,
+ OboeDeviceId deviceId);
/**
* Request a sample rate in Hz.
@@ -106,14 +110,14 @@
*
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_setSampleRate(OboeStreamBuilder builder,
+OBOE_API oboe_result_t OboeStreamBuilder_setSampleRate(OboeStreamBuilder builder,
oboe_sample_rate_t sampleRate);
/**
* Returns sample rate in Hertz (samples per second).
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_getSampleRate(OboeStreamBuilder builder,
+OBOE_API oboe_result_t OboeStreamBuilder_getSampleRate(OboeStreamBuilder builder,
oboe_sample_rate_t *sampleRate);
@@ -128,7 +132,7 @@
*
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_setSamplesPerFrame(OboeStreamBuilder builder,
+OBOE_API oboe_result_t OboeStreamBuilder_setSamplesPerFrame(OboeStreamBuilder builder,
int32_t samplesPerFrame);
/**
@@ -138,7 +142,7 @@
* @param samplesPerFrame pointer to a variable to be set to samplesPerFrame.
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_getSamplesPerFrame(OboeStreamBuilder builder,
+OBOE_API oboe_result_t OboeStreamBuilder_getSamplesPerFrame(OboeStreamBuilder builder,
int32_t *samplesPerFrame);
@@ -148,12 +152,14 @@
*
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_setFormat(OboeStreamBuilder builder, oboe_audio_format_t format);
+OBOE_API oboe_result_t OboeStreamBuilder_setFormat(OboeStreamBuilder builder,
+ oboe_audio_format_t format);
/**
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_getFormat(OboeStreamBuilder builder, oboe_audio_format_t *format);
+OBOE_API oboe_result_t OboeStreamBuilder_getFormat(OboeStreamBuilder builder,
+ oboe_audio_format_t *format);
/**
* Request a mode for sharing the device.
@@ -164,15 +170,15 @@
* @param sharingMode OBOE_SHARING_MODE_LEGACY or OBOE_SHARING_MODE_EXCLUSIVE
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_setSharingMode(OboeStreamBuilder builder,
- oboe_sharing_mode_t sharingMode);
+OBOE_API oboe_result_t OboeStreamBuilder_setSharingMode(OboeStreamBuilder builder,
+ oboe_sharing_mode_t sharingMode);
/**
* Return requested sharing mode.
* @return OBOE_OK or a negative error
*/
-oboe_result_t OboeStreamBuilder_getSharingMode(OboeStreamBuilder builder,
- oboe_sharing_mode_t *sharingMode);
+OBOE_API oboe_result_t OboeStreamBuilder_getSharingMode(OboeStreamBuilder builder,
+ oboe_sharing_mode_t *sharingMode);
/**
* Request the direction for a stream. The default is OBOE_DIRECTION_OUTPUT.
@@ -181,16 +187,16 @@
* @param direction OBOE_DIRECTION_OUTPUT or OBOE_DIRECTION_INPUT
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_setDirection(OboeStreamBuilder builder,
- oboe_direction_t direction);
+OBOE_API oboe_result_t OboeStreamBuilder_setDirection(OboeStreamBuilder builder,
+ oboe_direction_t direction);
/**
* @param builder handle provided by Oboe_createStreamBuilder()
* @param direction pointer to a variable to be set to the currently requested direction.
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_getDirection(OboeStreamBuilder builder,
- oboe_direction_t *direction);
+OBOE_API oboe_result_t OboeStreamBuilder_getDirection(OboeStreamBuilder builder,
+ oboe_direction_t *direction);
/**
* Open a stream based on the options in the StreamBuilder.
@@ -202,7 +208,8 @@
* @param stream pointer to a variable to receive the new stream handle
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_openStream(OboeStreamBuilder builder, OboeStream *stream);
+OBOE_API oboe_result_t OboeStreamBuilder_openStream(OboeStreamBuilder builder,
+ OboeStream *stream);
/**
* Delete the resources associated with the StreamBuilder.
@@ -210,7 +217,7 @@
* @param builder handle provided by Oboe_createStreamBuilder()
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStreamBuilder_delete(OboeStreamBuilder builder);
+OBOE_API oboe_result_t OboeStreamBuilder_delete(OboeStreamBuilder builder);
// ============================================================
// Stream Control
@@ -222,7 +229,7 @@
* @param stream handle provided by OboeStreamBuilder_openStream()
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_close(OboeStream stream);
+OBOE_API oboe_result_t OboeStream_close(OboeStream stream);
/**
* Asynchronously request to start playing the stream. For output streams, one should
@@ -233,7 +240,7 @@
* @param stream handle provided by OboeStreamBuilder_openStream()
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_requestStart(OboeStream stream);
+OBOE_API oboe_result_t OboeStream_requestStart(OboeStream stream);
/**
* Asynchronous request for the stream to pause.
@@ -244,7 +251,7 @@
* @param stream handle provided by OboeStreamBuilder_openStream()
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_requestPause(OboeStream stream);
+OBOE_API oboe_result_t OboeStream_requestPause(OboeStream stream);
/**
* Asynchronous request for the stream to flush.
@@ -256,7 +263,7 @@
* @param stream handle provided by OboeStreamBuilder_openStream()
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_requestFlush(OboeStream stream);
+OBOE_API oboe_result_t OboeStream_requestFlush(OboeStream stream);
/**
* Asynchronous request for the stream to stop.
@@ -266,7 +273,7 @@
* @param stream handle provided by OboeStreamBuilder_openStream()
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_requestStop(OboeStream stream);
+OBOE_API oboe_result_t OboeStream_requestStop(OboeStream stream);
/**
* Query the current state, eg. OBOE_STREAM_STATE_PAUSING
@@ -275,7 +282,7 @@
* @param state pointer to a variable that will be set to the current state
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getState(OboeStream stream, oboe_stream_state_t *state);
+OBOE_API oboe_result_t OboeStream_getState(OboeStream stream, oboe_stream_state_t *state);
/**
* Wait until the current state no longer matches the input state.
@@ -295,7 +302,7 @@
* @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_waitForStateChange(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_waitForStateChange(OboeStream stream,
oboe_stream_state_t inputState,
oboe_stream_state_t *nextState,
oboe_nanoseconds_t timeoutNanoseconds);
@@ -322,9 +329,9 @@
* @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
* @return The number of frames actually written or a negative error.
*/
-oboe_result_t OboeStream_read(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_read(OboeStream stream,
void *buffer,
- int32_t numFrames,
+ oboe_size_frames_t numFrames,
oboe_nanoseconds_t timeoutNanoseconds);
/**
@@ -345,9 +352,9 @@
* @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
* @return The number of frames actually written or a negative error.
*/
-oboe_result_t OboeStream_write(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_write(OboeStream stream,
const void *buffer,
- int32_t numFrames,
+ oboe_size_frames_t numFrames,
oboe_nanoseconds_t timeoutNanoseconds);
@@ -356,31 +363,33 @@
// ============================================================
/**
- * Create a thread with special properties for low latency audio performance.
- * This thread can be used to implement a callback API.
+ * Create a thread associated with a stream. The thread has special properties for
+ * low latency audio performance. This thread can be used to implement a callback API.
+ *
+ * Only one thread may be associated with a stream.
*
* Note that this API is in flux.
*
- * @param threadHandlePtr a pointer to receive a thread handle
+ * @param stream A stream created using OboeStreamBuilder_openStream().
* @param periodNanoseconds the estimated period at which the audio thread will need to wake up
* @param start_routine your thread entry point
* @param arg an argument that will be passed to your thread entry point
* @return OBOE_OK or a negative error.
*/
-oboe_result_t Oboe_createAudioThread(OboeThread *threadHandlePtr,
+OBOE_API oboe_result_t OboeStream_createThread(OboeStream stream,
oboe_nanoseconds_t periodNanoseconds,
- void *(*start_routine)(void *), void *arg);
+ void *(*startRoutine)(void *), void *arg);
/**
* Wait until the thread exits or an error occurs.
* The thread handle will be deleted.
*
- * @param thread the thread handle passed back from Oboe_createAudioThread()
+ * @param stream A stream created using OboeStreamBuilder_openStream().
* @param returnArg a pointer to a variable to receive the return value
* @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
* @return OBOE_OK or a negative error.
*/
-oboe_result_t Oboe_joinAudioThread(OboeThread thread,
+OBOE_API oboe_result_t OboeStream_joinThread(OboeStream stream,
void **returnArg,
oboe_nanoseconds_t timeoutNanoseconds);
@@ -398,10 +407,13 @@
* This cannot be set higher than OboeStream_getBufferCapacity().
*
* @param stream handle provided by OboeStreamBuilder_openStream()
- * @param frames requested number of frames that can be filled without blocking
- * @return actual number of frames or a negative error
+ * @param requestedFrames requested number of frames that can be filled without blocking
+ * @return actualFrames receives final number of frames
+ * @return OBOE_OK or a negative error
*/
-oboe_result_t OboeStream_setBufferSize(OboeStream stream, oboe_size_frames_t frames);
+OBOE_API oboe_result_t OboeStream_setBufferSize(OboeStream stream,
+ oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames);
/**
* Query the maximum number of frames that can be filled without blocking.
@@ -410,7 +422,7 @@
* @param frames pointer to variable to receive the buffer size
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getBufferSize(OboeStream stream, oboe_size_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getBufferSize(OboeStream stream, oboe_size_frames_t *frames);
/**
* Query the number of frames that are read or written by the endpoint at one time.
@@ -419,7 +431,7 @@
* @param frames pointer to variable to receive the burst size
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getFramesPerBurst(OboeStream stream, oboe_size_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getFramesPerBurst(OboeStream stream, oboe_size_frames_t *frames);
/**
* Query maximum buffer capacity in frames.
@@ -428,7 +440,7 @@
* @param frames pointer to variable to receive the buffer capacity
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getBufferCapacity(OboeStream stream, oboe_size_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getBufferCapacity(OboeStream stream, oboe_size_frames_t *frames);
/**
* An XRun is an Underrun or an Overrun.
@@ -443,14 +455,14 @@
* @param xRunCount pointer to variable to receive the underrun or overrun count
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getXRunCount(OboeStream stream, int32_t *xRunCount);
+OBOE_API oboe_result_t OboeStream_getXRunCount(OboeStream stream, int32_t *xRunCount);
/**
* @param stream handle provided by OboeStreamBuilder_openStream()
* @param sampleRate pointer to variable to receive the actual sample rate
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getSampleRate(OboeStream stream, int32_t *sampleRate);
+OBOE_API oboe_result_t OboeStream_getSampleRate(OboeStream stream, oboe_sample_rate_t *sampleRate);
/**
* The samplesPerFrame is also known as channelCount.
@@ -459,14 +471,14 @@
* @param samplesPerFrame pointer to variable to receive the actual samples per frame
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getSamplesPerFrame(OboeStream stream, int32_t *samplesPerFrame);
+OBOE_API oboe_result_t OboeStream_getSamplesPerFrame(OboeStream stream, int32_t *samplesPerFrame);
/**
* @param stream handle provided by OboeStreamBuilder_openStream()
* @param format pointer to variable to receive the actual data format
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getFormat(OboeStream stream, oboe_audio_format_t *format);
+OBOE_API oboe_result_t OboeStream_getFormat(OboeStream stream, oboe_audio_format_t *format);
/**
* Provide actual sharing mode.
@@ -474,7 +486,7 @@
* @param sharingMode pointer to variable to receive the actual sharing mode
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getSharingMode(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_getSharingMode(OboeStream stream,
oboe_sharing_mode_t *sharingMode);
/**
@@ -482,7 +494,7 @@
* @param direction pointer to a variable to be set to the current direction.
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getDirection(OboeStream stream, uint32_t *direction);
+OBOE_API oboe_result_t OboeStream_getDirection(OboeStream stream, oboe_direction_t *direction);
/**
* Passes back the number of frames that have been written since the stream was created.
@@ -495,7 +507,8 @@
* @param frames pointer to variable to receive the frames written
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getFramesWritten(OboeStream stream, oboe_position_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getFramesWritten(OboeStream stream,
+ oboe_position_frames_t *frames);
/**
* Passes back the number of frames that have been read since the stream was created.
@@ -508,7 +521,7 @@
* @param frames pointer to variable to receive the frames written
* @return OBOE_OK or a negative error.
*/
-oboe_result_t OboeStream_getFramesRead(OboeStream stream, oboe_position_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getFramesRead(OboeStream stream, oboe_position_frames_t *frames);
/**
* Passes back the time at which a particular frame was presented.
@@ -527,12 +540,12 @@
* The position and time passed back are monotonically increasing.
*
* @param stream A handle provided by OboeStreamBuilder_openStream()
- * @param clockId OBOE_CLOCK_MONOTONIC or OBOE_CLOCK_BOOTTIME
+ * @param clockid OBOE_CLOCK_MONOTONIC or OBOE_CLOCK_BOOTTIME
* @param framePosition pointer to a variable to receive the position
* @param timeNanoseconds pointer to a variable to receive the time
* @return OBOE_OK or a negative error
*/
-oboe_result_t OboeStream_getTimestamp(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_getTimestamp(OboeStream stream,
oboe_clockid_t clockid,
oboe_position_frames_t *framePosition,
oboe_nanoseconds_t *timeNanoseconds);
diff --git a/media/liboboe/include/oboe/OboeDefinitions.h b/media/liboboe/include/oboe/OboeDefinitions.h
index 79fef3a..d80c958 100644
--- a/media/liboboe/include/oboe/OboeDefinitions.h
+++ b/media/liboboe/include/oboe/OboeDefinitions.h
@@ -28,7 +28,10 @@
typedef int32_t oboe_sample_rate_t;
/** This is used for small quantities such as the number of frames in a buffer. */
typedef int32_t oboe_size_frames_t;
-/** This is used for large quantities, such as the number of frames that have
+/** This is used for small quantities such as the number of bytes in a frame. */
+typedef int32_t oboe_size_bytes_t;
+/**
+ * This is used for large quantities, such as the number of frames that have
* been played since a stream was started.
* At 48000 Hz, a 32-bit integer would wrap around in just over 12 hours.
*/
@@ -85,7 +88,8 @@
/**
* Fields packed into oboe_audio_format_t, from most to least significant bits.
- * Reserved:8
+ * Invalid:1
+ * Reserved:7
* Wrapper:8
* Content:8
* Data Type:8
@@ -97,7 +101,7 @@
OBOE_AUDIO_FORMAT(dataType, content, OBOE_AUDIO_WRAPPER_NONE)
#define OBOE_AUDIO_FORMAT_DATA_TYPE(format) \
- (format & 0x0FF)
+ ((oboe_datatype_t)(format & 0x0FF))
// Define some common formats.
#define OBOE_AUDIO_FORMAT_PCM16 \
@@ -106,6 +110,9 @@
OBOE_AUDIO_FORMAT_RAW(OBOE_AUDIO_DATATYPE_FLOAT32, OBOE_AUDIO_CONTENT_PCM)
#define OBOE_AUDIO_FORMAT_PCM824 \
OBOE_AUDIO_FORMAT_RAW(OBOE_AUDIO_DATATYPE_INT824, OBOE_AUDIO_CONTENT_PCM)
+#define OBOE_AUDIO_FORMAT_PCM32 \
+ OBOE_AUDIO_FORMAT_RAW(OBOE_AUDIO_DATATYPE_INT32, OBOE_AUDIO_CONTENT_PCM)
+#define OBOE_AUDIO_FORMAT_INVALID ((oboe_audio_format_t)-1)
enum {
OBOE_OK,
@@ -126,7 +133,8 @@
OBOE_ERROR_NULL,
OBOE_ERROR_TIMEOUT,
OBOE_ERROR_WOULD_BLOCK,
- OBOE_ERROR_INVALID_ORDER
+ OBOE_ERROR_INVALID_ORDER,
+ OBOE_ERROR_OUT_OF_RANGE
};
typedef enum {
diff --git a/media/liboboe/liboboe.map.txt b/media/liboboe/liboboe.map.txt
new file mode 100644
index 0000000..229038f
--- /dev/null
+++ b/media/liboboe/liboboe.map.txt
@@ -0,0 +1,46 @@
+LIBOBOE {
+ global:
+ Oboe_getNanoseconds;
+ Oboe_convertResultToText;
+ Oboe_convertStreamStateToText;
+ Oboe_createStreamBuilder;
+ OboeStreamBuilder_setDeviceId;
+ OboeStreamBuilder_setSampleRate;
+ OboeStreamBuilder_getSampleRate;
+ OboeStreamBuilder_setSamplesPerFrame;
+ OboeStreamBuilder_getSamplesPerFrame;
+ OboeStreamBuilder_setFormat;
+ OboeStreamBuilder_getFormat;
+ OboeStreamBuilder_setSharingMode;
+ OboeStreamBuilder_getSharingMode;
+ OboeStreamBuilder_setDirection;
+ OboeStreamBuilder_getDirection;
+ OboeStreamBuilder_openStream;
+ OboeStreamBuilder_delete;
+ OboeStream_close;
+ OboeStream_requestStart;
+ OboeStream_requestPause;
+ OboeStream_requestFlush;
+ OboeStream_requestStop;
+ OboeStream_getState;
+ OboeStream_waitForStateChange;
+ OboeStream_read;
+ OboeStream_write;
+ Oboe_createAudioThread;
+ Oboe_joinAudioThread;
+ OboeStream_setBufferSize;
+ OboeStream_getBufferSize;
+ OboeStream_getFramesPerBurst;
+ OboeStream_getBufferCapacity;
+ OboeStream_getXRunCount;
+ OboeStream_getSampleRate;
+ OboeStream_getSamplesPerFrame;
+ OboeStream_getFormat;
+ OboeStream_getSharingMode;
+ OboeStream_getDirection;
+ OboeStream_getFramesWritten;
+ OboeStream_getFramesRead;
+ OboeStream_getTimestamp;
+ local:
+ *;
+};
diff --git a/media/liboboe/src/Android.mk b/media/liboboe/src/Android.mk
new file mode 100644
index 0000000..7b9a906
--- /dev/null
+++ b/media/liboboe/src/Android.mk
@@ -0,0 +1,70 @@
+LOCAL_PATH:= $(call my-dir)
+
+# ======================= STATIC LIBRARY ==========================
+# This is being built because it make Oboe testing very easy with a complete executable.
+# TODO Remove this target later, when not needed.
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := liboboe
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/native/include \
+ system/core/base/include \
+ frameworks/native/media/liboboe/include/include \
+ frameworks/av/media/liboboe/include \
+ $(LOCAL_PATH)/core \
+ $(LOCAL_PATH)/utility \
+ $(LOCAL_PATH)/legacy
+
+LOCAL_SRC_FILES += core/AudioStream.cpp
+LOCAL_SRC_FILES += core/AudioStreamBuilder.cpp
+LOCAL_SRC_FILES += core/OboeAudio.cpp
+LOCAL_SRC_FILES += legacy/AudioStreamRecord.cpp
+LOCAL_SRC_FILES += legacy/AudioStreamTrack.cpp
+LOCAL_SRC_FILES += utility/HandleTracker.cpp
+LOCAL_SRC_FILES += utility/OboeUtilities.cpp
+
+LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CFLAGS += -Wall -Werror
+# By default, all symbols are hidden.
+LOCAL_CFLAGS += -fvisibility=hidden
+# OBOE_API is used to explicitly export a function or a variable as a visible symbol.
+LOCAL_CFLAGS += -DOBOE_API='__attribute__((visibility("default")))'
+
+include $(BUILD_STATIC_LIBRARY)
+
+# ======================= SHARED LIBRARY ==========================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := liboboe
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/native/include \
+ system/core/base/include \
+ frameworks/native/media/liboboe/include/include \
+ frameworks/av/media/liboboe/include \
+ $(LOCAL_PATH)/core \
+ $(LOCAL_PATH)/utility \
+ $(LOCAL_PATH)/legacy
+
+LOCAL_SRC_FILES += core/AudioStream.cpp
+LOCAL_SRC_FILES += core/AudioStreamBuilder.cpp
+LOCAL_SRC_FILES += core/OboeAudio.cpp
+LOCAL_SRC_FILES += legacy/AudioStreamRecord.cpp
+LOCAL_SRC_FILES += legacy/AudioStreamTrack.cpp
+LOCAL_SRC_FILES += utility/HandleTracker.cpp
+LOCAL_SRC_FILES += utility/OboeUtilities.cpp
+
+LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CFLAGS += -Wall -Werror
+# By default, all symbols are hidden.
+LOCAL_CFLAGS += -fvisibility=hidden
+# OBOE_API is used to explicitly export a function or a variable as a visible symbol.
+LOCAL_CFLAGS += -DOBOE_API='__attribute__((visibility("default")))'
+
+LOCAL_SHARED_LIBRARIES := libaudioclient liblog libutils
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/liboboe/src/core/AudioStream.cpp b/media/liboboe/src/core/AudioStream.cpp
new file mode 100644
index 0000000..f154002
--- /dev/null
+++ b/media/liboboe/src/core/AudioStream.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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 "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <oboe/OboeAudio.h>
+
+#include "AudioStreamBuilder.h"
+#include "AudioStream.h"
+#include "AudioClock.h"
+
+using namespace oboe;
+
+/*
+ * AudioStream
+ */
+AudioStream::AudioStream() {
+}
+
+oboe_result_t AudioStream::open(const AudioStreamBuilder& builder)
+{
+ // TODO validate parameters.
+ // Copy parameters from the Builder because the Builder may be deleted after this call.
+ mSamplesPerFrame = builder.getSamplesPerFrame();
+ mSampleRate = builder.getSampleRate();
+ mDeviceId = builder.getDeviceId();
+ mFormat = builder.getFormat();
+ mSharingMode = builder.getSharingMode();
+ return OBOE_OK;
+}
+
+AudioStream::~AudioStream() {
+ close();
+}
+
+oboe_result_t AudioStream::waitForStateTransition(oboe_stream_state_t startingState,
+ oboe_stream_state_t endingState,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ oboe_stream_state_t state = getState();
+ oboe_stream_state_t nextState = state;
+ if (state == startingState && state != endingState) {
+ oboe_result_t result = waitForStateChange(state, &nextState, timeoutNanoseconds);
+ if (result != OBOE_OK) {
+ return result;
+ }
+ }
+// It's OK if the expected transition has already occurred.
+// But if we reach an unexpected state then that is an error.
+ if (nextState != endingState) {
+ return OBOE_ERROR_UNEXPECTED_STATE;
+ } else {
+ return OBOE_OK;
+ }
+}
+
+oboe_result_t AudioStream::waitForStateChange(oboe_stream_state_t currentState,
+ oboe_stream_state_t *nextState,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ // TODO replace this when similar functionality added to AudioTrack.cpp
+ oboe_nanoseconds_t durationNanos = 20 * OBOE_NANOS_PER_MILLISECOND;
+ oboe_stream_state_t state = getState();
+ while (state == currentState && timeoutNanoseconds > 0) {
+ if (durationNanos > timeoutNanoseconds) {
+ durationNanos = timeoutNanoseconds;
+ }
+ AudioClock::sleepForNanos(durationNanos);
+ timeoutNanoseconds -= durationNanos;
+
+ oboe_result_t result = updateState();
+ if (result != OBOE_OK) {
+ return result;
+ }
+
+ state = getState();
+ }
+ if (nextState != NULL) {
+ *nextState = state;
+ }
+ return (state == currentState) ? OBOE_ERROR_TIMEOUT : OBOE_OK;
+}
+
+oboe_result_t AudioStream::createThread(oboe_nanoseconds_t periodNanoseconds,
+ void *(*startRoutine)(void *), void *arg)
+{
+ if (mHasThread) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ if (startRoutine == NULL) {
+ return OBOE_ERROR_NULL;
+ }
+ int err = pthread_create(&mThread, NULL, startRoutine, arg);
+ if (err != 0) {
+ return OBOE_ERROR_INTERNAL;
+ } else {
+ mHasThread = true;
+ return OBOE_OK;
+ }
+}
+
+oboe_result_t AudioStream::joinThread(void **returnArg, oboe_nanoseconds_t timeoutNanoseconds)
+{
+ if (!mHasThread) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+#if 0
+ // TODO implement equivalent of pthread_timedjoin_np()
+ struct timespec abstime;
+ int err = pthread_timedjoin_np(mThread, returnArg, &abstime);
+#else
+ int err = pthread_join(mThread, returnArg);
+#endif
+ mHasThread = false;
+ // TODO Just leaked a thread?
+ return err ? OBOE_ERROR_INTERNAL : OBOE_OK;
+}
+
diff --git a/media/liboboe/src/core/AudioStream.h b/media/liboboe/src/core/AudioStream.h
new file mode 100644
index 0000000..9cb9b1b
--- /dev/null
+++ b/media/liboboe/src/core/AudioStream.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2016 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 OBOE_AUDIOSTREAM_H
+#define OBOE_AUDIOSTREAM_H
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <oboe/OboeAudio.h>
+#include "OboeUtilities.h"
+#include "MonotonicCounter.h"
+
+namespace oboe {
+
+class AudioStreamBuilder;
+
+/**
+ * Oboe audio stream.
+ */
+class AudioStream {
+public:
+
+ AudioStream();
+
+ virtual ~AudioStream();
+
+
+ // =========== Begin ABSTRACT methods ===========================
+
+ /* Asynchronous requests.
+ * Use waitForStateChange() to wait for completion.
+ */
+ virtual oboe_result_t requestStart() = 0;
+ virtual oboe_result_t requestPause() = 0;
+ virtual oboe_result_t requestFlush() = 0;
+ virtual oboe_result_t requestStop() = 0;
+
+ // TODO use oboe_clockid_t all the way down to AudioClock
+ virtual oboe_result_t getTimestamp(clockid_t clockId,
+ oboe_position_frames_t *framePosition,
+ oboe_nanoseconds_t *timeNanoseconds) = 0;
+
+
+ virtual oboe_result_t updateState() = 0;
+
+
+ // =========== End ABSTRACT methods ===========================
+
+ virtual oboe_result_t waitForStateChange(oboe_stream_state_t currentState,
+ oboe_stream_state_t *nextState,
+ oboe_nanoseconds_t timeoutNanoseconds);
+
+ /**
+ * Open the stream using the parameters in the builder.
+ * Allocate the necessary resources.
+ */
+ virtual oboe_result_t open(const AudioStreamBuilder& builder);
+
+ /**
+ * Close the stream and deallocate any resources from the open() call.
+ * It is safe to call close() multiple times.
+ */
+ virtual oboe_result_t close() {
+ return OBOE_OK;
+ }
+
+ virtual oboe_result_t setBufferSize(oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames) {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+ virtual oboe_result_t createThread(oboe_nanoseconds_t periodNanoseconds,
+ void *(*start_routine)(void *), void *arg);
+
+ virtual oboe_result_t joinThread(void **returnArg, oboe_nanoseconds_t timeoutNanoseconds);
+
+ // ============== Queries ===========================
+
+ virtual oboe_stream_state_t getState() const {
+ return mState;
+ }
+
+ virtual oboe_size_frames_t getBufferSize() const {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+ virtual oboe_size_frames_t getBufferCapacity() const {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+ virtual oboe_size_frames_t getFramesPerBurst() const {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+ virtual int32_t getXRunCount() const {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+ bool isPlaying() const {
+ return mState == OBOE_STREAM_STATE_STARTING || mState == OBOE_STREAM_STATE_STARTED;
+ }
+
+ oboe_result_t getSampleRate() const {
+ return mSampleRate;
+ }
+
+ oboe_audio_format_t getFormat() const {
+ return mFormat;
+ }
+
+ oboe_result_t getSamplesPerFrame() const {
+ return mSamplesPerFrame;
+ }
+
+ OboeDeviceId getDeviceId() const {
+ return mDeviceId;
+ }
+
+ oboe_sharing_mode_t getSharingMode() const {
+ return mSharingMode;
+ }
+
+ oboe_direction_t getDirection() const {
+ return mDirection;
+ }
+
+ oboe_size_bytes_t getBytesPerFrame() const {
+ return mSamplesPerFrame * getBytesPerSample();
+ }
+
+ oboe_size_bytes_t getBytesPerSample() const {
+ return OboeConvert_formatToSizeInBytes(mFormat);
+ }
+
+ virtual oboe_position_frames_t getFramesWritten() {
+ return mFramesWritten.get();
+ }
+
+ virtual oboe_position_frames_t getFramesRead() {
+ return mFramesRead.get();
+ }
+
+
+ // ============== I/O ===========================
+ // A Stream will only implement read() or write() depending on its direction.
+ virtual oboe_result_t write(const void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds) {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+ virtual oboe_result_t read(void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds) {
+ return OBOE_ERROR_UNIMPLEMENTED;
+ }
+
+protected:
+
+ virtual oboe_position_frames_t incrementFramesWritten(oboe_size_frames_t frames) {
+ return static_cast<oboe_position_frames_t>(mFramesWritten.increment(frames));
+ }
+
+ virtual oboe_position_frames_t incrementFramesRead(oboe_size_frames_t frames) {
+ return static_cast<oboe_position_frames_t>(mFramesRead.increment(frames));
+ }
+
+ /**
+ * Wait for a transition from one state to another.
+ * @return OBOE_OK if the endingState was observed, or OBOE_ERROR_UNEXPECTED_STATE
+ * if any state that was not the startingState or endingState was observed
+ * or OBOE_ERROR_TIMEOUT
+ */
+ virtual oboe_result_t waitForStateTransition(oboe_stream_state_t startingState,
+ oboe_stream_state_t endingState,
+ oboe_nanoseconds_t timeoutNanoseconds);
+
+ /**
+ * This should not be called after the open() call.
+ */
+ void setSampleRate(oboe_sample_rate_t sampleRate) {
+ mSampleRate = sampleRate;
+ }
+
+ /**
+ * This should not be called after the open() call.
+ */
+ void setSamplesPerFrame(int32_t samplesPerFrame) {
+ mSamplesPerFrame = samplesPerFrame;
+ }
+
+ /**
+ * This should not be called after the open() call.
+ */
+ void setSharingMode(oboe_sharing_mode_t sharingMode) {
+ mSharingMode = sharingMode;
+ }
+
+ /**
+ * This should not be called after the open() call.
+ */
+ void setFormat(oboe_audio_format_t format) {
+ mFormat = format;
+ }
+
+ void setState(oboe_stream_state_t state) {
+ mState = state;
+ }
+
+ MonotonicCounter mFramesWritten;
+ MonotonicCounter mFramesRead;
+
+private:
+ // These do not change after open().
+ int32_t mSamplesPerFrame = OBOE_UNSPECIFIED;
+ oboe_sample_rate_t mSampleRate = OBOE_UNSPECIFIED;
+ oboe_stream_state_t mState = OBOE_STREAM_STATE_UNINITIALIZED;
+ OboeDeviceId mDeviceId = OBOE_UNSPECIFIED;
+ oboe_sharing_mode_t mSharingMode = OBOE_SHARING_MODE_LEGACY;
+ oboe_audio_format_t mFormat = OBOE_UNSPECIFIED;
+ oboe_direction_t mDirection = OBOE_DIRECTION_OUTPUT;
+
+ bool mHasThread;
+ pthread_t mThread;
+};
+
+} /* namespace oboe */
+
+#endif /* OBOE_AUDIOSTREAM_H */
diff --git a/media/liboboe/src/core/AudioStreamBuilder.cpp b/media/liboboe/src/core/AudioStreamBuilder.cpp
new file mode 100644
index 0000000..56e6706
--- /dev/null
+++ b/media/liboboe/src/core/AudioStreamBuilder.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <sys/types.h>
+#include "AudioStream.h"
+#include "AudioStreamBuilder.h"
+#include "AudioStreamRecord.h"
+#include "AudioStreamTrack.h"
+
+using namespace oboe;
+
+/*
+ * AudioStreamBuilder
+ */
+AudioStreamBuilder::AudioStreamBuilder() {
+}
+
+AudioStreamBuilder::~AudioStreamBuilder() {
+}
+
+oboe_result_t AudioStreamBuilder::build(AudioStream **streamPtr) {
+ // TODO Is there a better place to put the code that decides which class to use?
+ AudioStream *audioStream = nullptr;
+ const oboe_sharing_mode_t sharingMode = getSharingMode();
+ switch (getDirection()) {
+ case OBOE_DIRECTION_INPUT:
+ switch (sharingMode) {
+ case OBOE_SHARING_MODE_LEGACY:
+ audioStream = new AudioStreamRecord();
+ break;
+ default:
+ ALOGE("AudioStreamBuilder(): bad sharing mode = %d", sharingMode);
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ break;
+ }
+ break;
+ case OBOE_DIRECTION_OUTPUT:
+ switch (sharingMode) {
+ case OBOE_SHARING_MODE_LEGACY:
+ audioStream = new AudioStreamTrack();
+ break;
+ default:
+ ALOGE("AudioStreamBuilder(): bad sharing mode = %d", sharingMode);
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ break;
+ }
+ break;
+ default:
+ ALOGE("AudioStreamBuilder(): bad direction = %d", getDirection());
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ break;
+ }
+ if (audioStream == nullptr) {
+ return OBOE_ERROR_NO_MEMORY;
+ }
+ ALOGD("AudioStreamBuilder(): created audioStream = %p", audioStream);
+
+ // TODO maybe move this out of build and pass the builder to the constructors
+ // Open the stream using the parameters from the builder.
+ const oboe_result_t result = audioStream->open(*this);
+ if (result != OBOE_OK) {
+ delete audioStream;
+ } else {
+ *streamPtr = audioStream;
+ }
+ return result;
+}
diff --git a/media/liboboe/src/core/AudioStreamBuilder.h b/media/liboboe/src/core/AudioStreamBuilder.h
new file mode 100644
index 0000000..3f98ebb
--- /dev/null
+++ b/media/liboboe/src/core/AudioStreamBuilder.h
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+#ifndef OBOE_AUDIOSTREAMBUILDER_H
+#define OBOE_AUDIOSTREAMBUILDER_H
+
+#include <oboe/OboeAudio.h>
+#include "AudioStream.h"
+
+namespace oboe {
+
+/**
+ * Factory class for an AudioStream.
+ */
+class AudioStreamBuilder {
+public:
+ AudioStreamBuilder();
+
+ ~AudioStreamBuilder();
+
+ int getSamplesPerFrame() const {
+ return mSamplesPerFrame;
+ }
+
+ /**
+ * This is also known as channelCount.
+ */
+ AudioStreamBuilder *setSamplesPerFrame(int samplesPerFrame) {
+ mSamplesPerFrame = samplesPerFrame;
+ return this;
+ }
+
+ oboe_direction_t getDirection() const {
+ return mDirection;
+ }
+
+ AudioStreamBuilder *setDirection(oboe_direction_t direction) {
+ mDirection = direction;
+ return this;
+ }
+
+ oboe_sample_rate_t getSampleRate() const {
+ return mSampleRate;
+ }
+
+ AudioStreamBuilder *setSampleRate(oboe_sample_rate_t sampleRate) {
+ mSampleRate = sampleRate;
+ return this;
+ }
+
+ oboe_audio_format_t getFormat() const {
+ return mFormat;
+ }
+
+ AudioStreamBuilder *setFormat(oboe_audio_format_t format) {
+ mFormat = format;
+ return this;
+ }
+
+ oboe_sharing_mode_t getSharingMode() const {
+ return mSharingMode;
+ }
+
+ AudioStreamBuilder *setSharingMode(oboe_sharing_mode_t sharingMode) {
+ mSharingMode = sharingMode;
+ return this;
+ }
+
+ OboeDeviceId getDeviceId() const {
+ return mDeviceId;
+ }
+
+ AudioStreamBuilder *setDeviceId(OboeDeviceId deviceId) {
+ mDeviceId = deviceId;
+ return this;
+ }
+
+ oboe_result_t build(AudioStream **streamPtr);
+
+private:
+ int32_t mSamplesPerFrame = OBOE_UNSPECIFIED;
+ oboe_sample_rate_t mSampleRate = OBOE_UNSPECIFIED;
+ OboeDeviceId mDeviceId = OBOE_UNSPECIFIED; // TODO need better default
+ oboe_sharing_mode_t mSharingMode = OBOE_SHARING_MODE_LEGACY;
+ oboe_audio_format_t mFormat = OBOE_UNSPECIFIED;
+ oboe_direction_t mDirection = OBOE_DIRECTION_OUTPUT;
+};
+
+} /* namespace oboe */
+
+#endif /* OBOE_AUDIOSTREAMBUILDER_H */
diff --git a/media/liboboe/src/core/OboeAudio.cpp b/media/liboboe/src/core/OboeAudio.cpp
new file mode 100644
index 0000000..a02f226
--- /dev/null
+++ b/media/liboboe/src/core/OboeAudio.cpp
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2016 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 "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <time.h>
+#include <pthread.h>
+
+#include <oboe/OboeDefinitions.h>
+#include <oboe/OboeAudio.h>
+#include "AudioStreamBuilder.h"
+#include "AudioStream.h"
+#include "AudioClock.h"
+#include "HandleTracker.h"
+
+// temporary, as I stage in the MMAP/NOIRQ support, do not review
+#ifndef OBOE_SUPPORT_MMAP
+#define OBOE_SUPPORT_MMAP 0
+#endif
+
+#if OBOE_SUPPORT_MMAP
+#include "AudioStreamInternal.h"
+#include "OboeServiceGateway.h"
+#endif
+
+using namespace oboe;
+
+// This is not the maximum theoretic possible number of handles that the HandlerTracker
+// class could support; instead it is the maximum number of handles that we are configuring
+// for our HandleTracker instance (sHandleTracker).
+#define OBOE_MAX_HANDLES 64
+
+// Macros for common code that includes a return.
+// TODO Consider using do{}while(0) construct. I tried but it hung AndroidStudio
+#define CONVERT_BUILDER_HANDLE_OR_RETURN() \
+ convertOboeBuilderToStreamBuilder(builder); \
+ if (streamBuilder == nullptr) { \
+ return OBOE_ERROR_INVALID_HANDLE; \
+ }
+
+#define COMMON_GET_FROM_BUILDER_OR_RETURN(resultPtr) \
+ CONVERT_BUILDER_HANDLE_OR_RETURN() \
+ if ((resultPtr) == nullptr) { \
+ return OBOE_ERROR_NULL; \
+ }
+
+#define CONVERT_STREAM_HANDLE_OR_RETURN() \
+ convertOboeStreamToAudioStream(stream); \
+ if (audioStream == nullptr) { \
+ return OBOE_ERROR_INVALID_HANDLE; \
+ }
+
+#define COMMON_GET_FROM_STREAM_OR_RETURN(resultPtr) \
+ CONVERT_STREAM_HANDLE_OR_RETURN(); \
+ if ((resultPtr) == nullptr) { \
+ return OBOE_ERROR_NULL; \
+ }
+
+static HandleTracker sHandleTracker(OBOE_MAX_HANDLES);
+
+typedef enum
+{
+ OBOE_HANDLE_TYPE_STREAM,
+ OBOE_HANDLE_TYPE_STREAM_BUILDER,
+ OBOE_HANDLE_TYPE_COUNT
+} oboe_handle_type_t;
+static_assert(OBOE_HANDLE_TYPE_COUNT <= HANDLE_TRACKER_MAX_TYPES, "Too many handle types.");
+
+#if OBOE_SUPPORT_MMAP
+static OboeServiceGateway sOboeServiceGateway;
+#endif
+
+#define OBOE_CASE_ENUM(name) case name: return #name
+
+OBOE_API const char * Oboe_convertResultToText(oboe_result_t returnCode) {
+ switch (returnCode) {
+ OBOE_CASE_ENUM(OBOE_OK);
+ OBOE_CASE_ENUM(OBOE_ERROR_ILLEGAL_ARGUMENT);
+ OBOE_CASE_ENUM(OBOE_ERROR_INCOMPATIBLE);
+ OBOE_CASE_ENUM(OBOE_ERROR_INTERNAL);
+ OBOE_CASE_ENUM(OBOE_ERROR_INVALID_STATE);
+ OBOE_CASE_ENUM(OBOE_ERROR_INVALID_HANDLE);
+ OBOE_CASE_ENUM(OBOE_ERROR_INVALID_QUERY);
+ OBOE_CASE_ENUM(OBOE_ERROR_UNIMPLEMENTED);
+ OBOE_CASE_ENUM(OBOE_ERROR_UNAVAILABLE);
+ OBOE_CASE_ENUM(OBOE_ERROR_NO_FREE_HANDLES);
+ OBOE_CASE_ENUM(OBOE_ERROR_NO_MEMORY);
+ OBOE_CASE_ENUM(OBOE_ERROR_NULL);
+ OBOE_CASE_ENUM(OBOE_ERROR_TIMEOUT);
+ OBOE_CASE_ENUM(OBOE_ERROR_WOULD_BLOCK);
+ OBOE_CASE_ENUM(OBOE_ERROR_INVALID_ORDER);
+ OBOE_CASE_ENUM(OBOE_ERROR_OUT_OF_RANGE);
+ }
+ return "Unrecognized Oboe error.";
+}
+
+OBOE_API const char * Oboe_convertStreamStateToText(oboe_stream_state_t state) {
+ switch (state) {
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_UNINITIALIZED);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_OPEN);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_STARTING);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_STARTED);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_PAUSING);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_PAUSED);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_FLUSHING);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_FLUSHED);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_STOPPING);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_STOPPED);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_CLOSING);
+ OBOE_CASE_ENUM(OBOE_STREAM_STATE_CLOSED);
+ }
+ return "Unrecognized Oboe state.";
+}
+
+#undef OBOE_CASE_ENUM
+
+static AudioStream *convertOboeStreamToAudioStream(OboeStream stream)
+{
+ return (AudioStream *) sHandleTracker.get(OBOE_HANDLE_TYPE_STREAM,
+ (oboe_handle_t) stream);
+}
+
+static AudioStreamBuilder *convertOboeBuilderToStreamBuilder(OboeStreamBuilder builder)
+{
+ return (AudioStreamBuilder *) sHandleTracker.get(OBOE_HANDLE_TYPE_STREAM_BUILDER,
+ (oboe_handle_t) builder);
+}
+
+OBOE_API oboe_result_t Oboe_createStreamBuilder(OboeStreamBuilder *builder)
+{
+ ALOGD("Oboe_createStreamBuilder(): check sHandleTracker.isInitialized ()");
+ if (!sHandleTracker.isInitialized()) {
+ return OBOE_ERROR_NO_MEMORY;
+ }
+ AudioStreamBuilder *audioStreamBuilder = new AudioStreamBuilder();
+ if (audioStreamBuilder == nullptr) {
+ return OBOE_ERROR_NO_MEMORY;
+ }
+ ALOGD("Oboe_createStreamBuilder(): created AudioStreamBuilder = %p", audioStreamBuilder);
+ // TODO protect the put() with a Mutex
+ OboeStreamBuilder handle = sHandleTracker.put(OBOE_HANDLE_TYPE_STREAM_BUILDER,
+ audioStreamBuilder);
+ if (handle < 0) {
+ delete audioStreamBuilder;
+ return static_cast<oboe_result_t>(handle);
+ } else {
+ *builder = handle;
+ }
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setDeviceId(OboeStreamBuilder builder,
+ OboeDeviceId deviceId)
+{
+ AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+ streamBuilder->setDeviceId(deviceId);
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setSampleRate(OboeStreamBuilder builder,
+ oboe_sample_rate_t sampleRate)
+{
+ AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+ streamBuilder->setSampleRate(sampleRate);
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getSampleRate(OboeStreamBuilder builder,
+ oboe_sample_rate_t *sampleRate)
+{
+ AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(sampleRate);
+ *sampleRate = streamBuilder->getSampleRate();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setSamplesPerFrame(OboeStreamBuilder builder,
+ int32_t samplesPerFrame)
+{
+ AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+ streamBuilder->setSamplesPerFrame(samplesPerFrame);
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getSamplesPerFrame(OboeStreamBuilder builder,
+ int32_t *samplesPerFrame)
+{
+ AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(samplesPerFrame);
+ *samplesPerFrame = streamBuilder->getSamplesPerFrame();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setDirection(OboeStreamBuilder builder,
+ oboe_direction_t direction)
+{
+ AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+ streamBuilder->setDirection(direction);
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getDirection(OboeStreamBuilder builder,
+ oboe_direction_t *direction)
+{
+ AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(direction);
+ *direction = streamBuilder->getDirection();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setFormat(OboeStreamBuilder builder,
+ oboe_audio_format_t format)
+{
+ AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+ streamBuilder->setFormat(format);
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getFormat(OboeStreamBuilder builder,
+ oboe_audio_format_t *format)
+{
+ AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(format);
+ *format = streamBuilder->getFormat();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setSharingMode(OboeStreamBuilder builder,
+ oboe_sharing_mode_t sharingMode)
+{
+ AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+ if ((sharingMode < 0) || (sharingMode >= OBOE_SHARING_MODE_COUNT)) {
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ } else {
+ streamBuilder->setSharingMode(sharingMode);
+ return OBOE_OK;
+ }
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getSharingMode(OboeStreamBuilder builder,
+ oboe_sharing_mode_t *sharingMode)
+{
+ AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(sharingMode);
+ *sharingMode = streamBuilder->getSharingMode();
+ return OBOE_OK;
+}
+
+static oboe_result_t OboeInternal_openStream(AudioStreamBuilder *streamBuilder,
+ OboeStream *streamPtr)
+{
+ AudioStream *audioStream = nullptr;
+ oboe_result_t result = streamBuilder->build(&audioStream);
+ if (result != OBOE_OK) {
+ return result;
+ } else {
+ // Create a handle for referencing the object.
+ // TODO protect the put() with a Mutex
+ OboeStream handle = sHandleTracker.put(OBOE_HANDLE_TYPE_STREAM, audioStream);
+ if (handle < 0) {
+ delete audioStream;
+ return static_cast<oboe_result_t>(handle);
+ }
+ *streamPtr = handle;
+ return OBOE_OK;
+ }
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_openStream(OboeStreamBuilder builder,
+ OboeStream *streamPtr)
+{
+ ALOGD("OboeStreamBuilder_openStream(): builder = 0x%08X", builder);
+ AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(streamPtr);
+ return OboeInternal_openStream(streamBuilder, streamPtr);
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_delete(OboeStreamBuilder builder)
+{
+ // TODO protect the remove() with a Mutex
+ AudioStreamBuilder *streamBuilder = (AudioStreamBuilder *)
+ sHandleTracker.remove(OBOE_HANDLE_TYPE_STREAM_BUILDER, builder);
+ if (streamBuilder != nullptr) {
+ delete streamBuilder;
+ return OBOE_OK;
+ }
+ return OBOE_ERROR_INVALID_HANDLE;
+}
+
+OBOE_API oboe_result_t OboeStream_close(OboeStream stream)
+{
+ // TODO protect the remove() with a Mutex
+ AudioStream *audioStream = (AudioStream *)
+ sHandleTracker.remove(OBOE_HANDLE_TYPE_STREAM, (oboe_handle_t)stream);
+ if (audioStream != nullptr) {
+ audioStream->close();
+ delete audioStream;
+ return OBOE_OK;
+ }
+ return OBOE_ERROR_INVALID_HANDLE;
+}
+
+OBOE_API oboe_result_t OboeStream_requestStart(OboeStream stream)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ ALOGD("OboeStream_requestStart(0x%08X), audioStream = %p", stream, audioStream);
+ return audioStream->requestStart();
+}
+
+OBOE_API oboe_result_t OboeStream_requestPause(OboeStream stream)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ ALOGD("OboeStream_requestPause(0x%08X), audioStream = %p", stream, audioStream);
+ return audioStream->requestPause();
+}
+
+OBOE_API oboe_result_t OboeStream_requestFlush(OboeStream stream)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ ALOGD("OboeStream_requestFlush(0x%08X), audioStream = %p", stream, audioStream);
+ return audioStream->requestFlush();
+}
+
+OBOE_API oboe_result_t OboeStream_requestStop(OboeStream stream)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ ALOGD("OboeStream_requestStop(0x%08X), audioStream = %p", stream, audioStream);
+ return audioStream->requestStop();
+}
+
+OBOE_API oboe_result_t OboeStream_waitForStateChange(OboeStream stream,
+ oboe_stream_state_t inputState,
+ oboe_stream_state_t *nextState,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ return audioStream->waitForStateChange(inputState, nextState, timeoutNanoseconds);
+}
+
+// ============================================================
+// Stream - non-blocking I/O
+// ============================================================
+
+OBOE_API oboe_result_t OboeStream_read(OboeStream stream,
+ void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ if (buffer == nullptr) {
+ return OBOE_ERROR_NULL;
+ }
+ if (numFrames < 0) {
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ } else if (numFrames == 0) {
+ return 0;
+ }
+
+ oboe_result_t result = audioStream->read(buffer, numFrames, timeoutNanoseconds);
+ // ALOGD("OboeStream_read(): read returns %d", result);
+
+ return result;
+}
+
+OBOE_API oboe_result_t OboeStream_write(OboeStream stream,
+ const void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ if (buffer == nullptr) {
+ return OBOE_ERROR_NULL;
+ }
+ if (numFrames < 0) {
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ } else if (numFrames == 0) {
+ return 0;
+ }
+
+ oboe_result_t result = audioStream->write(buffer, numFrames, timeoutNanoseconds);
+ // ALOGD("OboeStream_write(): write returns %d", result);
+
+ return result;
+}
+
+// ============================================================
+// Miscellaneous
+// ============================================================
+
+OBOE_API oboe_result_t OboeStream_createThread(OboeStream stream,
+ oboe_nanoseconds_t periodNanoseconds,
+ void *(*startRoutine)(void *), void *arg)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ return audioStream->createThread(periodNanoseconds, startRoutine, arg);
+}
+
+OBOE_API oboe_result_t OboeStream_joinThread(OboeStream stream,
+ void **returnArg,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ return audioStream->joinThread(returnArg, timeoutNanoseconds);
+}
+
+// ============================================================
+// Stream - queries
+// ============================================================
+
+// TODO Use oboe_clockid_t all the way down through the C++ streams.
+static clockid_t OboeConvert_fromOboeClockId(oboe_clockid_t clockid)
+{
+ clockid_t hostClockId;
+ switch (clockid) {
+ case OBOE_CLOCK_MONOTONIC:
+ hostClockId = CLOCK_MONOTONIC;
+ break;
+ case OBOE_CLOCK_BOOTTIME:
+ hostClockId = CLOCK_BOOTTIME;
+ break;
+ default:
+ hostClockId = 0; // TODO review
+ }
+ return hostClockId;
+}
+
+oboe_nanoseconds_t Oboe_getNanoseconds(oboe_clockid_t clockid)
+{
+ clockid_t hostClockId = OboeConvert_fromOboeClockId(clockid);
+ return AudioClock::getNanoseconds(hostClockId);
+}
+
+OBOE_API oboe_result_t OboeStream_getSampleRate(OboeStream stream, oboe_sample_rate_t *sampleRate)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(sampleRate);
+ *sampleRate = audioStream->getSampleRate();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getSamplesPerFrame(OboeStream stream, int32_t *samplesPerFrame)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(samplesPerFrame);
+ *samplesPerFrame = audioStream->getSamplesPerFrame();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getState(OboeStream stream, oboe_stream_state_t *state)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(state);
+ *state = audioStream->getState();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getFormat(OboeStream stream, oboe_audio_format_t *format)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(format);
+ *format = audioStream->getFormat();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_setBufferSize(OboeStream stream,
+ oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ return audioStream->setBufferSize(requestedFrames, actualFrames);
+}
+
+OBOE_API oboe_result_t OboeStream_getBufferSize(OboeStream stream, oboe_size_frames_t *frames)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(frames);
+ *frames = audioStream->getBufferSize();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getDirection(OboeStream stream, int32_t *direction)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(direction);
+ *direction = audioStream->getDirection();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getFramesPerBurst(OboeStream stream,
+ oboe_size_frames_t *framesPerBurst)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(framesPerBurst);
+ *framesPerBurst = audioStream->getFramesPerBurst();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getBufferCapacity(OboeStream stream,
+ oboe_size_frames_t *capacity)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(capacity);
+ *capacity = audioStream->getBufferCapacity();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getXRunCount(OboeStream stream, int32_t *xRunCount)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(xRunCount);
+ *xRunCount = audioStream->getXRunCount();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getSharingMode(OboeStream stream,
+ oboe_sharing_mode_t *sharingMode)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(sharingMode);
+ *sharingMode = audioStream->getSharingMode();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getFramesWritten(OboeStream stream,
+ oboe_position_frames_t *frames)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(frames);
+ *frames = audioStream->getFramesWritten();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getFramesRead(OboeStream stream, oboe_position_frames_t *frames)
+{
+ AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(frames);
+ *frames = audioStream->getFramesRead();
+ return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getTimestamp(OboeStream stream,
+ oboe_clockid_t clockid,
+ oboe_position_frames_t *framePosition,
+ oboe_nanoseconds_t *timeNanoseconds)
+{
+ AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+ if (framePosition == nullptr) {
+ return OBOE_ERROR_NULL;
+ } else if (timeNanoseconds == nullptr) {
+ return OBOE_ERROR_NULL;
+ } else if (clockid != OBOE_CLOCK_MONOTONIC && clockid != OBOE_CLOCK_BOOTTIME) {
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ }
+
+ clockid_t hostClockId = OboeConvert_fromOboeClockId(clockid);
+ return audioStream->getTimestamp(hostClockId, framePosition, timeNanoseconds);
+}
diff --git a/media/liboboe/src/core/README.md b/media/liboboe/src/core/README.md
new file mode 100644
index 0000000..dd99286
--- /dev/null
+++ b/media/liboboe/src/core/README.md
@@ -0,0 +1,2 @@
+The core folder contains the essential Oboe files common to all implementations.
+The OboeAudio.cpp contains the 'C' API.
diff --git a/media/liboboe/src/core/VersionExperiment.txt b/media/liboboe/src/core/VersionExperiment.txt
new file mode 100644
index 0000000..071239b
--- /dev/null
+++ b/media/liboboe/src/core/VersionExperiment.txt
@@ -0,0 +1,55 @@
+
+// TODO Experiment with versioning. This may be removed or changed dramatically.
+// Please ignore for now. Do not review.
+#define OBOE_VERSION_EXPERIMENT 0
+#if OBOE_VERSION_EXPERIMENT
+
+#define OBOE_EARLIEST_SUPPORTED_VERSION 1
+#define OBOE_CURRENT_VERSION 2
+
+typedef struct OboeInterface_s {
+ int32_t size; // do not use size_t because its size can vary
+ int32_t version;
+ int32_t reserved1;
+ void * reserved2;
+ oboe_result_t (*createStreamBuilder)(OboeStreamBuilder *);
+} OboeInterface_t;
+
+OboeInterface_t s_oboe_template = {
+ .size = sizeof(OboeInterface_t),
+ .version = OBOE_CURRENT_VERSION,
+ .reserved1 = 0,
+ .reserved2 = NULL,
+ .createStreamBuilder = Oboe_createStreamBuilder
+};
+
+oboe_result_t Oboe_Unimplemented(OboeInterface_t *oboe) {
+ (void) oboe;
+ return OBOE_ERROR_UNIMPLEMENTED;
+}
+
+typedef oboe_result_t (*OboeFunction_t)(OboeInterface_t *oboe);
+
+int32_t Oboe_Initialize(OboeInterface_t *oboe, uint32_t flags) {
+ if (oboe->version < OBOE_EARLIEST_SUPPORTED_VERSION) {
+ return OBOE_ERROR_INCOMPATIBLE;
+ }
+ // Fill in callers vector table.
+ uint8_t *start = (uint8_t*)&oboe->reserved1;
+ uint8_t *end;
+ if (oboe->size <= s_oboe_template.size) {
+ end = ((uint8_t *)oboe) + oboe->size;
+ } else {
+ end = ((uint8_t *)oboe) + s_oboe_template.size;
+ // Assume the rest of the structure is vectors.
+ // Point them all to OboeInternal_Unimplemented()
+ // Point to first vector past end of the known structure.
+ OboeFunction_t *next = (OboeFunction_t*)end;
+ while ((((uint8_t *)next) - ((uint8_t *)oboe)) < oboe->size) {
+ *next++ = Oboe_Unimplemented;
+ }
+ }
+ memcpy(&oboe->reserved1, &s_oboe_template.reserved1, end - start);
+ return OBOE_OK;
+}
+#endif /* OBOE_VERSION_EXPERIMENT -------------------------- */
diff --git a/media/liboboe/src/legacy/AudioStreamRecord.cpp b/media/liboboe/src/legacy/AudioStreamRecord.cpp
new file mode 100644
index 0000000..f130cad
--- /dev/null
+++ b/media/liboboe/src/legacy/AudioStreamRecord.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2016 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 "AudioStreamRecord"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <utils/String16.h>
+#include <media/AudioRecord.h>
+#include <oboe/OboeAudio.h>
+
+#include "AudioClock.h"
+#include "AudioStreamRecord.h"
+
+using namespace android;
+using namespace oboe;
+
+AudioStreamRecord::AudioStreamRecord()
+ : AudioStream()
+{
+}
+
+AudioStreamRecord::~AudioStreamRecord()
+{
+ const oboe_stream_state_t state = getState();
+ bool bad = !(state == OBOE_STREAM_STATE_UNINITIALIZED || state == OBOE_STREAM_STATE_CLOSED);
+ ALOGE_IF(bad, "stream not closed, in state %d", state);
+}
+
+oboe_result_t AudioStreamRecord::open(const AudioStreamBuilder& builder)
+{
+ oboe_result_t result = OBOE_OK;
+
+ result = AudioStream::open(builder);
+ if (result != OBOE_OK) {
+ return result;
+ }
+
+ // Try to create an AudioRecord
+
+ // TODO Support UNSPECIFIED in AudioTrack. For now, use stereo if unspecified.
+ int32_t samplesPerFrame = (getSamplesPerFrame() == OBOE_UNSPECIFIED)
+ ? 2 : getSamplesPerFrame();
+ audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(samplesPerFrame);
+
+ AudioRecord::callback_t callback = NULL;
+ audio_input_flags_t flags = (audio_input_flags_t) AUDIO_INPUT_FLAG_NONE;
+
+ // TODO implement an unspecified Android format then use that.
+ audio_format_t format = (getFormat() == OBOE_UNSPECIFIED)
+ ? AUDIO_FORMAT_PCM_FLOAT
+ : OboeConvert_oboeToAndroidDataFormat(getFormat());
+
+ mAudioRecord = new AudioRecord(
+ AUDIO_SOURCE_DEFAULT,
+ getSampleRate(),
+ format,
+ channelMask,
+
+ mOpPackageName, // const String16& opPackageName TODO does not compile
+
+ 0, // size_t frameCount = 0,
+ callback,
+ NULL, // void* user = NULL,
+ 0, // uint32_t notificationFrames = 0,
+ AUDIO_SESSION_ALLOCATE,
+ AudioRecord::TRANSFER_DEFAULT,
+ flags
+ // int uid = -1,
+ // pid_t pid = -1,
+ // const audio_attributes_t* pAttributes = NULL
+ );
+
+ // Did we get a valid track?
+ status_t status = mAudioRecord->initCheck();
+ if (status != OK) {
+ close();
+ ALOGE("AudioStreamRecord::open(), initCheck() returned %d", status);
+ return OboeConvert_androidToOboeError(status);
+ }
+
+ // Get the actual rate.
+ setSampleRate(mAudioRecord->getSampleRate());
+ setSamplesPerFrame(mAudioRecord->channelCount());
+ setFormat(OboeConvert_androidToOboeDataFormat(mAudioRecord->format()));
+
+ setState(OBOE_STREAM_STATE_OPEN);
+
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamRecord::close()
+{
+ // TODO add close() or release() to AudioRecord API then call it from here
+ if (getState() != OBOE_STREAM_STATE_CLOSED) {
+ mAudioRecord.clear();
+ setState(OBOE_STREAM_STATE_CLOSED);
+ }
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamRecord::requestStart()
+{
+ if (mAudioRecord.get() == NULL) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ // Get current position so we can detect when the track is playing.
+ status_t err = mAudioRecord->getPosition(&mPositionWhenStarting);
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ }
+ err = mAudioRecord->start();
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ } else {
+ setState(OBOE_STREAM_STATE_STARTING);
+ }
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamRecord::requestPause()
+{
+ return OBOE_ERROR_UNIMPLEMENTED;
+}
+
+oboe_result_t AudioStreamRecord::requestFlush() {
+ return OBOE_ERROR_UNIMPLEMENTED;
+}
+
+oboe_result_t AudioStreamRecord::requestStop() {
+ if (mAudioRecord.get() == NULL) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ setState(OBOE_STREAM_STATE_STOPPING);
+ mAudioRecord->stop();
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamRecord::updateState()
+{
+ oboe_result_t result = OBOE_OK;
+ oboe_wrapping_frames_t position;
+ status_t err;
+ switch (getState()) {
+ // TODO add better state visibility to AudioRecord
+ case OBOE_STREAM_STATE_STARTING:
+ err = mAudioRecord->getPosition(&position);
+ if (err != OK) {
+ result = OboeConvert_androidToOboeError(err);
+ } else if (position != mPositionWhenStarting) {
+ setState(OBOE_STREAM_STATE_STARTED);
+ }
+ break;
+ case OBOE_STREAM_STATE_STOPPING:
+ if (mAudioRecord->stopped()) {
+ setState(OBOE_STREAM_STATE_STOPPED);
+ }
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+oboe_result_t AudioStreamRecord::read(void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ oboe_size_frames_t bytesPerFrame = getBytesPerFrame();
+ oboe_size_bytes_t numBytes;
+ oboe_result_t result = OboeConvert_framesToBytes(numFrames, bytesPerFrame, &numBytes);
+ if (result != OBOE_OK) {
+ return result;
+ }
+
+ // TODO add timeout to AudioRecord
+ bool blocking = (timeoutNanoseconds > 0);
+ ssize_t bytesRead = mAudioRecord->read(buffer, numBytes, blocking);
+ if (bytesRead == WOULD_BLOCK) {
+ return 0;
+ } else if (bytesRead < 0) {
+ return OboeConvert_androidToOboeError(bytesRead);
+ }
+ oboe_size_frames_t framesRead = (oboe_size_frames_t)(bytesRead / bytesPerFrame);
+ return (oboe_result_t) framesRead;
+}
+
+oboe_result_t AudioStreamRecord::setBufferSize(oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames)
+{
+ *actualFrames = getBufferCapacity();
+ return OBOE_OK;
+}
+
+oboe_size_frames_t AudioStreamRecord::getBufferSize() const
+{
+ return getBufferCapacity(); // TODO implement in AudioRecord?
+}
+
+oboe_size_frames_t AudioStreamRecord::getBufferCapacity() const
+{
+ return static_cast<oboe_size_frames_t>(mAudioRecord->frameCount());
+}
+
+int32_t AudioStreamRecord::getXRunCount() const
+{
+ return OBOE_ERROR_UNIMPLEMENTED; // TODO implement when AudioRecord supports it
+}
+
+oboe_size_frames_t AudioStreamRecord::getFramesPerBurst() const
+{
+ return 192; // TODO add query to AudioRecord.cpp
+}
+
+// TODO implement getTimestamp
+
diff --git a/media/liboboe/src/legacy/AudioStreamRecord.h b/media/liboboe/src/legacy/AudioStreamRecord.h
new file mode 100644
index 0000000..02ff220
--- /dev/null
+++ b/media/liboboe/src/legacy/AudioStreamRecord.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016 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 LEGACY_AUDIOSTREAMRECORD_H
+#define LEGACY_AUDIOSTREAMRECORD_H
+
+#include <media/AudioRecord.h>
+#include <oboe/OboeAudio.h>
+
+#include "AudioStreamBuilder.h"
+#include "AudioStream.h"
+#include "OboeLegacy.h"
+
+namespace oboe {
+
+/**
+ * Internal stream that uses the legacy AudioTrack path.
+ */
+class AudioStreamRecord : public AudioStream {
+public:
+ AudioStreamRecord();
+
+ virtual ~AudioStreamRecord();
+
+ virtual oboe_result_t open(const AudioStreamBuilder & builder) override;
+ virtual oboe_result_t close() override;
+
+ virtual oboe_result_t requestStart() override;
+ virtual oboe_result_t requestPause() override;
+ virtual oboe_result_t requestFlush() override;
+ virtual oboe_result_t requestStop() override;
+
+ virtual oboe_result_t getTimestamp(clockid_t clockId,
+ oboe_position_frames_t *framePosition,
+ oboe_nanoseconds_t *timeNanoseconds) override {
+ return OBOE_ERROR_UNIMPLEMENTED; // TODO
+ }
+
+ virtual oboe_result_t read(void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds) override;
+
+ virtual oboe_result_t setBufferSize(oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames) override;
+
+ virtual oboe_size_frames_t getBufferSize() const override;
+
+ virtual oboe_size_frames_t getBufferCapacity() const override;
+
+ virtual int32_t getXRunCount() const override;
+
+ virtual oboe_size_frames_t getFramesPerBurst() const override;
+
+ virtual oboe_result_t updateState() override;
+
+private:
+ android::sp<android::AudioRecord> mAudioRecord;
+ // TODO add 64-bit position reporting to AudioRecord and use it.
+ oboe_wrapping_frames_t mPositionWhenStarting = 0;
+ android::String16 mOpPackageName;
+};
+
+} /* namespace oboe */
+
+#endif /* LEGACY_AUDIOSTREAMRECORD_H */
diff --git a/media/liboboe/src/legacy/AudioStreamTrack.cpp b/media/liboboe/src/legacy/AudioStreamTrack.cpp
new file mode 100644
index 0000000..5205fc5
--- /dev/null
+++ b/media/liboboe/src/legacy/AudioStreamTrack.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2016 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 "AudioStreamTrack"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <media/AudioTrack.h>
+
+#include <oboe/OboeAudio.h>
+#include "AudioClock.h"
+#include "AudioStreamTrack.h"
+
+
+using namespace android;
+using namespace oboe;
+
+/*
+ * Create a stream that uses the AudioTrack.
+ */
+AudioStreamTrack::AudioStreamTrack()
+ : AudioStream()
+{
+}
+
+AudioStreamTrack::~AudioStreamTrack()
+{
+ const oboe_stream_state_t state = getState();
+ bool bad = !(state == OBOE_STREAM_STATE_UNINITIALIZED || state == OBOE_STREAM_STATE_CLOSED);
+ ALOGE_IF(bad, "stream not closed, in state %d", state);
+}
+
+oboe_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder)
+{
+ oboe_result_t result = OBOE_OK;
+
+ result = AudioStream::open(builder);
+ if (result != OK) {
+ return result;
+ }
+
+ // Try to create an AudioTrack
+ // TODO Support UNSPECIFIED in AudioTrack. For now, use stereo if unspecified.
+ int32_t samplesPerFrame = (getSamplesPerFrame() == OBOE_UNSPECIFIED)
+ ? 2 : getSamplesPerFrame();
+ audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(samplesPerFrame);
+ ALOGE("AudioStreamTrack::open(), samplesPerFrame = %d, channelMask = 0x%08x",
+ samplesPerFrame, channelMask);
+
+ AudioTrack::callback_t callback = NULL;
+ // TODO add more performance options
+ audio_output_flags_t flags = (audio_output_flags_t) AUDIO_OUTPUT_FLAG_FAST;
+ size_t frameCount = 0;
+ // TODO implement an unspecified AudioTrack format then use that.
+ audio_format_t format = (getFormat() == OBOE_UNSPECIFIED)
+ ? AUDIO_FORMAT_PCM_FLOAT
+ : OboeConvert_oboeToAndroidDataFormat(getFormat());
+
+ mAudioTrack = new AudioTrack(
+ (audio_stream_type_t) AUDIO_STREAM_MUSIC,
+ getSampleRate(),
+ format,
+ channelMask,
+ frameCount,
+ flags,
+ callback,
+ NULL, // user callback data
+ 0, // notificationFrames
+ AUDIO_SESSION_ALLOCATE,
+ AudioTrack::transfer_type::TRANSFER_SYNC // TODO - this does not allow FAST
+ );
+
+ // Did we get a valid track?
+ status_t status = mAudioTrack->initCheck();
+ // FIXME - this should work - if (status != NO_ERROR) {
+ // But initCheck() is returning 1 !
+ if (status < 0) {
+ close();
+ ALOGE("AudioStreamTrack::open(), initCheck() returned %d", status);
+ return OboeConvert_androidToOboeError(status);
+ }
+
+ // Get the actual values from the AudioTrack.
+ setSamplesPerFrame(mAudioTrack->channelCount());
+ setSampleRate(mAudioTrack->getSampleRate());
+ setFormat(OboeConvert_androidToOboeDataFormat(mAudioTrack->format()));
+
+ setState(OBOE_STREAM_STATE_OPEN);
+
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::close()
+{
+ // TODO maybe add close() or release() to AudioTrack API then call it from here
+ if (getState() != OBOE_STREAM_STATE_CLOSED) {
+ mAudioTrack.clear(); // TODO is this right?
+ setState(OBOE_STREAM_STATE_CLOSED);
+ }
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::requestStart()
+{
+ if (mAudioTrack.get() == NULL) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ // Get current position so we can detect when the track is playing.
+ status_t err = mAudioTrack->getPosition(&mPositionWhenStarting);
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ }
+ err = mAudioTrack->start();
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ } else {
+ setState(OBOE_STREAM_STATE_STARTING);
+ }
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::requestPause()
+{
+ if (mAudioTrack.get() == NULL) {
+ return OBOE_ERROR_INVALID_STATE;
+ } else if (getState() != OBOE_STREAM_STATE_STARTING
+ && getState() != OBOE_STREAM_STATE_STARTED) {
+ ALOGE("requestPause(), called when state is %s", Oboe_convertStreamStateToText(getState()));
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ setState(OBOE_STREAM_STATE_PAUSING);
+ mAudioTrack->pause();
+ status_t err = mAudioTrack->getPosition(&mPositionWhenPausing);
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ }
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::requestFlush() {
+ if (mAudioTrack.get() == NULL) {
+ return OBOE_ERROR_INVALID_STATE;
+ } else if (getState() != OBOE_STREAM_STATE_PAUSED) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ setState(OBOE_STREAM_STATE_FLUSHING);
+ incrementFramesRead(getFramesWritten() - getFramesRead());
+ mAudioTrack->flush();
+ mFramesWritten.reset32();
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::requestStop() {
+ if (mAudioTrack.get() == NULL) {
+ return OBOE_ERROR_INVALID_STATE;
+ }
+ setState(OBOE_STREAM_STATE_STOPPING);
+ incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
+ mAudioTrack->stop();
+ mFramesWritten.reset32();
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::updateState()
+{
+ status_t err;
+ oboe_wrapping_frames_t position;
+ switch (getState()) {
+ // TODO add better state visibility to AudioTrack
+ case OBOE_STREAM_STATE_STARTING:
+ if (mAudioTrack->hasStarted()) {
+ setState(OBOE_STREAM_STATE_STARTED);
+ }
+ break;
+ case OBOE_STREAM_STATE_PAUSING:
+ if (mAudioTrack->stopped()) {
+ err = mAudioTrack->getPosition(&position);
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ } else if (position == mPositionWhenPausing) {
+ // Has stream really stopped advancing?
+ setState(OBOE_STREAM_STATE_PAUSED);
+ }
+ mPositionWhenPausing = position;
+ }
+ break;
+ case OBOE_STREAM_STATE_FLUSHING:
+ {
+ err = mAudioTrack->getPosition(&position);
+ if (err != OK) {
+ return OboeConvert_androidToOboeError(err);
+ } else if (position == 0) {
+ // Advance frames read to match written.
+ setState(OBOE_STREAM_STATE_FLUSHED);
+ }
+ }
+ break;
+ case OBOE_STREAM_STATE_STOPPING:
+ if (mAudioTrack->stopped()) {
+ setState(OBOE_STREAM_STATE_STOPPED);
+ }
+ break;
+ default:
+ break;
+ }
+ return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::write(const void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds)
+{
+ oboe_size_frames_t bytesPerFrame = getBytesPerFrame();
+ oboe_size_bytes_t numBytes;
+ oboe_result_t result = OboeConvert_framesToBytes(numFrames, bytesPerFrame, &numBytes);
+ if (result != OBOE_OK) {
+ return result;
+ }
+
+ // TODO add timeout to AudioTrack
+ bool blocking = timeoutNanoseconds > 0;
+ ssize_t bytesWritten = mAudioTrack->write(buffer, numBytes, blocking);
+ if (bytesWritten == WOULD_BLOCK) {
+ return 0;
+ } else if (bytesWritten < 0) {
+ ALOGE("invalid write, returned %d", (int)bytesWritten);
+ return OboeConvert_androidToOboeError(bytesWritten);
+ }
+ oboe_size_frames_t framesWritten = (oboe_size_frames_t)(bytesWritten / bytesPerFrame);
+ incrementFramesWritten(framesWritten);
+ return framesWritten;
+}
+
+oboe_result_t AudioStreamTrack::setBufferSize(oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames)
+{
+ ssize_t result = mAudioTrack->setBufferSizeInFrames(requestedFrames);
+ if (result != OK) {
+ return OboeConvert_androidToOboeError(result);
+ } else {
+ *actualFrames = result;
+ return OBOE_OK;
+ }
+}
+
+oboe_size_frames_t AudioStreamTrack::getBufferSize() const
+{
+ return static_cast<oboe_size_frames_t>(mAudioTrack->getBufferSizeInFrames());
+}
+
+oboe_size_frames_t AudioStreamTrack::getBufferCapacity() const
+{
+ return static_cast<oboe_size_frames_t>(mAudioTrack->frameCount());
+}
+
+int32_t AudioStreamTrack::getXRunCount() const
+{
+ return static_cast<int32_t>(mAudioTrack->getUnderrunCount());
+}
+
+int32_t AudioStreamTrack::getFramesPerBurst() const
+{
+ return 192; // TODO add query to AudioTrack.cpp
+}
+
+oboe_position_frames_t AudioStreamTrack::getFramesRead() {
+ oboe_wrapping_frames_t position;
+ status_t result;
+ switch (getState()) {
+ case OBOE_STREAM_STATE_STARTING:
+ case OBOE_STREAM_STATE_STARTED:
+ case OBOE_STREAM_STATE_STOPPING:
+ result = mAudioTrack->getPosition(&position);
+ if (result == OK) {
+ mFramesRead.update32(position);
+ }
+ break;
+ default:
+ break;
+ }
+ return AudioStream::getFramesRead();
+}
diff --git a/media/liboboe/src/legacy/AudioStreamTrack.h b/media/liboboe/src/legacy/AudioStreamTrack.h
new file mode 100644
index 0000000..8c40884
--- /dev/null
+++ b/media/liboboe/src/legacy/AudioStreamTrack.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016 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 LEGACY_AUDIOSTREAMTRACK_H
+#define LEGACY_AUDIOSTREAMTRACK_H
+
+#include <media/AudioTrack.h>
+#include <oboe/OboeAudio.h>
+
+#include "AudioStreamBuilder.h"
+#include "AudioStream.h"
+#include "OboeLegacy.h"
+
+namespace oboe {
+
+
+/**
+ * Internal stream that uses the legacy AudioTrack path.
+ */
+class AudioStreamTrack : public AudioStream {
+public:
+ AudioStreamTrack();
+
+ virtual ~AudioStreamTrack();
+
+
+ virtual oboe_result_t open(const AudioStreamBuilder & builder) override;
+ virtual oboe_result_t close() override;
+
+ virtual oboe_result_t requestStart() override;
+ virtual oboe_result_t requestPause() override;
+ virtual oboe_result_t requestFlush() override;
+ virtual oboe_result_t requestStop() override;
+
+ virtual oboe_result_t getTimestamp(clockid_t clockId,
+ oboe_position_frames_t *framePosition,
+ oboe_nanoseconds_t *timeNanoseconds) override {
+ return OBOE_ERROR_UNIMPLEMENTED; // TODO call getTimestamp(ExtendedTimestamp *timestamp);
+ }
+
+ virtual oboe_result_t write(const void *buffer,
+ oboe_size_frames_t numFrames,
+ oboe_nanoseconds_t timeoutNanoseconds) override;
+
+ virtual oboe_result_t setBufferSize(oboe_size_frames_t requestedFrames,
+ oboe_size_frames_t *actualFrames) override;
+ virtual oboe_size_frames_t getBufferSize() const override;
+ virtual oboe_size_frames_t getBufferCapacity() const override;
+ virtual oboe_size_frames_t getFramesPerBurst()const override;
+ virtual int32_t getXRunCount() const override;
+
+ virtual oboe_position_frames_t getFramesRead() override;
+
+ virtual oboe_result_t updateState() override;
+
+private:
+ android::sp<android::AudioTrack> mAudioTrack;
+ // TODO add 64-bit position reporting to AudioRecord and use it.
+ oboe_wrapping_frames_t mPositionWhenStarting = 0;
+ oboe_wrapping_frames_t mPositionWhenPausing = 0;
+};
+
+} /* namespace oboe */
+
+#endif /* LEGACY_AUDIOSTREAMTRACK_H */
diff --git a/media/liboboe/src/legacy/OboeLegacy.h b/media/liboboe/src/legacy/OboeLegacy.h
new file mode 100644
index 0000000..6803837
--- /dev/null
+++ b/media/liboboe/src/legacy/OboeLegacy.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 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 OBOE_LEGACY_H
+#define OBOE_LEGACY_H
+
+#include <stdint.h>
+#include <oboe/OboeAudio.h>
+
+/**
+ * Common code for legacy classes.
+ */
+
+/* AudioTrack uses a 32-bit frame counter that can wrap around in about a day. */
+typedef uint32_t oboe_wrapping_frames_t;
+
+#endif /* OBOE_LEGACY_H */
diff --git a/media/liboboe/src/legacy/README.md b/media/liboboe/src/legacy/README.md
new file mode 100644
index 0000000..b51c44b
--- /dev/null
+++ b/media/liboboe/src/legacy/README.md
@@ -0,0 +1,2 @@
+The legacy folder contains the classes that implement Oboe AudioStream on top of
+Android AudioTrack and AudioRecord.
diff --git a/media/liboboe/src/utility/AudioClock.h b/media/liboboe/src/utility/AudioClock.h
new file mode 100644
index 0000000..da2f74a
--- /dev/null
+++ b/media/liboboe/src/utility/AudioClock.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 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 UTILITY_AUDIOCLOCK_H
+#define UTILITY_AUDIOCLOCK_H
+
+#include <sys/types.h>
+#include <time.h>
+#include "oboe/OboeDefinitions.h"
+#include "oboe/OboeAudio.h"
+
+class AudioClock {
+public:
+ static oboe_nanoseconds_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
+ struct timespec time;
+ int result = clock_gettime(clockId, &time);
+ if (result < 0) {
+ return -errno;
+ }
+ return (time.tv_sec * OBOE_NANOS_PER_SECOND) + time.tv_nsec;
+ }
+
+ /**
+ * Sleep until the specified absolute time.
+ * Return immediately with OBOE_ERROR_ILLEGAL_ARGUMENT if a negative
+ * nanoTime is specified.
+ *
+ * @param nanoTime time to wake up
+ * @param clockId CLOCK_MONOTONIC is default
+ * @return 0, a negative error, or 1 if the call is interrupted by a signal handler (EINTR)
+ */
+ static int sleepUntilNanoTime(oboe_nanoseconds_t nanoTime,
+ clockid_t clockId = CLOCK_MONOTONIC) {
+ if (nanoTime > 0) {
+ struct timespec time;
+ time.tv_sec = nanoTime / OBOE_NANOS_PER_SECOND;
+ // Calculate the fractional nanoseconds. Avoids expensive % operation.
+ time.tv_nsec = nanoTime - (time.tv_sec * OBOE_NANOS_PER_SECOND);
+ int err = clock_nanosleep(clockId, TIMER_ABSTIME, &time, nullptr);
+ switch (err) {
+ case EINTR:
+ return 1;
+ case 0:
+ return 0;
+ default:
+ // Subtract because clock_nanosleep() returns a positive error number!
+ return 0 - err;
+ }
+ } else {
+ return OBOE_ERROR_ILLEGAL_ARGUMENT;
+ }
+ }
+
+ /**
+ * Sleep for the specified number of relative nanoseconds in real-time.
+ * Return immediately with 0 if a negative nanoseconds is specified.
+ *
+ * @param nanoseconds time to sleep
+ * @param clockId CLOCK_MONOTONIC is default
+ * @return 0, a negative error, or 1 if the call is interrupted by a signal handler (EINTR)
+ */
+ static int sleepForNanos(oboe_nanoseconds_t nanoseconds, clockid_t clockId = CLOCK_MONOTONIC) {
+ if (nanoseconds > 0) {
+ struct timespec time;
+ time.tv_sec = nanoseconds / OBOE_NANOS_PER_SECOND;
+ // Calculate the fractional nanoseconds. Avoids expensive % operation.
+ time.tv_nsec = nanoseconds - (time.tv_sec * OBOE_NANOS_PER_SECOND);
+ const int flags = 0; // documented as relative sleep
+ int err = clock_nanosleep(clockId, flags, &time, nullptr);
+ switch (err) {
+ case EINTR:
+ return 1;
+ case 0:
+ return 0;
+ default:
+ // Subtract because clock_nanosleep() returns a positive error number!
+ return 0 - err;
+ }
+ }
+ return 0;
+ }
+};
+
+
+#endif // UTILITY_AUDIOCLOCK_H
diff --git a/media/liboboe/src/utility/HandleTracker.cpp b/media/liboboe/src/utility/HandleTracker.cpp
new file mode 100644
index 0000000..be2a64c
--- /dev/null
+++ b/media/liboboe/src/utility/HandleTracker.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2016 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 "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <assert.h>
+
+#include <oboe/OboeDefinitions.h>
+#include "HandleTracker.h"
+
+// Handle format is: tgggiiii
+// where each letter is 4 bits, t=type, g=generation, i=index
+
+#define TYPE_SIZE 4
+#define GENERATION_SIZE 12
+#define INDEX_SIZE 16
+
+#define GENERATION_INVALID 0
+#define GENERATION_SHIFT INDEX_SIZE
+
+#define TYPE_MASK ((1 << TYPE_SIZE) - 1)
+#define GENERATION_MASK ((1 << GENERATION_SIZE) - 1)
+#define INDEX_MASK ((1 << INDEX_SIZE) - 1)
+
+#define SLOT_UNAVAILABLE (-1)
+
+// Error if handle is negative so type is limited to bottom half.
+#define HANDLE_INVALID_TYPE TYPE_MASK
+
+static_assert(HANDLE_TRACKER_MAX_TYPES == (1 << (TYPE_SIZE - 1)),
+ "Mismatch between header and cpp.");
+static_assert(HANDLE_TRACKER_MAX_HANDLES == (1 << (INDEX_SIZE)),
+ "Mismatch between header and cpp.");
+
+HandleTracker::HandleTracker(uint32_t maxHandles)
+ : mMaxHandleCount(maxHandles)
+ , mHandleAddresses(nullptr)
+ , mHandleHeaders(nullptr)
+{
+ assert(maxHandles <= HANDLE_TRACKER_MAX_HANDLES);
+ // Allocate arrays to hold addresses and validation info.
+ mHandleAddresses = (handle_tracker_address_t *) new handle_tracker_address_t[maxHandles];
+ if (mHandleAddresses != nullptr) {
+ mHandleHeaders = new handle_tracker_header_t[maxHandles];
+ if (mHandleHeaders != nullptr) {
+ // Initialize linked list of free nodes. NULL terminated.
+ for (uint32_t i = 0; i < (maxHandles - 1); i++) {
+ mHandleAddresses[i] = &mHandleAddresses[i + 1]; // point to next node
+ mHandleHeaders[i] = 0;
+ }
+ mNextFreeAddress = &mHandleAddresses[0];
+ mHandleAddresses[maxHandles - 1] = nullptr;
+ mHandleHeaders[maxHandles - 1] = 0;
+ } else {
+ delete[] mHandleAddresses; // so the class appears uninitialized
+ }
+ }
+}
+
+HandleTracker::~HandleTracker()
+{
+ delete[] mHandleAddresses;
+ delete[] mHandleHeaders;
+}
+
+bool HandleTracker::isInitialized() const {
+ return mHandleAddresses != nullptr;
+}
+
+handle_tracker_slot_t HandleTracker::allocateSlot() {
+ void **allocated = mNextFreeAddress;
+ if (allocated == nullptr) {
+ return SLOT_UNAVAILABLE;
+ }
+ // Remove this slot from the head of the linked list.
+ mNextFreeAddress = (void **) *allocated;
+ return (allocated - mHandleAddresses);
+}
+
+handle_tracker_generation_t HandleTracker::nextGeneration(handle_tracker_slot_t index) {
+ handle_tracker_generation_t generation = (mHandleHeaders[index] + 1) & GENERATION_MASK;
+ // Avoid generation zero so that 0x0 is not a valid handle.
+ if (generation == GENERATION_INVALID) {
+ generation++;
+ }
+ return generation;
+}
+
+oboe_handle_t HandleTracker::put(handle_tracker_type_t type, void *address)
+{
+ if (type < 0 || type >= HANDLE_TRACKER_MAX_TYPES) {
+ return static_cast<oboe_handle_t>(OBOE_ERROR_OUT_OF_RANGE);
+ }
+ if (!isInitialized()) {
+ return static_cast<oboe_handle_t>(OBOE_ERROR_NO_MEMORY);
+ }
+
+ // Find an empty slot.
+ handle_tracker_slot_t index = allocateSlot();
+ if (index == SLOT_UNAVAILABLE) {
+ ALOGE("HandleTracker::put() no room for more handles");
+ return static_cast<oboe_handle_t>(OBOE_ERROR_NO_FREE_HANDLES);
+ }
+
+ // Cycle the generation counter so stale handles can be detected.
+ handle_tracker_generation_t generation = nextGeneration(index); // reads header table
+ handle_tracker_header_t inputHeader = buildHeader(type, generation);
+
+ // These two writes may need to be observed by other threads or cores during get().
+ mHandleHeaders[index] = inputHeader;
+ mHandleAddresses[index] = address;
+ // TODO use store release to enforce memory order with get()
+
+ // Generate a handle.
+ oboe_handle_t handle = buildHandle(inputHeader, index);
+
+ //ALOGD("HandleTracker::put(%p) returns 0x%08x", address, handle);
+ return handle;
+}
+
+handle_tracker_slot_t HandleTracker::handleToIndex(handle_tracker_type_t type,
+ oboe_handle_t handle) const
+{
+ // Validate the handle.
+ handle_tracker_slot_t index = extractIndex(handle);
+ if (index >= mMaxHandleCount) {
+ ALOGE("HandleTracker::handleToIndex() invalid handle = 0x%08X", handle);
+ return static_cast<oboe_handle_t>(OBOE_ERROR_INVALID_HANDLE);
+ }
+ handle_tracker_generation_t handleGeneration = extractGeneration(handle);
+ handle_tracker_header_t inputHeader = buildHeader(type, handleGeneration);
+ if (inputHeader != mHandleHeaders[index]) {
+ ALOGE("HandleTracker::handleToIndex() inputHeader = 0x%08x != mHandleHeaders[%d] = 0x%08x",
+ inputHeader, index, mHandleHeaders[index]);
+ return static_cast<oboe_handle_t>(OBOE_ERROR_INVALID_HANDLE);
+ }
+ return index;
+}
+
+handle_tracker_address_t HandleTracker::get(handle_tracker_type_t type, oboe_handle_t handle) const
+{
+ if (!isInitialized()) {
+ return nullptr;
+ }
+ handle_tracker_slot_t index = handleToIndex(type, handle);
+ if (index >= 0) {
+ return mHandleAddresses[index];
+ } else {
+ return nullptr;
+ }
+}
+
+handle_tracker_address_t HandleTracker::remove(handle_tracker_type_t type, oboe_handle_t handle) {
+ if (!isInitialized()) {
+ return nullptr;
+ }
+ handle_tracker_slot_t index = handleToIndex(type,handle);
+ if (index >= 0) {
+ handle_tracker_address_t address = mHandleAddresses[index];
+
+ // Invalidate the header type but preserve the generation count.
+ handle_tracker_generation_t generation = mHandleHeaders[index] & GENERATION_MASK;
+ handle_tracker_header_t inputHeader = buildHeader(
+ (handle_tracker_type_t) HANDLE_INVALID_TYPE, generation);
+ mHandleHeaders[index] = inputHeader;
+
+ // Add this slot to the head of the linked list.
+ mHandleAddresses[index] = mNextFreeAddress;
+ mNextFreeAddress = (handle_tracker_address_t *) &mHandleAddresses[index];
+ return address;
+ } else {
+ return nullptr;
+ }
+}
+
+oboe_handle_t HandleTracker::buildHandle(handle_tracker_header_t typeGeneration,
+ handle_tracker_slot_t index) {
+ return (oboe_handle_t)((typeGeneration << GENERATION_SHIFT) | (index & INDEX_MASK));
+}
+
+handle_tracker_header_t HandleTracker::buildHeader(handle_tracker_type_t type,
+ handle_tracker_generation_t generation)
+{
+ return (handle_tracker_header_t) (((type & TYPE_MASK) << GENERATION_SIZE)
+ | (generation & GENERATION_MASK));
+}
+
+handle_tracker_slot_t HandleTracker::extractIndex(oboe_handle_t handle)
+{
+ return handle & INDEX_MASK;
+}
+
+handle_tracker_generation_t HandleTracker::extractGeneration(oboe_handle_t handle)
+{
+ return (handle >> GENERATION_SHIFT) & GENERATION_MASK;
+}
diff --git a/media/liboboe/src/utility/HandleTracker.h b/media/liboboe/src/utility/HandleTracker.h
new file mode 100644
index 0000000..da5b654
--- /dev/null
+++ b/media/liboboe/src/utility/HandleTracker.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2016 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 UTILITY_HANDLETRACKER_H
+#define UTILITY_HANDLETRACKER_H
+
+#include <stdint.h>
+
+typedef int32_t handle_tracker_type_t; // what kind of handle
+typedef int32_t handle_tracker_slot_t; // index in allocation table
+typedef int32_t handle_tracker_generation_t; // incremented when slot used
+typedef uint16_t handle_tracker_header_t; // combines type and generation
+typedef void *handle_tracker_address_t; // address of something that is stored here
+
+#define HANDLE_TRACKER_MAX_TYPES (1 << 3)
+#define HANDLE_TRACKER_MAX_HANDLES (1 << 16)
+
+/**
+ * Represent Objects using an integer handle that can be used with Java.
+ * This also makes the 'C' ABI more robust.
+ *
+ * Note that this should only be called from a single thread.
+ * If you call it from more than one thread then you need to use your own mutex.
+ */
+class HandleTracker {
+
+public:
+ /**
+ * @param maxHandles cannot exceed HANDLE_TRACKER_MAX_HANDLES
+ */
+ HandleTracker(uint32_t maxHandles);
+ virtual ~HandleTracker();
+
+ /**
+ * Don't use if this returns false;
+ * @return true if the internal allocation succeeded
+ */
+ bool isInitialized() const;
+
+ /**
+ * Store a pointer and return a handle that can be used to retrieve the pointer.
+ *
+ * @param type the type of the object to be tracked
+ * @param address pointer to be converted to a handle
+ * @return a valid handle or a negative error
+ */
+ oboe_handle_t put(handle_tracker_type_t expectedType, handle_tracker_address_t address);
+
+ /**
+ * Get the original pointer associated with the handle.
+ * The handle will be validated to prevent stale handles from being reused.
+ * Note that the validation is designed to prevent common coding errors and not
+ * to prevent deliberate hacking.
+ *
+ * @param expectedType shouldmatch the type we passed to put()
+ * @param handle to be converted to a pointer
+ * @return address associated with handle or nullptr
+ */
+ handle_tracker_address_t get(handle_tracker_type_t expectedType, oboe_handle_t handle) const;
+
+ /**
+ * Free up the storage associated with the handle.
+ * Subsequent attempts to use the handle will fail.
+ *
+ * @param expectedType shouldmatch the type we passed to put()
+ * @param handle to be removed from tracking
+ * @return address associated with handle or nullptr if not found
+ */
+ handle_tracker_address_t remove(handle_tracker_type_t expectedType, oboe_handle_t handle);
+
+private:
+ const int32_t mMaxHandleCount; // size of array
+ // This is const after initialization.
+ handle_tracker_address_t * mHandleAddresses; // address of objects or a free linked list node
+ // This is const after initialization.
+ handle_tracker_header_t * mHandleHeaders; // combination of type and generation
+ handle_tracker_address_t * mNextFreeAddress; // head of the linked list of free nodes in mHandleAddresses
+
+ /**
+ * Pull slot off of a list of empty slots.
+ * @return index or a negative error
+ */
+ handle_tracker_slot_t allocateSlot();
+
+ /**
+ * Validate the handle and return the corresponding index.
+ * @return slot index or a negative error
+ */
+ handle_tracker_slot_t handleToIndex(oboe_handle_t handle, handle_tracker_type_t type) const;
+
+ /**
+ * Construct a handle from a header and an index.
+ * @param header combination of a type and a generation
+ * @param index slot index returned from allocateSlot
+ * @return handle or a negative error
+ */
+ oboe_handle_t buildHandle(handle_tracker_header_t header, handle_tracker_slot_t index);
+
+ /**
+ * Combine a type and a generation field into a header.
+ */
+ static handle_tracker_header_t buildHeader(handle_tracker_type_t type,
+ handle_tracker_generation_t generation);
+
+ /**
+ * Extract the index from a handle.
+ * Does not validate the handle.
+ * @return index associated with a handle
+ */
+ static handle_tracker_slot_t extractIndex(oboe_handle_t handle);
+
+ /**
+ * Extract the generation from a handle.
+ * Does not validate the handle.
+ * @return generation associated with a handle
+ */
+ static handle_tracker_generation_t extractGeneration(oboe_handle_t handle);
+
+ /**
+ * Increment the generation for the slot, avoiding zero.
+ */
+ handle_tracker_generation_t nextGeneration(handle_tracker_slot_t index);
+
+};
+
+#endif //UTILITY_HANDLETRACKER_H
diff --git a/media/liboboe/src/utility/MonotonicCounter.h b/media/liboboe/src/utility/MonotonicCounter.h
new file mode 100644
index 0000000..befad21
--- /dev/null
+++ b/media/liboboe/src/utility/MonotonicCounter.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016 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 UTILITY_MONOTONICCOUNTER_H
+#define UTILITY_MONOTONICCOUNTER_H
+
+#include <stdint.h>
+
+/**
+ * Maintain a 64-bit monotonic counter.
+ * Can be used to track a 32-bit counter that wraps or gets reset.
+ *
+ * Note that this is not atomic and has no interior locks.
+ * A caller will need to provide their own exterior locking
+ * if they need to use it from multiple threads.
+ */
+class MonotonicCounter {
+
+public:
+ MonotonicCounter() {};
+ virtual ~MonotonicCounter() {};
+
+ /**
+ * @return current value of the counter
+ */
+ int64_t get() const {
+ return mCounter64;
+ }
+
+ /**
+ * Advance the counter if delta is positive.
+ * @return current value of the counter
+ */
+ int64_t increment(int64_t delta) {
+ if (delta > 0) {
+ mCounter64 += delta;
+ }
+ return mCounter64;
+ }
+
+ /**
+ * Advance the 64-bit counter if (current32 - previousCurrent32) > 0.
+ * This can be used to convert a 32-bit counter that may be wrapping into
+ * a monotonic 64-bit counter.
+ *
+ * This counter32 should NOT be allowed to advance by more than 0x7FFFFFFF between calls.
+ * Think of the wrapping counter like a sine wave. If the frequency of the signal
+ * is more than half the sampling rate (Nyquist rate) then you cannot measure it properly.
+ * If the counter wraps around every 24 hours then we should measure it with a period
+ * of less than 12 hours.
+ *
+ * @return current value of the 64-bit counter
+ */
+ int64_t update32(int32_t counter32) {
+ int32_t delta = counter32 - mCounter32;
+ // protect against the mCounter64 going backwards
+ if (delta > 0) {
+ mCounter64 += delta;
+ mCounter32 = counter32;
+ }
+ return mCounter64;
+ }
+
+ /**
+ * Reset the stored value of the 32-bit counter.
+ * This is used if your counter32 has been reset to zero.
+ */
+ void reset32() {
+ mCounter32 = 0;
+ }
+
+private:
+ int64_t mCounter64 = 0;
+ int32_t mCounter32 = 0;
+};
+
+
+#endif //UTILITY_MONOTONICCOUNTER_H
diff --git a/media/liboboe/src/utility/OboeUtilities.cpp b/media/liboboe/src/utility/OboeUtilities.cpp
new file mode 100644
index 0000000..b28f7c7
--- /dev/null
+++ b/media/liboboe/src/utility/OboeUtilities.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2016 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 "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+
+#include "oboe/OboeDefinitions.h"
+#include "OboeUtilities.h"
+
+using namespace android;
+
+oboe_size_bytes_t OboeConvert_formatToSizeInBytes(oboe_audio_format_t format) {
+ oboe_datatype_t dataType = OBOE_AUDIO_FORMAT_DATA_TYPE(format);
+ oboe_size_bytes_t size;
+ switch (dataType) {
+ case OBOE_AUDIO_DATATYPE_UINT8:
+ size = sizeof(uint8_t);
+ break;
+ case OBOE_AUDIO_DATATYPE_INT16:
+ size = sizeof(int16_t);
+ break;
+ case OBOE_AUDIO_DATATYPE_INT32:
+ case OBOE_AUDIO_DATATYPE_INT824:
+ size = sizeof(int32_t);
+ break;
+ case OBOE_AUDIO_DATATYPE_FLOAT32:
+ size = sizeof(float);
+ break;
+ default:
+ size = OBOE_ERROR_ILLEGAL_ARGUMENT;
+ break;
+ }
+ return size;
+}
+
+// TODO This similar to a function in audio_utils. Consider using that instead.
+void OboeConvert_floatToPcm16(const float *source, int32_t numSamples, int16_t *destination) {
+ for (int i = 0; i < numSamples; i++) {
+ float fval = source[i];
+ fval += 1.0; // to avoid discontinuity at 0.0 caused by truncation
+ fval *= 32768.0f;
+ int32_t sample = (int32_t) fval;
+ // clip to 16-bit range
+ if (sample < 0) sample = 0;
+ else if (sample > 0x0FFFF) sample = 0x0FFFF;
+ sample -= 32768; // center at zero
+ destination[i] = (int16_t) sample;
+ }
+}
+
+void OboeConvert_pcm16ToFloat(const float *source, int32_t numSamples, int16_t *destination) {
+ for (int i = 0; i < numSamples; i++) {
+ destination[i] = source[i] * (1.0f / 32768.0f);
+ }
+}
+
+oboe_result_t OboeConvert_androidToOboeError(status_t error) {
+ if (error >= 0) {
+ return error;
+ }
+ oboe_result_t result;
+ switch (error) {
+ case OK:
+ result = OBOE_OK;
+ break;
+ case INVALID_OPERATION:
+ result = OBOE_ERROR_INVALID_STATE;
+ break;
+ case BAD_VALUE:
+ result = OBOE_ERROR_UNEXPECTED_VALUE;
+ break;
+ case WOULD_BLOCK:
+ result = OBOE_ERROR_WOULD_BLOCK;
+ break;
+ // TODO add more error codes
+ default:
+ result = OBOE_ERROR_INTERNAL;
+ break;
+ }
+ return result;
+}
+
+audio_format_t OboeConvert_oboeToAndroidDataFormat(oboe_audio_format_t oboeFormat) {
+ audio_format_t androidFormat;
+ switch (oboeFormat) {
+ case OBOE_AUDIO_FORMAT_PCM16:
+ androidFormat = AUDIO_FORMAT_PCM_16_BIT;
+ break;
+ case OBOE_AUDIO_FORMAT_PCM_FLOAT:
+ androidFormat = AUDIO_FORMAT_PCM_FLOAT;
+ break;
+ case OBOE_AUDIO_FORMAT_PCM824:
+ androidFormat = AUDIO_FORMAT_PCM_8_24_BIT;
+ break;
+ case OBOE_AUDIO_FORMAT_PCM32:
+ androidFormat = AUDIO_FORMAT_PCM_32_BIT;
+ break;
+ default:
+ androidFormat = AUDIO_FORMAT_DEFAULT;
+ ALOGE("OboeConvert_oboeToAndroidDataFormat 0x%08X unrecognized", oboeFormat);
+ break;
+ }
+ return androidFormat;
+}
+
+oboe_audio_format_t OboeConvert_androidToOboeDataFormat(audio_format_t androidFormat) {
+ oboe_audio_format_t oboeFormat = OBOE_AUDIO_FORMAT_INVALID;
+ switch (androidFormat) {
+ case AUDIO_FORMAT_PCM_16_BIT:
+ oboeFormat = OBOE_AUDIO_FORMAT_PCM16;
+ break;
+ case AUDIO_FORMAT_PCM_FLOAT:
+ oboeFormat = OBOE_AUDIO_FORMAT_PCM_FLOAT;
+ break;
+ case AUDIO_FORMAT_PCM_32_BIT:
+ oboeFormat = OBOE_AUDIO_FORMAT_PCM32;
+ break;
+ case AUDIO_FORMAT_PCM_8_24_BIT:
+ oboeFormat = OBOE_AUDIO_FORMAT_PCM824;
+ break;
+ default:
+ oboeFormat = OBOE_AUDIO_FORMAT_INVALID;
+ ALOGE("OboeConvert_androidToOboeDataFormat 0x%08X unrecognized", androidFormat);
+ break;
+ }
+ return oboeFormat;
+}
+
+oboe_size_bytes_t OboeConvert_framesToBytes(oboe_size_frames_t numFrames,
+ oboe_size_bytes_t bytesPerFrame,
+ oboe_size_bytes_t *sizeInBytes) {
+ // TODO implement more elegantly
+ const int32_t maxChannels = 256; // ridiculously large
+ const oboe_size_frames_t maxBytesPerFrame = maxChannels * sizeof(float);
+ // Prevent overflow by limiting multiplicands.
+ if (bytesPerFrame > maxBytesPerFrame || numFrames > (0x3FFFFFFF / maxBytesPerFrame)) {
+ ALOGE("size overflow, numFrames = %d, frameSize = %zd", numFrames, bytesPerFrame);
+ return OBOE_ERROR_OUT_OF_RANGE;
+ }
+ *sizeInBytes = numFrames * bytesPerFrame;
+ return OBOE_OK;
+}
diff --git a/media/liboboe/src/utility/OboeUtilities.h b/media/liboboe/src/utility/OboeUtilities.h
new file mode 100644
index 0000000..974ccf6
--- /dev/null
+++ b/media/liboboe/src/utility/OboeUtilities.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 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 UTILITY_OBOEUTILITIES_H
+#define UTILITY_OBOEUTILITIES_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <hardware/audio.h>
+
+#include "oboe/OboeDefinitions.h"
+
+oboe_result_t OboeConvert_androidToOboeError(android::status_t error);
+
+void OboeConvert_floatToPcm16(const float *source, int32_t numSamples, int16_t *destination);
+
+void OboeConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples, float *destination);
+
+/**
+ * Calculate the number of bytes and prevent numeric overflow.
+ * @param numFrames frame count
+ * @param bytesPerFrame size of a frame in bytes
+ * @param sizeInBytes total size in bytes
+ * @return OBOE_OK or negative error, eg. OBOE_ERROR_OUT_OF_RANGE
+ */
+oboe_size_bytes_t OboeConvert_framesToBytes(oboe_size_frames_t numFrames,
+ oboe_size_bytes_t bytesPerFrame,
+ oboe_size_bytes_t *sizeInBytes);
+
+audio_format_t OboeConvert_oboeToAndroidDataFormat(oboe_audio_format_t oboe_format);
+
+oboe_audio_format_t OboeConvert_androidToOboeDataFormat(audio_format_t format);
+
+/**
+ * @return the size of a sample of the given format in bytes or OBOE_ERROR_ILLEGAL_ARGUMENT
+ */
+oboe_size_bytes_t OboeConvert_formatToSizeInBytes(oboe_audio_format_t format);
+
+#endif //UTILITY_OBOEUTILITIES_H
diff --git a/media/liboboe/src/utility/README.md b/media/liboboe/src/utility/README.md
new file mode 100644
index 0000000..9db926a
--- /dev/null
+++ b/media/liboboe/src/utility/README.md
@@ -0,0 +1,3 @@
+The utility folder contains things that may be shared between the Oboe client and server.
+They might also be handy outside Oboe.
+They generally do not depend on Oboe functionality.
diff --git a/media/liboboe/tests/Android.mk b/media/liboboe/tests/Android.mk
new file mode 100644
index 0000000..f2c65d9
--- /dev/null
+++ b/media/liboboe/tests/Android.mk
@@ -0,0 +1,27 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/av/media/liboboe/include \
+ frameworks/av/media/liboboe/src/core \
+ frameworks/av/media/liboboe/src/utility
+LOCAL_SRC_FILES:= test_oboe_api.cpp
+LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
+ libbinder libcutils libutils \
+ libaudioclient liblog
+LOCAL_STATIC_LIBRARIES := liboboe
+LOCAL_MODULE := test_oboe_api
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/av/media/liboboe/include \
+ frameworks/av/media/liboboe/src/core \
+ frameworks/av/media/liboboe/src/utility
+LOCAL_SRC_FILES:= test_handle_tracker.cpp
+LOCAL_SHARED_LIBRARIES := libbinder libcutils libutils liblog
+LOCAL_STATIC_LIBRARIES := liboboe
+LOCAL_MODULE := test_handle_tracker
+include $(BUILD_NATIVE_TEST)
diff --git a/media/liboboe/tests/test_handle_tracker.cpp b/media/liboboe/tests/test_handle_tracker.cpp
new file mode 100644
index 0000000..ae7384e
--- /dev/null
+++ b/media/liboboe/tests/test_handle_tracker.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// Unit tests for Oboe Handle Tracker
+
+#include <stdlib.h>
+#include <math.h>
+
+#include <gtest/gtest.h>
+
+#include <oboe/OboeDefinitions.h>
+#include "HandleTracker.h"
+
+// Test adding one address.
+TEST(test_handle_tracker, oboe_handle_tracker) {
+ const int MAX_HANDLES = 4;
+ HandleTracker tracker(MAX_HANDLES);
+ handle_tracker_type_t type = 3; // arbitrary generic type
+ int data; // something that has an address we can use
+ handle_tracker_address_t found;
+
+ // repeat the test several times to see if it breaks
+ const int SEVERAL = 5; // arbitrary
+ for (int i = 0; i < SEVERAL; i++) {
+ // should fail to find a bogus handle
+ found = tracker.get(type, 0); // bad handle
+ EXPECT_EQ(nullptr, found);
+
+ // create a valid handle and use it to lookup the object again
+ oboe_handle_t dataHandle = tracker.put(type, &data);
+ ASSERT_TRUE(dataHandle > 0);
+ found = tracker.get(type, dataHandle);
+ EXPECT_EQ(&data, found);
+ found = tracker.get(type, 0); // bad handle
+ EXPECT_EQ(nullptr, found);
+
+ // wrong type
+ found = tracker.get(type+1, dataHandle);
+ EXPECT_EQ(nullptr, found);
+
+ // remove from storage
+ found = tracker.remove(type, dataHandle);
+ EXPECT_EQ(&data, found);
+ // should fail the second time
+ found = tracker.remove(type, dataHandle);
+ EXPECT_EQ(NULL, found);
+ }
+}
+
+// Test filling the tracker.
+TEST(test_handle_tracker, oboe_full_up) {
+ const int MAX_HANDLES = 5;
+ HandleTracker tracker(MAX_HANDLES);
+ handle_tracker_type_t type = 4; // arbitrary generic type
+ int data[MAX_HANDLES];
+ oboe_handle_t handles[MAX_HANDLES];
+ handle_tracker_address_t found;
+
+ // repeat the test several times to see if it breaks
+ const int SEVERAL = 5; // arbitrary
+ for (int i = 0; i < SEVERAL; i++) {
+ for (int i = 0; i < MAX_HANDLES; i++) {
+ // add a handle
+ handles[i] = tracker.put(type, &data[i]);
+ ASSERT_TRUE(handles[i] > 0);
+ found = tracker.get(type, handles[i]);
+ EXPECT_EQ(&data[i], found);
+ }
+
+ // Now that it is full, try to add one more.
+ oboe_handle_t handle = tracker.put(type, &data[0]);
+ EXPECT_TRUE(handle < 0);
+
+ for (int i = 0; i < MAX_HANDLES; i++) {
+ // look up each handle
+ found = tracker.get(type, handles[i]);
+ EXPECT_EQ(&data[i], found);
+ }
+
+ // remove one from storage
+ found = tracker.remove(type, handles[2]);
+ EXPECT_EQ(&data[2], found);
+ // now try to look up the same handle and fail
+ found = tracker.get(type, handles[2]);
+ EXPECT_EQ(nullptr, found);
+
+ // add that same one back
+ handle = tracker.put(type, &data[2]);
+ ASSERT_TRUE(handle > 0);
+ found = tracker.get(type, handle);
+ EXPECT_EQ(&data[2], found);
+ // now use a stale handle again with a valid index and fail
+ found = tracker.get(type, handles[2]);
+ EXPECT_EQ(nullptr, found);
+
+ // remove them all
+ handles[2] = handle;
+ for (int i = 0; i < MAX_HANDLES; i++) {
+ // look up each handle
+ found = tracker.remove(type, handles[i]);
+ EXPECT_EQ(&data[i], found);
+ }
+ }
+}
diff --git a/media/liboboe/tests/test_oboe_api.cpp b/media/liboboe/tests/test_oboe_api.cpp
new file mode 100644
index 0000000..acf3000
--- /dev/null
+++ b/media/liboboe/tests/test_oboe_api.cpp
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// Unit tests for Oboe 'C' API.
+
+#include <stdlib.h>
+#include <math.h>
+
+#include <gtest/gtest.h>
+
+#include <oboe/OboeDefinitions.h>
+#include <oboe/OboeAudio.h>
+#include "OboeUtilities.h"
+
+#define DEFAULT_STATE_TIMEOUT (500 * OBOE_NANOS_PER_MILLISECOND)
+
+// Test OboeStreamBuilder
+TEST(test_oboe_api, oboe_stream_builder) {
+ const oboe_sample_rate_t requestedSampleRate1 = 48000;
+ const oboe_sample_rate_t requestedSampleRate2 = 44100;
+ const int32_t requestedSamplesPerFrame = 2;
+ const oboe_audio_format_t requestedDataFormat = OBOE_AUDIO_DATATYPE_INT16;
+
+ oboe_sample_rate_t sampleRate = 0;
+ int32_t samplesPerFrame = 0;
+ oboe_audio_format_t actualDataFormat;
+ OboeStreamBuilder oboeBuilder1;
+ OboeStreamBuilder oboeBuilder2;
+
+ oboe_result_t result = OBOE_OK;
+
+ // Use an OboeStreamBuilder to define the stream.
+ result = Oboe_createStreamBuilder(&oboeBuilder1);
+ ASSERT_EQ(OBOE_OK, result);
+
+ // Request stream properties.
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSampleRate(oboeBuilder1, requestedSampleRate1));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSamplesPerFrame(oboeBuilder1, requestedSamplesPerFrame));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setFormat(oboeBuilder1, requestedDataFormat));
+
+ // Check to make sure builder saved the properties.
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder1, &sampleRate));
+ EXPECT_EQ(requestedSampleRate1, sampleRate);
+
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSamplesPerFrame(oboeBuilder1, &samplesPerFrame));
+ EXPECT_EQ(requestedSamplesPerFrame, samplesPerFrame);
+
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getFormat(oboeBuilder1, &actualDataFormat));
+ EXPECT_EQ(requestedDataFormat, actualDataFormat);
+
+ result = OboeStreamBuilder_getSampleRate(0x0BADCAFE, &sampleRate); // ridiculous token
+ EXPECT_EQ(OBOE_ERROR_INVALID_HANDLE, result);
+
+ // Create a second builder and make sure they do not collide.
+ ASSERT_EQ(OBOE_OK, Oboe_createStreamBuilder(&oboeBuilder2));
+ ASSERT_NE(oboeBuilder1, oboeBuilder2);
+
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSampleRate(oboeBuilder2, requestedSampleRate2));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder1, &sampleRate));
+ EXPECT_EQ(requestedSampleRate1, sampleRate);
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder2, &sampleRate));
+ EXPECT_EQ(requestedSampleRate2, sampleRate);
+
+ // Delete the builder.
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder1));
+
+ // Now it should no longer be valid.
+ // Note that test assumes we are using the HandleTracker. If we use plain pointers
+ // then it will be difficult to detect this kind of error.
+ result = OboeStreamBuilder_getSampleRate(oboeBuilder1, &sampleRate); // stale token
+ EXPECT_EQ(OBOE_ERROR_INVALID_HANDLE, result);
+
+ // Second builder should still be valid.
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder2, &sampleRate));
+ EXPECT_EQ(requestedSampleRate2, sampleRate);
+
+ // Delete the second builder.
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder2));
+
+ // Now it should no longer be valid. Assumes HandlerTracker used.
+ EXPECT_EQ(OBOE_ERROR_INVALID_HANDLE, OboeStreamBuilder_getSampleRate(oboeBuilder2, &sampleRate));
+}
+
+
+// Test creating a default stream with everything unspecified.
+TEST(test_oboe_api, oboe_stream_unspecified) {
+ OboeStreamBuilder oboeBuilder;
+ OboeStream oboeStream;
+ oboe_result_t result = OBOE_OK;
+
+ // Use an OboeStreamBuilder to define the stream.
+ result = Oboe_createStreamBuilder(&oboeBuilder);
+ ASSERT_EQ(OBOE_OK, result);
+
+ // Create an OboeStream using the Builder.
+ ASSERT_EQ(OBOE_OK, OboeStreamBuilder_openStream(oboeBuilder, &oboeStream));
+
+ // Cleanup
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder));
+ EXPECT_EQ(OBOE_OK, OboeStream_close(oboeStream));
+}
+
+// Test Writing to an OboeStream
+TEST(test_oboe_api, oboe_stream) {
+ const oboe_sample_rate_t requestedSampleRate = 48000;
+ const oboe_sample_rate_t requestedSamplesPerFrame = 2;
+ const oboe_audio_format_t requestedDataFormat = OBOE_AUDIO_DATATYPE_INT16;
+ //const oboe_sharing_mode_t requestedSharingMode = OBOE_SHARING_MODE_EXCLUSIVE; // MMAP NOIRQ
+ const oboe_sharing_mode_t requestedSharingMode = OBOE_SHARING_MODE_LEGACY; // AudioTrack
+
+ oboe_sample_rate_t actualSampleRate = -1;
+ int32_t actualSamplesPerFrame = -1;
+ oboe_audio_format_t actualDataFormat = OBOE_AUDIO_FORMAT_PCM824;
+ oboe_sharing_mode_t actualSharingMode;
+ oboe_size_frames_t framesPerBurst = -1;
+
+ oboe_size_frames_t framesWritten = 0;
+ oboe_size_frames_t framesPrimed = 0;
+ oboe_position_frames_t framesTotal = 0;
+ oboe_position_frames_t oboeFramesRead = 0;
+ oboe_position_frames_t oboeFramesRead1 = 0;
+ oboe_position_frames_t oboeFramesRead2 = 0;
+ oboe_position_frames_t oboeFramesWritten = 0;
+
+ oboe_nanoseconds_t timeoutNanos;
+
+ oboe_stream_state_t state = OBOE_STREAM_STATE_UNINITIALIZED;
+ OboeStreamBuilder oboeBuilder;
+ OboeStream oboeStream;
+
+ oboe_result_t result = OBOE_OK;
+
+ // Use an OboeStreamBuilder to define the stream.
+ result = Oboe_createStreamBuilder(&oboeBuilder);
+ ASSERT_EQ(OBOE_OK, result);
+
+ // Request stream properties.
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSampleRate(oboeBuilder, requestedSampleRate));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSamplesPerFrame(oboeBuilder, requestedSamplesPerFrame));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setFormat(oboeBuilder, requestedDataFormat));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSharingMode(oboeBuilder, requestedSharingMode));
+
+ // Create an OboeStream using the Builder.
+ ASSERT_EQ(OBOE_OK, OboeStreamBuilder_openStream(oboeBuilder, &oboeStream));
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder));
+
+ EXPECT_EQ(OBOE_OK, OboeStream_getState(oboeStream, &state));
+ EXPECT_EQ(OBOE_STREAM_STATE_OPEN, state);
+
+ // Check to see what kind of stream we actually got.
+ EXPECT_EQ(OBOE_OK, OboeStream_getSampleRate(oboeStream, &actualSampleRate));
+ EXPECT_TRUE(actualSampleRate >= 44100 && actualSampleRate <= 96000); // TODO what is range?
+
+ EXPECT_EQ(OBOE_OK, OboeStream_getSamplesPerFrame(oboeStream, &actualSamplesPerFrame));
+ EXPECT_TRUE(actualSamplesPerFrame >= 1 && actualSamplesPerFrame <= 16); // TODO what is max?
+
+ EXPECT_EQ(OBOE_OK, OboeStream_getSharingMode(oboeStream, &actualSharingMode));
+ EXPECT_TRUE(actualSharingMode == OBOE_SHARING_MODE_EXCLUSIVE
+ || actualSharingMode == OBOE_SHARING_MODE_LEGACY);
+
+ EXPECT_EQ(OBOE_OK, OboeStream_getFramesPerBurst(oboeStream, &framesPerBurst));
+ EXPECT_TRUE(framesPerBurst >= 16 && framesPerBurst <= 1024); // TODO what is min/max?
+
+ // Allocate a buffer for the audio data.
+ int16_t *data = new int16_t[framesPerBurst * actualSamplesPerFrame];
+ ASSERT_TRUE(NULL != data);
+
+ timeoutNanos = 0;
+ do {
+ framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+ // There should be some room for priming the buffer.
+ framesTotal += framesWritten;
+ ASSERT_GE(framesWritten, 0);
+ ASSERT_LE(framesWritten, framesPerBurst);
+ } while(framesWritten > 0);
+ ASSERT_TRUE(framesTotal > 0);
+
+ // Start and wait for server to respond.
+ ASSERT_EQ(OBOE_OK, OboeStream_requestStart(oboeStream));
+ ASSERT_EQ(OBOE_OK, OboeStream_waitForStateChange(oboeStream,
+ OBOE_STREAM_STATE_STARTING,
+ &state,
+ DEFAULT_STATE_TIMEOUT));
+ EXPECT_EQ(OBOE_STREAM_STATE_STARTED, state);
+
+ // Write some data while we are running. Read counter should be advancing.
+ int loops = 1 * actualSampleRate / framesPerBurst; // 1 second
+ ASSERT_LT(2, loops); // detect absurdly high framesPerBurst
+ timeoutNanos = 10 * OBOE_NANOS_PER_SECOND * framesPerBurst / actualSampleRate; // bursts
+ framesWritten = 1;
+ ASSERT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+ oboeFramesRead1 = oboeFramesRead;
+ oboe_nanoseconds_t beginTime = Oboe_getNanoseconds(OBOE_CLOCK_MONOTONIC);
+ do {
+ framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+ ASSERT_GE(framesWritten, 0);
+ ASSERT_LE(framesWritten, framesPerBurst);
+
+ framesTotal += framesWritten;
+ EXPECT_EQ(OBOE_OK, OboeStream_getFramesWritten(oboeStream, &oboeFramesWritten));
+ EXPECT_EQ(framesTotal, oboeFramesWritten);
+
+ // Try to get a more accurate measure of the sample rate.
+ if (beginTime == 0) {
+ EXPECT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+ if (oboeFramesRead > oboeFramesRead1) { // is read pointer advancing
+ beginTime = Oboe_getNanoseconds(OBOE_CLOCK_MONOTONIC);
+ oboeFramesRead1 = oboeFramesRead;
+ }
+ }
+ } while (framesWritten > 0 && loops-- > 0);
+
+ EXPECT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead2));
+ oboe_nanoseconds_t endTime = Oboe_getNanoseconds(OBOE_CLOCK_MONOTONIC);
+ ASSERT_GT(oboeFramesRead2, 0);
+ ASSERT_GT(oboeFramesRead2, oboeFramesRead1);
+ ASSERT_LE(oboeFramesRead2, oboeFramesWritten);
+
+ // TODO why is legacy so inaccurate?
+ const double rateTolerance = 200.0; // arbitrary tolerance for sample rate
+ if (requestedSharingMode != OBOE_SHARING_MODE_LEGACY) {
+ // Calculate approximate sample rate and compare with stream rate.
+ double seconds = (endTime - beginTime) / (double) OBOE_NANOS_PER_SECOND;
+ double measuredRate = (oboeFramesRead2 - oboeFramesRead1) / seconds;
+ ASSERT_NEAR(actualSampleRate, measuredRate, rateTolerance);
+ }
+
+ // Request async pause and wait for server to say that it has completed the pause.
+ ASSERT_EQ(OBOE_OK, OboeStream_requestPause(oboeStream));
+ EXPECT_EQ(OBOE_OK, OboeStream_waitForStateChange(oboeStream,
+ OBOE_STREAM_STATE_PAUSING,
+ &state,
+ DEFAULT_STATE_TIMEOUT));
+ EXPECT_EQ(OBOE_STREAM_STATE_PAUSED, state);
+
+ // Make sure the read counter is not advancing when we are paused.
+ ASSERT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+ ASSERT_GE(oboeFramesRead, oboeFramesRead2); // monotonic increase
+
+ // Use this to sleep by waiting for something that won't happen.
+ OboeStream_waitForStateChange(oboeStream, OBOE_STREAM_STATE_PAUSED, &state, timeoutNanos);
+ ASSERT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead2));
+ EXPECT_EQ(oboeFramesRead, oboeFramesRead2);
+
+ // Fill up the buffer.
+ timeoutNanos = 0;
+ loops = 100;
+ do {
+ framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+ framesTotal += framesWritten;
+ } while (framesWritten > 0 && loops-- > 0);
+ EXPECT_EQ(0, framesWritten);
+
+ // Flush and wait for server to respond.
+ ASSERT_EQ(OBOE_OK, OboeStream_requestFlush(oboeStream));
+ EXPECT_EQ(OBOE_OK, OboeStream_waitForStateChange(oboeStream,
+ OBOE_STREAM_STATE_FLUSHING,
+ &state,
+ DEFAULT_STATE_TIMEOUT));
+ EXPECT_EQ(OBOE_STREAM_STATE_FLUSHED, state);
+
+ // After a flush, the read counter should be caught up with the write counter.
+ EXPECT_EQ(OBOE_OK, OboeStream_getFramesWritten(oboeStream, &oboeFramesWritten));
+ EXPECT_EQ(framesTotal, oboeFramesWritten);
+ EXPECT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+ EXPECT_EQ(oboeFramesRead, oboeFramesWritten);
+
+ // The buffer should be empty after a flush so we should be able to write.
+ framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+ // There should be some room for priming the buffer.
+ ASSERT_TRUE(framesWritten > 0 && framesWritten <= framesPerBurst);
+
+ EXPECT_EQ(OBOE_OK, OboeStream_close(oboeStream));
+}
+
+#define OBOE_THREAD_ANSWER 1826375
+#define OBOE_THREAD_DURATION_MSEC 500
+
+static void *TestOboeStreamThreadProc(void *arg) {
+ OboeStream oboeStream = (OboeStream) reinterpret_cast<size_t>(arg);
+ oboe_stream_state_t state;
+
+ // Use this to sleep by waiting for something that won't happen.
+ EXPECT_EQ(OBOE_OK, OboeStream_getState(oboeStream, &state));
+ OboeStream_waitForStateChange(oboeStream, OBOE_STREAM_STATE_PAUSED, &state,
+ OBOE_THREAD_DURATION_MSEC * OBOE_NANOS_PER_MILLISECOND);
+ return reinterpret_cast<void *>(OBOE_THREAD_ANSWER);
+}
+
+// Test creating a stream related thread.
+TEST(test_oboe_api, oboe_stream_thread_basic) {
+ OboeStreamBuilder oboeBuilder;
+ OboeStream oboeStream;
+ oboe_result_t result = OBOE_OK;
+ void *threadResult;
+
+ // Use an OboeStreamBuilder to define the stream.
+ result = Oboe_createStreamBuilder(&oboeBuilder);
+ ASSERT_EQ(OBOE_OK, result);
+
+ // Create an OboeStream using the Builder.
+ ASSERT_EQ(OBOE_OK, OboeStreamBuilder_openStream(oboeBuilder, &oboeStream));
+
+ // Start a thread.
+ ASSERT_EQ(OBOE_OK, OboeStream_createThread(oboeStream,
+ 10 * OBOE_NANOS_PER_MILLISECOND,
+ TestOboeStreamThreadProc,
+ reinterpret_cast<void *>(oboeStream)));
+ // Thread already started.
+ ASSERT_NE(OBOE_OK, OboeStream_createThread(oboeStream, // should fail!
+ 10 * OBOE_NANOS_PER_MILLISECOND,
+ TestOboeStreamThreadProc,
+ reinterpret_cast<void *>(oboeStream)));
+
+ // Wait for the thread to finish.
+ ASSERT_EQ(OBOE_OK, OboeStream_joinThread(oboeStream,
+ &threadResult, 2 * OBOE_THREAD_DURATION_MSEC * OBOE_NANOS_PER_MILLISECOND));
+ // The thread returns a special answer.
+ ASSERT_EQ(OBOE_THREAD_ANSWER, (int)reinterpret_cast<size_t>(threadResult));
+
+ // Thread should already be joined.
+ ASSERT_NE(OBOE_OK, OboeStream_joinThread(oboeStream, // should fail!
+ &threadResult, 2 * OBOE_THREAD_DURATION_MSEC * OBOE_NANOS_PER_MILLISECOND));
+
+ // Cleanup
+ EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder));
+ EXPECT_EQ(OBOE_OK, OboeStream_close(oboeStream));
+}
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index bd837ae..f3d622b 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -108,9 +108,9 @@
libstagefright_mpeg2ts \
libstagefright_id3 \
libFLAC \
- libmedia_helper \
LOCAL_SHARED_LIBRARIES += \
+ libmedia_helper \
libstagefright_foundation \
libdl \
libRScpp \
diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp
index d36ac65..8ba36d5 100644
--- a/media/libstagefright/DRMExtractor.cpp
+++ b/media/libstagefright/DRMExtractor.cpp
@@ -244,7 +244,6 @@
mDecryptHandle(NULL),
mDrmManagerClient(NULL) {
mOriginalExtractor = MediaExtractor::Create(source, mime);
- mOriginalExtractor->setDrmFlag(true);
mOriginalExtractor->getMetaData()->setInt32(kKeyIsDRM, 1);
source->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index aeaead5..49f480d 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -160,6 +160,8 @@
if (!strncmp(drmMime, "drm+es_based+", 13)) {
// DRMExtractor sets container metadata kKeyIsDRM to 1
return new DRMExtractor(source, drmMime + 14);
+ } else {
+ mime = drmMime + 20; // get real mimetype after "drm+container_based+" prefix
}
}
@@ -201,28 +203,6 @@
mime, confidence);
}
- bool isDrm = false;
- // DRM MIME type syntax is "drm+type+original" where
- // type is "es_based" or "container_based" and
- // original is the content's cleartext MIME type
- if (!strncmp(mime, "drm+", 4)) {
- const char *originalMime = strchr(mime+4, '+');
- if (originalMime == NULL) {
- // second + not found
- return NULL;
- }
- ++originalMime;
- if (!strncmp(mime, "drm+es_based+", 13)) {
- // DRMExtractor sets container metadata kKeyIsDRM to 1
- return new DRMExtractor(source, originalMime);
- } else if (!strncmp(mime, "drm+container_based+", 20)) {
- mime = originalMime;
- isDrm = true;
- } else {
- return NULL;
- }
- }
-
MediaExtractor *ret = NULL;
if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
|| !strcasecmp(mime, "audio/mp4")) {
@@ -250,14 +230,6 @@
ret = new MidiExtractor(source);
}
- if (ret != NULL) {
- if (isDrm) {
- ret->setDrmFlag(true);
- } else {
- ret->setDrmFlag(false);
- }
- }
-
return ret;
}
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 6f8220f..d25ce6c 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -82,14 +82,6 @@
return ERROR_UNSUPPORTED;
}
- sp<MetaData> fileMeta = mImpl->getMetaData();
- if (mImpl->getDrmFlag()) {
- // Don't expose decrypted content to Java application
- mImpl.clear();
- mImpl = NULL;
- return ERROR_UNSUPPORTED;
- }
-
status_t err = updateDurationAndBitrate();
if (err == OK) {
mDataSource = dataSource;
diff --git a/media/libstagefright/SimpleDecodingSource.cpp b/media/libstagefright/SimpleDecodingSource.cpp
index 4c4d93e..ea7d5af 100644
--- a/media/libstagefright/SimpleDecodingSource.cpp
+++ b/media/libstagefright/SimpleDecodingSource.cpp
@@ -18,6 +18,7 @@
#include <media/ICrypto.h>
#include <media/MediaCodecBuffer.h>
+#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
@@ -74,7 +75,10 @@
err = codec->getOutputFormat(&format);
}
if (err == OK) {
- return new SimpleDecodingSource(codec, source, looper, surface != NULL, format);
+ return new SimpleDecodingSource(codec, source, looper,
+ surface != NULL,
+ strcmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS) == 0,
+ format);
}
ALOGD("Failed to configure codec '%s'", componentName.c_str());
@@ -90,11 +94,12 @@
SimpleDecodingSource::SimpleDecodingSource(
const sp<MediaCodec> &codec, const sp<IMediaSource> &source, const sp<ALooper> &looper,
- bool usingSurface, const sp<AMessage> &format)
+ bool usingSurface, bool isVorbis, const sp<AMessage> &format)
: mCodec(codec),
mSource(source),
mLooper(looper),
mUsingSurface(usingSurface),
+ mIsVorbis(isVorbis),
mProtectedState(format) {
mCodec->getName(&mComponentName);
}
@@ -280,16 +285,25 @@
if (in_buf != NULL) {
int64_t timestampUs = 0;
CHECK(in_buf->meta_data()->findInt64(kKeyTime, ×tampUs));
- if (in_buf->range_length() > in_buffer->capacity()) {
+ if (in_buf->range_length() + (mIsVorbis ? 4 : 0) > in_buffer->capacity()) {
ALOGW("'%s' received %zu input bytes for buffer of size %zu",
mComponentName.c_str(),
- in_buf->range_length(), in_buffer->capacity());
+ in_buf->range_length() + (mIsVorbis ? 4 : 0), in_buffer->capacity());
}
+ size_t cpLen = min(in_buf->range_length(), in_buffer->capacity());
memcpy(in_buffer->base(), (uint8_t *)in_buf->data() + in_buf->range_offset(),
- min(in_buf->range_length(), in_buffer->capacity()));
+ cpLen );
+
+ if (mIsVorbis) {
+ int32_t numPageSamples;
+ if (!in_buf->meta_data()->findInt32(kKeyValidSamples, &numPageSamples)) {
+ numPageSamples = -1;
+ }
+ memcpy(in_buffer->base() + cpLen, &numPageSamples, sizeof(numPageSamples));
+ }
res = mCodec->queueInputBuffer(
- in_ix, 0 /* offset */, in_buf->range_length(),
+ in_ix, 0 /* offset */, in_buf->range_length() + (mIsVorbis ? 4 : 0),
timestampUs, 0 /* flags */);
if (res != OK) {
ALOGI("[%s] failed to queue input buffer #%zu", mComponentName.c_str(), in_ix);
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 5e00c44..883a4dd 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -783,11 +783,6 @@
}
}
}
-
- // To check whether the media file is drm-protected
- if (mExtractor->getDrmFlag()) {
- mMetaData.add(METADATA_KEY_IS_DRM, String8("1"));
- }
}
void StagefrightMetadataRetriever::clearMetadata() {
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk
index cb7e4aa..58753ff 100644
--- a/media/mtp/Android.mk
+++ b/media/mtp/Android.mk
@@ -19,26 +19,31 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ AsyncIO.cpp \
MtpDataPacket.cpp \
MtpDebug.cpp \
+ MtpDevHandle.cpp \
MtpDevice.cpp \
- MtpEventPacket.cpp \
MtpDeviceInfo.cpp \
+ MtpEventPacket.cpp \
+ MtpFfsHandle.cpp \
MtpObjectInfo.cpp \
MtpPacket.cpp \
MtpProperty.cpp \
MtpRequestPacket.cpp \
MtpResponsePacket.cpp \
MtpServer.cpp \
+ MtpStorage.cpp \
MtpStorageInfo.cpp \
MtpStringBuffer.cpp \
- MtpStorage.cpp \
MtpUtils.cpp \
LOCAL_MODULE:= libmtp
LOCAL_CFLAGS := -DMTP_DEVICE -DMTP_HOST -Wall -Wextra -Werror
-LOCAL_SHARED_LIBRARIES := libutils libcutils liblog libusbhost libbinder
+LOCAL_SHARED_LIBRARIES := libbase libutils libcutils liblog libusbhost libbinder
include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/mtp/AsyncIO.cpp b/media/mtp/AsyncIO.cpp
new file mode 100644
index 0000000..a1a98ab
--- /dev/null
+++ b/media/mtp/AsyncIO.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 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 <android-base/logging.h>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <queue>
+
+#include "AsyncIO.h"
+
+namespace {
+
+void read_func(struct aiocb *aiocbp) {
+ aiocbp->ret = TEMP_FAILURE_RETRY(pread(aiocbp->aio_fildes,
+ aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
+ if (aiocbp->ret == -1) aiocbp->error = errno;
+}
+
+void write_func(struct aiocb *aiocbp) {
+ aiocbp->ret = TEMP_FAILURE_RETRY(pwrite(aiocbp->aio_fildes,
+ aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
+ if (aiocbp->ret == -1) aiocbp->error = errno;
+}
+
+void splice_read_func(struct aiocb *aiocbp) {
+ aiocbp->ret = TEMP_FAILURE_RETRY(splice(aiocbp->aio_fildes,
+ (off64_t*) &aiocbp->aio_offset, aiocbp->aio_sink,
+ NULL, aiocbp->aio_nbytes, 0));
+ if (aiocbp->ret == -1) aiocbp->error = errno;
+}
+
+void splice_write_func(struct aiocb *aiocbp) {
+ aiocbp->ret = TEMP_FAILURE_RETRY(splice(aiocbp->aio_fildes, NULL,
+ aiocbp->aio_sink, (off64_t*) &aiocbp->aio_offset,
+ aiocbp->aio_nbytes, 0));
+ if (aiocbp->ret == -1) aiocbp->error = errno;
+}
+
+std::queue<std::unique_ptr<struct aiocb>> queue;
+std::mutex queue_lock;
+std::condition_variable queue_cond;
+std::condition_variable write_cond;
+int done = 1;
+void splice_write_pool_func(int) {
+ while(1) {
+ std::unique_lock<std::mutex> lk(queue_lock);
+ queue_cond.wait(lk, []{return !queue.empty() || done;});
+ if (queue.empty() && done) {
+ return;
+ }
+ std::unique_ptr<struct aiocb> aiocbp = std::move(queue.front());
+ queue.pop();
+ lk.unlock();
+ write_cond.notify_one();
+ splice_write_func(aiocbp.get());
+ close(aiocbp->aio_fildes);
+ }
+}
+
+void write_pool_func(int) {
+ while(1) {
+ std::unique_lock<std::mutex> lk(queue_lock);
+ queue_cond.wait(lk, []{return !queue.empty() || done;});
+ if (queue.empty() && done) {
+ return;
+ }
+ std::unique_ptr<struct aiocb> aiocbp = std::move(queue.front());
+ queue.pop();
+ lk.unlock();
+ write_cond.notify_one();
+ aiocbp->ret = TEMP_FAILURE_RETRY(pwrite(aiocbp->aio_fildes,
+ aiocbp->aio_pool_buf.get(), aiocbp->aio_nbytes, aiocbp->aio_offset));
+ if (aiocbp->ret == -1) aiocbp->error = errno;
+ }
+}
+
+constexpr int NUM_THREADS = 1;
+constexpr int MAX_QUEUE_SIZE = 10;
+std::thread pool[NUM_THREADS];
+
+} // end anonymous namespace
+
+void aio_pool_init(void(f)(int)) {
+ CHECK(done == 1);
+ done = 0;
+ for (int i = 0; i < NUM_THREADS; i++) {
+ pool[i] = std::thread(f, i);
+ }
+}
+
+void aio_pool_splice_init() {
+ aio_pool_init(splice_write_pool_func);
+}
+
+void aio_pool_write_init() {
+ aio_pool_init(write_pool_func);
+}
+
+void aio_pool_end() {
+ done = 1;
+ for (int i = 0; i < NUM_THREADS; i++) {
+ std::unique_lock<std::mutex> lk(queue_lock);
+ lk.unlock();
+ queue_cond.notify_one();
+ }
+
+ for (int i = 0; i < NUM_THREADS; i++) {
+ pool[i].join();
+ }
+}
+
+// used for both writes and splices depending on which init was used before.
+int aio_pool_write(struct aiocb *aiocbp) {
+ std::unique_lock<std::mutex> lk(queue_lock);
+ write_cond.wait(lk, []{return queue.size() < MAX_QUEUE_SIZE;});
+ queue.push(std::unique_ptr<struct aiocb>(aiocbp));
+ lk.unlock();
+ queue_cond.notify_one();
+ return 0;
+}
+
+int aio_read(struct aiocb *aiocbp) {
+ aiocbp->thread = std::thread(read_func, aiocbp);
+ return 0;
+}
+
+int aio_write(struct aiocb *aiocbp) {
+ aiocbp->thread = std::thread(write_func, aiocbp);
+ return 0;
+}
+
+int aio_splice_read(struct aiocb *aiocbp) {
+ aiocbp->thread = std::thread(splice_read_func, aiocbp);
+ return 0;
+}
+
+int aio_splice_write(struct aiocb *aiocbp) {
+ aiocbp->thread = std::thread(splice_write_func, aiocbp);
+ return 0;
+}
+
+int aio_error(const struct aiocb *aiocbp) {
+ return aiocbp->error;
+}
+
+ssize_t aio_return(struct aiocb *aiocbp) {
+ return aiocbp->ret;
+}
+
+int aio_suspend(struct aiocb *aiocbp[], int n,
+ const struct timespec *) {
+ for (int i = 0; i < n; i++) {
+ aiocbp[i]->thread.join();
+ }
+ return 0;
+}
+
+int aio_cancel(int, struct aiocb *) {
+ // Not implemented
+ return -1;
+}
+
diff --git a/media/mtp/AsyncIO.h b/media/mtp/AsyncIO.h
new file mode 100644
index 0000000..f7515a2
--- /dev/null
+++ b/media/mtp/AsyncIO.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 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 _ASYNCIO_H
+#define _ASYNCIO_H
+
+#include <fcntl.h>
+#include <linux/aio_abi.h>
+#include <memory>
+#include <signal.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <time.h>
+#include <thread>
+#include <unistd.h>
+
+/**
+ * Provides a subset of POSIX aio operations, as well
+ * as similar operations with splice and threadpools.
+ */
+
+struct aiocb {
+ int aio_fildes; // Assumed to be the source for splices
+ void *aio_buf; // Unused for splices
+
+ // Used for threadpool operations only, freed automatically
+ std::unique_ptr<char[]> aio_pool_buf;
+
+ off_t aio_offset;
+ size_t aio_nbytes;
+
+ int aio_sink; // Unused for non splice r/w
+
+ // Used internally
+ std::thread thread;
+ ssize_t ret;
+ int error;
+};
+
+// Submit a request for IO to be completed
+int aio_read(struct aiocb *);
+int aio_write(struct aiocb *);
+int aio_splice_read(struct aiocb *);
+int aio_splice_write(struct aiocb *);
+
+// Suspend current thread until given IO is complete, at which point
+// its return value and any errors can be accessed
+int aio_suspend(struct aiocb *[], int, const struct timespec *);
+int aio_error(const struct aiocb *);
+ssize_t aio_return(struct aiocb *);
+int aio_cancel(int, struct aiocb *);
+
+// Initialize a threadpool to perform IO. Only one pool can be
+// running at a time.
+void aio_pool_write_init();
+void aio_pool_splice_init();
+// Suspend current thread until all queued work is complete, then ends the threadpool
+void aio_pool_end();
+// Submit IO work for the threadpool to complete. Memory associated with the work is
+// freed automatically when the work is complete.
+int aio_pool_write(struct aiocb *);
+
+#endif // ASYNCIO_H
+
diff --git a/media/mtp/IMtpHandle.h b/media/mtp/IMtpHandle.h
new file mode 100644
index 0000000..9185255
--- /dev/null
+++ b/media/mtp/IMtpHandle.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 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 _IMTP_HANDLE_H
+#define _IMTP_HANDLE_H
+
+#include <linux/usb/f_mtp.h>
+
+constexpr char FFS_MTP_EP0[] = "/dev/usb-ffs/mtp/ep0";
+
+class IMtpHandle {
+public:
+ // Return number of bytes read/written, or -1 and errno is set
+ virtual int read(void *data, int len) = 0;
+ virtual int write(const void *data, int len) = 0;
+
+ // Return 0 if send/receive is successful, or -1 and errno is set
+ virtual int receiveFile(mtp_file_range mfr) = 0;
+ virtual int sendFile(mtp_file_range mfr) = 0;
+ virtual int sendEvent(mtp_event me) = 0;
+
+ // Return 0 if operation is successful, or -1 else
+ virtual int start() = 0;
+ virtual int configure(bool ptp) = 0;
+
+ virtual void close() = 0;
+
+ virtual ~IMtpHandle() {}
+};
+
+IMtpHandle *get_ffs_handle();
+IMtpHandle *get_mtp_handle();
+
+#endif // _IMTP_HANDLE_H
+
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 0356753..d1c71d7 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -24,6 +24,7 @@
#include <sys/types.h>
#include <usbhost/usbhost.h>
#include "MtpStringBuffer.h"
+#include "IMtpHandle.h"
namespace android {
@@ -438,9 +439,9 @@
putUInt16(0);
}
-#ifdef MTP_DEVICE
-int MtpDataPacket::read(int fd) {
- int ret = ::read(fd, mBuffer, MTP_BUFFER_SIZE);
+#ifdef MTP_DEVICE
+int MtpDataPacket::read(IMtpHandle *h) {
+ int ret = h->read(mBuffer, MTP_BUFFER_SIZE);
if (ret < MTP_CONTAINER_HEADER_SIZE)
return -1;
mPacketSize = ret;
@@ -448,20 +449,20 @@
return ret;
}
-int MtpDataPacket::write(int fd) {
+int MtpDataPacket::write(IMtpHandle *h) {
MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
- int ret = ::write(fd, mBuffer, mPacketSize);
+ int ret = h->write(mBuffer, mPacketSize);
return (ret < 0 ? ret : 0);
}
-int MtpDataPacket::writeData(int fd, void* data, uint32_t length) {
+int MtpDataPacket::writeData(IMtpHandle *h, void* data, uint32_t length) {
allocate(length + MTP_CONTAINER_HEADER_SIZE);
memcpy(mBuffer + MTP_CONTAINER_HEADER_SIZE, data, length);
length += MTP_CONTAINER_HEADER_SIZE;
MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length);
MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
- int ret = ::write(fd, mBuffer, length);
+ int ret = h->write(mBuffer, length);
return (ret < 0 ? ret : 0);
}
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index 82e0ee4..a449d6f 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -20,6 +20,7 @@
#include "MtpPacket.h"
#include "mtp.h"
+class IMtpHandle;
struct usb_device;
struct usb_request;
@@ -94,12 +95,12 @@
inline void putEmptyArray() { putUInt32(0); }
#ifdef MTP_DEVICE
- // fill our buffer with data from the given file descriptor
- int read(int fd);
+ // fill our buffer with data from the given usb handle
+ int read(IMtpHandle *h);
- // write our data to the given file descriptor
- int write(int fd);
- int writeData(int fd, void* data, uint32_t length);
+ // write our data to the given usb handle
+ int write(IMtpHandle *h);
+ int writeData(IMtpHandle *h, void* data, uint32_t length);
#endif
#ifdef MTP_HOST
diff --git a/media/mtp/MtpDevHandle.cpp b/media/mtp/MtpDevHandle.cpp
new file mode 100644
index 0000000..afc0525
--- /dev/null
+++ b/media/mtp/MtpDevHandle.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 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 <utils/Log.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <cutils/properties.h>
+#include <dirent.h>
+#include <errno.h>
+#include <linux/usb/ch9.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include "IMtpHandle.h"
+
+constexpr char mtp_dev_path[] = "/dev/mtp_usb";
+
+class MtpDevHandle : public IMtpHandle {
+private:
+ android::base::unique_fd mFd;
+
+public:
+ MtpDevHandle();
+ ~MtpDevHandle();
+ int read(void *data, int len);
+ int write(const void *data, int len);
+
+ int receiveFile(mtp_file_range mfr);
+ int sendFile(mtp_file_range mfr);
+ int sendEvent(mtp_event me);
+
+ int start();
+ void close();
+
+ int configure(bool ptp);
+};
+
+MtpDevHandle::MtpDevHandle()
+ : mFd(-1) {};
+
+MtpDevHandle::~MtpDevHandle() {}
+
+int MtpDevHandle::read(void *data, int len) {
+ return ::read(mFd, data, len);
+}
+
+int MtpDevHandle::write(const void *data, int len) {
+ return ::write(mFd, data, len);
+}
+
+int MtpDevHandle::receiveFile(mtp_file_range mfr) {
+ return ioctl(mFd, MTP_RECEIVE_FILE, reinterpret_cast<unsigned long>(&mfr));
+}
+
+int MtpDevHandle::sendFile(mtp_file_range mfr) {
+ return ioctl(mFd, MTP_SEND_FILE_WITH_HEADER, reinterpret_cast<unsigned long>(&mfr));
+}
+
+int MtpDevHandle::sendEvent(mtp_event me) {
+ return ioctl(mFd, MTP_SEND_EVENT, reinterpret_cast<unsigned long>(&me));
+}
+
+int MtpDevHandle::start() {
+ mFd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(mtp_dev_path, O_RDWR)));
+ if (mFd == -1) return -1;
+ return 0;
+}
+
+void MtpDevHandle::close() {
+ mFd.reset();
+}
+
+int MtpDevHandle::configure(bool) {
+ // Nothing to do, driver can configure itself
+ return 0;
+}
+
+IMtpHandle *get_mtp_handle() {
+ return new MtpDevHandle();
+}
diff --git a/media/mtp/MtpEventPacket.cpp b/media/mtp/MtpEventPacket.cpp
index d9ef311..921ecbd 100644
--- a/media/mtp/MtpEventPacket.cpp
+++ b/media/mtp/MtpEventPacket.cpp
@@ -19,12 +19,8 @@
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
-#include <sys/ioctl.h>
-#ifdef MTP_DEVICE
-#include <linux/usb/f_mtp.h>
-#endif
-
+#include "IMtpHandle.h"
#include "MtpEventPacket.h"
#include <usbhost/usbhost.h>
@@ -40,7 +36,7 @@
}
#ifdef MTP_DEVICE
-int MtpEventPacket::write(int fd) {
+int MtpEventPacket::write(IMtpHandle *h) {
struct mtp_event event;
putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
@@ -48,7 +44,7 @@
event.data = mBuffer;
event.length = mPacketSize;
- int ret = ::ioctl(fd, MTP_SEND_EVENT, (unsigned long)&event);
+ int ret = h->sendEvent(event);
return (ret < 0 ? ret : 0);
}
#endif
diff --git a/media/mtp/MtpEventPacket.h b/media/mtp/MtpEventPacket.h
index a8779fd..3f3b6a3 100644
--- a/media/mtp/MtpEventPacket.h
+++ b/media/mtp/MtpEventPacket.h
@@ -20,6 +20,8 @@
#include "MtpPacket.h"
#include "mtp.h"
+class IMtpHandle;
+
namespace android {
class MtpEventPacket : public MtpPacket {
@@ -29,8 +31,8 @@
virtual ~MtpEventPacket();
#ifdef MTP_DEVICE
- // write our data to the given file descriptor
- int write(int fd);
+ // write our data to the given usb handle
+ int write(IMtpHandle *h);
#endif
#ifdef MTP_HOST
diff --git a/media/mtp/MtpFfsHandle.cpp b/media/mtp/MtpFfsHandle.cpp
new file mode 100644
index 0000000..10314e9
--- /dev/null
+++ b/media/mtp/MtpFfsHandle.cpp
@@ -0,0 +1,670 @@
+/*
+ * Copyright (C) 2016 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 <android-base/logging.h>
+#include <android-base/properties.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+#include <mutex>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/endian.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <vector>
+
+#include "AsyncIO.h"
+#include "MtpFfsHandle.h"
+
+#define cpu_to_le16(x) htole16(x)
+#define cpu_to_le32(x) htole32(x)
+
+namespace {
+
+constexpr char FFS_MTP_EP_IN[] = "/dev/usb-ffs/mtp/ep1";
+constexpr char FFS_MTP_EP_OUT[] = "/dev/usb-ffs/mtp/ep2";
+constexpr char FFS_MTP_EP_INTR[] = "/dev/usb-ffs/mtp/ep3";
+
+constexpr int MAX_PACKET_SIZE_FS = 64;
+constexpr int MAX_PACKET_SIZE_HS = 512;
+constexpr int MAX_PACKET_SIZE_SS = 1024;
+
+// Must be divisible by all max packet size values
+constexpr int MAX_FILE_CHUNK_SIZE = 3145728;
+
+// Safe values since some devices cannot handle large DMAs
+// To get good performance, override these with
+// higher values per device using the properties
+// sys.usb.ffs.max_read and sys.usb.ffs.max_write
+constexpr int USB_FFS_MAX_WRITE = 32768;
+constexpr int USB_FFS_MAX_READ = 32768;
+
+constexpr unsigned int MAX_MTP_FILE_SIZE = 0xFFFFFFFF;
+
+struct func_desc {
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor_no_audio sink;
+ struct usb_endpoint_descriptor_no_audio source;
+ struct usb_endpoint_descriptor_no_audio intr;
+} __attribute__((packed));
+
+struct ss_func_desc {
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor_no_audio sink;
+ struct usb_ss_ep_comp_descriptor sink_comp;
+ struct usb_endpoint_descriptor_no_audio source;
+ struct usb_ss_ep_comp_descriptor source_comp;
+ struct usb_endpoint_descriptor_no_audio intr;
+ struct usb_ss_ep_comp_descriptor intr_comp;
+} __attribute__((packed));
+
+struct desc_v1 {
+ struct usb_functionfs_descs_head_v1 {
+ __le32 magic;
+ __le32 length;
+ __le32 fs_count;
+ __le32 hs_count;
+ } __attribute__((packed)) header;
+ struct func_desc fs_descs, hs_descs;
+} __attribute__((packed));
+
+struct desc_v2 {
+ struct usb_functionfs_descs_head_v2 header;
+ // The rest of the structure depends on the flags in the header.
+ __le32 fs_count;
+ __le32 hs_count;
+ __le32 ss_count;
+ struct func_desc fs_descs, hs_descs;
+ struct ss_func_desc ss_descs;
+} __attribute__((packed));
+
+const struct usb_interface_descriptor mtp_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 3,
+ .bInterfaceClass = USB_CLASS_STILL_IMAGE,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 1,
+};
+
+const struct usb_interface_descriptor ptp_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 3,
+ .bInterfaceClass = USB_CLASS_STILL_IMAGE,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 1,
+};
+
+const struct usb_endpoint_descriptor_no_audio fs_sink = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+};
+
+const struct usb_endpoint_descriptor_no_audio fs_source = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+};
+
+const struct usb_endpoint_descriptor_no_audio fs_intr = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 3 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+ .bInterval = 6,
+};
+
+const struct usb_endpoint_descriptor_no_audio hs_sink = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+};
+
+const struct usb_endpoint_descriptor_no_audio hs_source = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+};
+
+const struct usb_endpoint_descriptor_no_audio hs_intr = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 3 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = MAX_PACKET_SIZE_HS,
+ .bInterval = 6,
+};
+
+const struct usb_endpoint_descriptor_no_audio ss_sink = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 1 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+};
+
+const struct usb_endpoint_descriptor_no_audio ss_source = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 2 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+};
+
+const struct usb_endpoint_descriptor_no_audio ss_intr = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 3 | USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = MAX_PACKET_SIZE_SS,
+ .bInterval = 6,
+};
+
+const struct usb_ss_ep_comp_descriptor ss_sink_comp = {
+ .bLength = sizeof(ss_sink_comp),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 2,
+};
+
+const struct usb_ss_ep_comp_descriptor ss_source_comp = {
+ .bLength = sizeof(ss_source_comp),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 2,
+};
+
+const struct usb_ss_ep_comp_descriptor ss_intr_comp = {
+ .bLength = sizeof(ss_intr_comp),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+const struct func_desc mtp_fs_descriptors = {
+ .intf = mtp_interface_desc,
+ .sink = fs_sink,
+ .source = fs_source,
+ .intr = fs_intr,
+};
+
+const struct func_desc mtp_hs_descriptors = {
+ .intf = mtp_interface_desc,
+ .sink = hs_sink,
+ .source = hs_source,
+ .intr = hs_intr,
+};
+
+const struct ss_func_desc mtp_ss_descriptors = {
+ .intf = mtp_interface_desc,
+ .sink = ss_sink,
+ .sink_comp = ss_sink_comp,
+ .source = ss_source,
+ .source_comp = ss_source_comp,
+ .intr = ss_intr,
+ .intr_comp = ss_intr_comp,
+};
+
+const struct func_desc ptp_fs_descriptors = {
+ .intf = ptp_interface_desc,
+ .sink = fs_sink,
+ .source = fs_source,
+ .intr = fs_intr,
+};
+
+const struct func_desc ptp_hs_descriptors = {
+ .intf = ptp_interface_desc,
+ .sink = hs_sink,
+ .source = hs_source,
+ .intr = hs_intr,
+};
+
+const struct ss_func_desc ptp_ss_descriptors = {
+ .intf = ptp_interface_desc,
+ .sink = ss_sink,
+ .sink_comp = ss_sink_comp,
+ .source = ss_source,
+ .source_comp = ss_source_comp,
+ .intr = ss_intr,
+ .intr_comp = ss_intr_comp,
+};
+
+const struct {
+ struct usb_functionfs_strings_head header;
+} __attribute__((packed)) strings = {
+ .header = {
+ .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
+ .length = cpu_to_le32(sizeof(strings)),
+ .str_count = cpu_to_le32(0),
+ .lang_count = cpu_to_le32(0),
+ },
+};
+
+} // anonymous namespace
+
+namespace android {
+
+MtpFfsHandle::MtpFfsHandle() :
+ mMaxWrite(USB_FFS_MAX_WRITE),
+ mMaxRead(USB_FFS_MAX_READ) {}
+
+MtpFfsHandle::~MtpFfsHandle() {}
+
+void MtpFfsHandle::closeEndpoints() {
+ mIntr.reset();
+ mBulkIn.reset();
+ mBulkOut.reset();
+}
+
+bool MtpFfsHandle::initFunctionfs() {
+ ssize_t ret;
+ struct desc_v1 v1_descriptor;
+ struct desc_v2 v2_descriptor;
+
+ v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
+ v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
+ v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+ FUNCTIONFS_HAS_SS_DESC;
+ v2_descriptor.fs_count = 4;
+ v2_descriptor.hs_count = 4;
+ v2_descriptor.ss_count = 7;
+ v2_descriptor.fs_descs = mPtp ? ptp_fs_descriptors : mtp_fs_descriptors;
+ v2_descriptor.hs_descs = mPtp ? ptp_hs_descriptors : mtp_hs_descriptors;
+ v2_descriptor.ss_descs = mPtp ? ptp_ss_descriptors : mtp_ss_descriptors;
+
+ if (mControl < 0) { // might have already done this before
+ mControl.reset(TEMP_FAILURE_RETRY(open(FFS_MTP_EP0, O_RDWR)));
+ if (mControl < 0) {
+ PLOG(ERROR) << FFS_MTP_EP0 << ": cannot open control endpoint";
+ goto err;
+ }
+
+ ret = TEMP_FAILURE_RETRY(::write(mControl, &v2_descriptor, sizeof(v2_descriptor)));
+ if (ret < 0) {
+ v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
+ v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
+ v1_descriptor.header.fs_count = 4;
+ v1_descriptor.header.hs_count = 4;
+ v1_descriptor.fs_descs = mPtp ? ptp_fs_descriptors : mtp_fs_descriptors;
+ v1_descriptor.hs_descs = mPtp ? ptp_hs_descriptors : mtp_hs_descriptors;
+ PLOG(ERROR) << FFS_MTP_EP0 << "Switching to V1 descriptor format";
+ ret = TEMP_FAILURE_RETRY(::write(mControl, &v1_descriptor, sizeof(v1_descriptor)));
+ if (ret < 0) {
+ PLOG(ERROR) << FFS_MTP_EP0 << "Writing descriptors failed";
+ goto err;
+ }
+ }
+ ret = TEMP_FAILURE_RETRY(::write(mControl, &strings, sizeof(strings)));
+ if (ret < 0) {
+ PLOG(ERROR) << FFS_MTP_EP0 << "Writing strings failed";
+ goto err;
+ }
+ }
+ if (mBulkIn > -1 || mBulkOut > -1 || mIntr > -1)
+ LOG(WARNING) << "Endpoints were not closed before configure!";
+
+ mBulkIn.reset(TEMP_FAILURE_RETRY(open(FFS_MTP_EP_IN, O_RDWR)));
+ if (mBulkIn < 0) {
+ PLOG(ERROR) << FFS_MTP_EP_IN << ": cannot open bulk in ep";
+ goto err;
+ }
+
+ mBulkOut.reset(TEMP_FAILURE_RETRY(open(FFS_MTP_EP_OUT, O_RDWR)));
+ if (mBulkOut < 0) {
+ PLOG(ERROR) << FFS_MTP_EP_OUT << ": cannot open bulk out ep";
+ goto err;
+ }
+
+ mIntr.reset(TEMP_FAILURE_RETRY(open(FFS_MTP_EP_INTR, O_RDWR)));
+ if (mIntr < 0) {
+ PLOG(ERROR) << FFS_MTP_EP0 << ": cannot open intr ep";
+ goto err;
+ }
+
+ return true;
+
+err:
+ closeEndpoints();
+ closeConfig();
+ return false;
+}
+
+void MtpFfsHandle::closeConfig() {
+ mControl.reset();
+}
+
+int MtpFfsHandle::writeHandle(int fd, const void* data, int len) {
+ LOG(VERBOSE) << "MTP about to write fd = " << fd << ", len=" << len;
+ int ret = 0;
+ const char* buf = static_cast<const char*>(data);
+ while (len > 0) {
+ int write_len = std::min(mMaxWrite, len);
+ int n = TEMP_FAILURE_RETRY(::write(fd, buf, write_len));
+
+ if (n < 0) {
+ PLOG(ERROR) << "write ERROR: fd = " << fd << ", n = " << n;
+ return -1;
+ } else if (n < write_len) {
+ errno = EIO;
+ PLOG(ERROR) << "less written than expected";
+ return -1;
+ }
+ buf += n;
+ len -= n;
+ ret += n;
+ }
+ return ret;
+}
+
+int MtpFfsHandle::readHandle(int fd, void* data, int len) {
+ LOG(VERBOSE) << "MTP about to read fd = " << fd << ", len=" << len;
+ int ret = 0;
+ char* buf = static_cast<char*>(data);
+ while (len > 0) {
+ int read_len = std::min(mMaxRead, len);
+ int n = TEMP_FAILURE_RETRY(::read(fd, buf, read_len));
+ if (n < 0) {
+ PLOG(ERROR) << "read ERROR: fd = " << fd << ", n = " << n;
+ return -1;
+ }
+ ret += n;
+ if (n < read_len) // done reading early
+ break;
+ buf += n;
+ len -= n;
+ }
+ return ret;
+}
+
+int MtpFfsHandle::spliceReadHandle(int fd, int pipe_out, int len) {
+ LOG(VERBOSE) << "MTP about to splice read fd = " << fd << ", len=" << len;
+ int ret = 0;
+ loff_t dummyoff;
+ while (len > 0) {
+ int read_len = std::min(mMaxRead, len);
+ dummyoff = 0;
+ int n = TEMP_FAILURE_RETRY(splice(fd, &dummyoff, pipe_out, nullptr, read_len, 0));
+ if (n < 0) {
+ PLOG(ERROR) << "splice read ERROR: fd = " << fd << ", n = " << n;
+ return -1;
+ }
+ ret += n;
+ if (n < read_len) // done reading early
+ break;
+ len -= n;
+ }
+ return ret;
+}
+
+int MtpFfsHandle::read(void* data, int len) {
+ return readHandle(mBulkOut, data, len);
+}
+
+int MtpFfsHandle::write(const void* data, int len) {
+ return writeHandle(mBulkIn, data, len);
+}
+
+int MtpFfsHandle::start() {
+ mLock.lock();
+ return 0;
+}
+
+int MtpFfsHandle::configure(bool usePtp) {
+ // Wait till previous server invocation has closed
+ std::lock_guard<std::mutex> lk(mLock);
+
+ // If ptp is changed, the configuration must be rewritten
+ if (mPtp != usePtp) {
+ closeEndpoints();
+ closeConfig();
+ }
+ mPtp = usePtp;
+
+ if (!initFunctionfs()) {
+ return -1;
+ }
+
+ // Get device specific r/w size
+ mMaxWrite = android::base::GetIntProperty("sys.usb.ffs.max_write", 0);
+ mMaxRead = android::base::GetIntProperty("sys.usb.ffs.max_read", 0);
+ if (!mMaxWrite)
+ mMaxWrite = USB_FFS_MAX_WRITE;
+ if (!mMaxRead)
+ mMaxRead = USB_FFS_MAX_READ;
+ return 0;
+}
+
+void MtpFfsHandle::close() {
+ closeEndpoints();
+ mLock.unlock();
+}
+
+/* Read from USB and write to a local file. */
+int MtpFfsHandle::receiveFile(mtp_file_range mfr) {
+ // When receiving files, the incoming length is given in 32 bits.
+ // A >4G file is given as 0xFFFFFFFF
+ uint32_t file_length = mfr.length;
+ uint64_t offset = lseek(mfr.fd, 0, SEEK_CUR);
+
+ int buf1_len = std::min(static_cast<uint32_t>(MAX_FILE_CHUNK_SIZE), file_length);
+ std::vector<char> buf1(buf1_len);
+ char* data = buf1.data();
+
+ // If necessary, allocate a second buffer for background r/w
+ int buf2_len = std::min(static_cast<uint32_t>(MAX_FILE_CHUNK_SIZE),
+ file_length - MAX_FILE_CHUNK_SIZE);
+ std::vector<char> buf2(std::max(0, buf2_len));
+ char *data2 = buf2.data();
+
+ struct aiocb aio;
+ aio.aio_fildes = mfr.fd;
+ aio.aio_buf = nullptr;
+ struct aiocb *aiol[] = {&aio};
+ int ret;
+ size_t length;
+ bool read = false;
+ bool write = false;
+
+ posix_fadvise(mfr.fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+
+ // Break down the file into pieces that fit in buffers
+ while (file_length > 0 || write) {
+ if (file_length > 0) {
+ length = std::min(static_cast<uint32_t>(MAX_FILE_CHUNK_SIZE), file_length);
+
+ // Read data from USB
+ if ((ret = readHandle(mBulkOut, data, length)) == -1) {
+ return -1;
+ }
+
+ if (file_length != MAX_MTP_FILE_SIZE && ret < static_cast<int>(length)) {
+ errno = EIO;
+ return -1;
+ }
+ read = true;
+ }
+
+ if (write) {
+ // get the return status of the last write request
+ aio_suspend(aiol, 1, nullptr);
+
+ int written = aio_return(&aio);
+ if (written == -1) {
+ errno = aio_error(&aio);
+ return -1;
+ }
+ if (static_cast<size_t>(written) < aio.aio_nbytes) {
+ errno = EIO;
+ return -1;
+ }
+ write = false;
+ }
+
+ if (read) {
+ // Enqueue a new write request
+ aio.aio_buf = data;
+ aio.aio_sink = mfr.fd;
+ aio.aio_offset = offset;
+ aio.aio_nbytes = ret;
+ aio_write(&aio);
+
+ if (file_length == MAX_MTP_FILE_SIZE) {
+ // For larger files, receive until a short packet is received.
+ if (static_cast<size_t>(ret) < length) {
+ file_length = 0;
+ }
+ } else {
+ file_length -= ret;
+ }
+
+ offset += ret;
+ std::swap(data, data2);
+
+ write = true;
+ read = false;
+ }
+ }
+ return 0;
+}
+
+/* Read from a local file and send over USB. */
+int MtpFfsHandle::sendFile(mtp_file_range mfr) {
+ uint64_t file_length = mfr.length;
+ uint32_t given_length = std::min(static_cast<uint64_t>(MAX_MTP_FILE_SIZE),
+ file_length + sizeof(mtp_data_header));
+ uint64_t offset = 0;
+ struct usb_endpoint_descriptor mBulkIn_desc;
+ int packet_size;
+
+ if (ioctl(mBulkIn, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&mBulkIn_desc))) {
+ PLOG(ERROR) << "Could not get FFS bulk-in descriptor";
+ packet_size = MAX_PACKET_SIZE_HS;
+ } else {
+ packet_size = mBulkIn_desc.wMaxPacketSize;
+ }
+
+ posix_fadvise(mfr.fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+
+ int init_read_len = packet_size - sizeof(mtp_data_header);
+ int buf1_len = std::max(static_cast<uint64_t>(packet_size), std::min(
+ static_cast<uint64_t>(MAX_FILE_CHUNK_SIZE), file_length - init_read_len));
+ std::vector<char> buf1(buf1_len);
+ char *data = buf1.data();
+
+ // If necessary, allocate a second buffer for background r/w
+ int buf2_len = std::min(static_cast<uint64_t>(MAX_FILE_CHUNK_SIZE),
+ file_length - MAX_FILE_CHUNK_SIZE - init_read_len);
+ std::vector<char> buf2(std::max(0, buf2_len));
+ char *data2 = buf2.data();
+
+ struct aiocb aio;
+ aio.aio_fildes = mfr.fd;
+ struct aiocb *aiol[] = {&aio};
+ int ret, length;
+ bool read = false;
+ bool write = false;
+
+ // Send the header data
+ mtp_data_header *header = reinterpret_cast<mtp_data_header*>(data);
+ header->length = __cpu_to_le32(given_length);
+ header->type = __cpu_to_le16(2); /* data packet */
+ header->command = __cpu_to_le16(mfr.command);
+ header->transaction_id = __cpu_to_le32(mfr.transaction_id);
+
+ // Some hosts don't support header/data separation even though MTP allows it
+ // Handle by filling first packet with initial file data
+ if (TEMP_FAILURE_RETRY(pread(mfr.fd, reinterpret_cast<char*>(data) +
+ sizeof(mtp_data_header), init_read_len, offset))
+ != init_read_len) return -1;
+ file_length -= init_read_len;
+ offset += init_read_len;
+ if (writeHandle(mBulkIn, data, packet_size) == -1) return -1;
+ if (file_length == 0) return 0;
+
+ // Break down the file into pieces that fit in buffers
+ while(file_length > 0) {
+ if (read) {
+ // Wait for the previous read to finish
+ aio_suspend(aiol, 1, nullptr);
+ ret = aio_return(&aio);
+ if (ret == -1) {
+ errno = aio_error(&aio);
+ return -1;
+ }
+ if (static_cast<size_t>(ret) < aio.aio_nbytes) {
+ errno = EIO;
+ return -1;
+ }
+
+ file_length -= ret;
+ offset += ret;
+ std::swap(data, data2);
+ read = false;
+ write = true;
+ }
+
+ if (file_length > 0) {
+ length = std::min((uint64_t) MAX_FILE_CHUNK_SIZE, file_length);
+ // Queue up another read
+ aio.aio_buf = data;
+ aio.aio_offset = offset;
+ aio.aio_nbytes = length;
+ aio_read(&aio);
+ read = true;
+ }
+
+ if (write) {
+ if (writeHandle(mBulkIn, data2, ret) == -1)
+ return -1;
+ write = false;
+ }
+ }
+
+ if (given_length == MAX_MTP_FILE_SIZE && ret % packet_size == 0) {
+ // If the last packet wasn't short, send a final empty packet
+ if (writeHandle(mBulkIn, data, 0) == -1) return -1;
+ }
+
+ return 0;
+}
+
+int MtpFfsHandle::sendEvent(mtp_event me) {
+ unsigned length = me.length;
+ int ret = writeHandle(mIntr, me.data, length);
+ return static_cast<unsigned>(ret) == length ? 0 : -1;
+}
+
+} // namespace android
+
+IMtpHandle *get_ffs_handle() {
+ return new android::MtpFfsHandle();
+}
+
diff --git a/media/mtp/MtpFfsHandle.h b/media/mtp/MtpFfsHandle.h
new file mode 100644
index 0000000..9cd4dcf
--- /dev/null
+++ b/media/mtp/MtpFfsHandle.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 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 _MTP_FFS_HANDLE_H
+#define _MTP_FFS_HANDLE_H
+
+#include <android-base/unique_fd.h>
+#include <IMtpHandle.h>
+
+namespace android {
+
+class MtpFfsHandleTest;
+
+class MtpFfsHandle : public IMtpHandle {
+ friend class android::MtpFfsHandleTest;
+private:
+ int writeHandle(int fd, const void *data, int len);
+ int readHandle(int fd, void *data, int len);
+ int spliceReadHandle(int fd, int fd_out, int len);
+ bool initFunctionfs();
+ void closeConfig();
+ void closeEndpoints();
+
+ bool mPtp;
+
+ std::mutex mLock;
+
+ android::base::unique_fd mControl;
+ // "in" from the host's perspective => sink for mtp server
+ android::base::unique_fd mBulkIn;
+ // "out" from the host's perspective => source for mtp server
+ android::base::unique_fd mBulkOut;
+ android::base::unique_fd mIntr;
+
+ int mMaxWrite;
+ int mMaxRead;
+
+public:
+ int read(void *data, int len);
+ int write(const void *data, int len);
+
+ int receiveFile(mtp_file_range mfr);
+ int sendFile(mtp_file_range mfr);
+ int sendEvent(mtp_event me);
+
+ int start();
+ void close();
+
+ int configure(bool ptp);
+
+ MtpFfsHandle();
+ ~MtpFfsHandle();
+};
+
+struct mtp_data_header {
+ /* length of packet, including this header */
+ __le32 length;
+ /* container type (2 for data packet) */
+ __le16 type;
+ /* MTP command code */
+ __le16 command;
+ /* MTP transaction ID */
+ __le32 transaction_id;
+};
+
+} // namespace android
+
+#endif // _MTP_FF_HANDLE_H
+
diff --git a/media/mtp/MtpPacket.h b/media/mtp/MtpPacket.h
index 4da53bf..d47c91d 100644
--- a/media/mtp/MtpPacket.h
+++ b/media/mtp/MtpPacket.h
@@ -17,6 +17,8 @@
#ifndef _MTP_PACKET_H
#define _MTP_PACKET_H
+#include <android-base/macros.h>
+
#include "MtpTypes.h"
struct usb_device;
@@ -66,6 +68,8 @@
uint32_t getUInt32(int offset) const;
void putUInt16(int offset, uint16_t value);
void putUInt32(int offset, uint32_t value);
+
+ DISALLOW_COPY_AND_ASSIGN(MtpPacket);
};
}; // namespace android
diff --git a/media/mtp/MtpRequestPacket.cpp b/media/mtp/MtpRequestPacket.cpp
index 471967f..e0e86a9 100644
--- a/media/mtp/MtpRequestPacket.cpp
+++ b/media/mtp/MtpRequestPacket.cpp
@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <fcntl.h>
+#include "IMtpHandle.h"
#include "MtpRequestPacket.h"
#include <usbhost/usbhost.h>
@@ -36,8 +37,8 @@
}
#ifdef MTP_DEVICE
-int MtpRequestPacket::read(int fd) {
- int ret = ::read(fd, mBuffer, mBufferSize);
+int MtpRequestPacket::read(IMtpHandle *h) {
+ int ret = h->read(mBuffer, mBufferSize);
if (ret < 0) {
// file read error
return ret;
diff --git a/media/mtp/MtpRequestPacket.h b/media/mtp/MtpRequestPacket.h
index 79b798d..d1dc0ff 100644
--- a/media/mtp/MtpRequestPacket.h
+++ b/media/mtp/MtpRequestPacket.h
@@ -20,6 +20,7 @@
#include "MtpPacket.h"
#include "mtp.h"
+class IMtpHandle;
struct usb_request;
namespace android {
@@ -31,8 +32,8 @@
virtual ~MtpRequestPacket();
#ifdef MTP_DEVICE
- // fill our buffer with data from the given file descriptor
- int read(int fd);
+ // fill our buffer with data from the given usb handle
+ int read(IMtpHandle *h);
#endif
#ifdef MTP_HOST
diff --git a/media/mtp/MtpResponsePacket.cpp b/media/mtp/MtpResponsePacket.cpp
index c2b41e4..f186b37 100644
--- a/media/mtp/MtpResponsePacket.cpp
+++ b/media/mtp/MtpResponsePacket.cpp
@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <fcntl.h>
+#include "IMtpHandle.h"
#include "MtpResponsePacket.h"
#include <usbhost/usbhost.h>
@@ -35,10 +36,10 @@
}
#ifdef MTP_DEVICE
-int MtpResponsePacket::write(int fd) {
+int MtpResponsePacket::write(IMtpHandle *h) {
putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_RESPONSE);
- int ret = ::write(fd, mBuffer, mPacketSize);
+ int ret = h->write(mBuffer, mPacketSize);
return (ret < 0 ? ret : 0);
}
#endif
diff --git a/media/mtp/MtpResponsePacket.h b/media/mtp/MtpResponsePacket.h
index 592ad4a..29a72ba 100644
--- a/media/mtp/MtpResponsePacket.h
+++ b/media/mtp/MtpResponsePacket.h
@@ -20,6 +20,8 @@
#include "MtpPacket.h"
#include "mtp.h"
+class IMtpHandle;
+
namespace android {
class MtpResponsePacket : public MtpPacket {
@@ -29,8 +31,8 @@
virtual ~MtpResponsePacket();
#ifdef MTP_DEVICE
- // write our data to the given file descriptor
- int write(int fd);
+ // write our data to the given usb handle
+ int write(IMtpHandle *h);
#endif
#ifdef MTP_HOST
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index e39dcdd..2a96ac9 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -14,18 +14,18 @@
* limitations under the License.
*/
+#include <android-base/properties.h>
+#include <chrono>
+#include <cutils/properties.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
-#include <sys/ioctl.h>
#include <sys/stat.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <errno.h>
#include <sys/stat.h>
-#include <dirent.h>
-
-#include <cutils/properties.h>
#define LOG_TAG "MtpServer"
@@ -37,8 +37,6 @@
#include "MtpStorage.h"
#include "MtpStringBuffer.h"
-#include <linux/usb/f_mtp.h>
-
namespace android {
static const MtpOperationCode kSupportedOperationCodes[] = {
@@ -97,10 +95,9 @@
MTP_EVENT_DEVICE_PROP_CHANGED,
};
-MtpServer::MtpServer(int fd, MtpDatabase* database, bool ptp,
+MtpServer::MtpServer(MtpDatabase* database, bool ptp,
int fileGroup, int filePerm, int directoryPerm)
- : mFD(fd),
- mDatabase(database),
+ : mDatabase(database),
mPtp(ptp),
mFileGroup(fileGroup),
mFilePermission(filePerm),
@@ -116,6 +113,21 @@
MtpServer::~MtpServer() {
}
+IMtpHandle* MtpServer::sHandle = nullptr;
+
+int MtpServer::configure(bool usePtp) {
+ if (sHandle == nullptr) {
+ bool ffs_ok = access(FFS_MTP_EP0, W_OK) == 0;
+ sHandle = ffs_ok ? get_ffs_handle() : get_mtp_handle();
+ }
+
+ int ret = sHandle->configure(usePtp);
+ if (ret) ALOGE("Failed to configure MTP driver!");
+ else android::base::SetProperty("sys.usb.ffs.mtp.ready", "1");
+
+ return ret;
+}
+
void MtpServer::addStorage(MtpStorage* storage) {
Mutex::Autolock autoLock(mMutex);
@@ -143,24 +155,30 @@
if (storage->getStorageID() == id)
return storage;
}
- return NULL;
+ return nullptr;
}
bool MtpServer::hasStorage(MtpStorageID id) {
if (id == 0 || id == 0xFFFFFFFF)
return mStorages.size() > 0;
- return (getStorage(id) != NULL);
+ return (getStorage(id) != nullptr);
}
void MtpServer::run() {
- int fd = mFD;
+ if (!sHandle) {
+ ALOGE("MtpServer was never configured!");
+ return;
+ }
- ALOGV("MtpServer::run fd: %d\n", fd);
+ if (sHandle->start()) {
+ ALOGE("Failed to start usb driver!");
+ return;
+ }
while (1) {
- int ret = mRequest.read(fd);
+ int ret = mRequest.read(sHandle);
if (ret < 0) {
- ALOGV("request read returned %d, errno: %d", ret, errno);
+ ALOGE("request read returned %d, errno: %d", ret, errno);
if (errno == ECANCELED) {
// return to top of loop and wait for next command
continue;
@@ -171,15 +189,13 @@
MtpTransactionID transaction = mRequest.getTransactionID();
ALOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
- mRequest.dump();
-
// FIXME need to generalize this
bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
|| operation == MTP_OPERATION_SET_OBJECT_REFERENCES
|| operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
|| operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
if (dataIn) {
- int ret = mData.read(fd);
+ int ret = mData.read(sHandle);
if (ret < 0) {
ALOGE("data read returned %d, errno: %d", ret, errno);
if (errno == ECANCELED) {
@@ -189,7 +205,6 @@
break;
}
ALOGV("received data:");
- mData.dump();
} else {
mData.reset();
}
@@ -199,8 +214,7 @@
mData.setOperationCode(operation);
mData.setTransactionID(transaction);
ALOGV("sending data:");
- mData.dump();
- ret = mData.write(fd);
+ ret = mData.write(sHandle);
if (ret < 0) {
ALOGE("request write returned %d, errno: %d", ret, errno);
if (errno == ECANCELED) {
@@ -213,9 +227,8 @@
mResponse.setTransactionID(transaction);
ALOGV("sending response %04X", mResponse.getResponseCode());
- ret = mResponse.write(fd);
+ ret = mResponse.write(sHandle);
const int savedErrno = errno;
- mResponse.dump();
if (ret < 0) {
ALOGE("request write returned %d, errno: %d", ret, errno);
if (savedErrno == ECANCELED) {
@@ -240,8 +253,8 @@
if (mSessionOpen)
mDatabase->sessionEnded();
- close(fd);
- mFD = -1;
+
+ sHandle->close();
}
void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
@@ -274,8 +287,8 @@
mEvent.setEventCode(code);
mEvent.setTransactionID(mRequest.getTransactionID());
mEvent.setParameter(1, param1);
- int ret = mEvent.write(mFD);
- ALOGV("mEvent.write returned %d\n", ret);
+ if (mEvent.write(sHandle))
+ ALOGE("Mtp send event failed: %s", strerror(errno));
}
}
@@ -291,7 +304,7 @@
ObjectEdit* edit = mObjectEditList[i];
if (edit->mHandle == handle) return edit;
}
- return NULL;
+ return nullptr;
}
void MtpServer::removeEditObject(MtpObjectHandle handle) {
@@ -775,6 +788,8 @@
if (result != MTP_RESPONSE_OK)
return result;
+ auto start = std::chrono::steady_clock::now();
+
const char* filePath = (const char *)pathBuf;
mtp_file_range mfr;
mfr.fd = open(filePath, O_RDONLY);
@@ -787,8 +802,9 @@
mfr.transaction_id = mRequest.getTransactionID();
// then transfer the file
- int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
+ int ret = sHandle->sendFile(mfr);
if (ret < 0) {
+ ALOGE("Mtp send file got error %s", strerror(errno));
if (errno == ECANCELED) {
result = MTP_RESPONSE_TRANSACTION_CANCELLED;
} else {
@@ -798,7 +814,13 @@
result = MTP_RESPONSE_OK;
}
- ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
+ auto end = std::chrono::steady_clock::now();
+ std::chrono::duration<double> diff = end - start;
+ struct stat sstat;
+ fstat(mfr.fd, &sstat);
+ uint64_t finalsize = sstat.st_size;
+ ALOGV("Sent a file over MTP. Time: %f s, Size: %" PRIu64 ", Rate: %f bytes/s",
+ diff.count(), finalsize, ((double) finalsize) / diff.count());
close(mfr.fd);
return result;
}
@@ -813,7 +835,7 @@
// send data
mData.setOperationCode(mRequest.getOperationCode());
mData.setTransactionID(mRequest.getTransactionID());
- mData.writeData(mFD, thumb, thumbSize);
+ mData.writeData(sHandle, thumb, thumbSize);
free(thumb);
return MTP_RESPONSE_OK;
} else {
@@ -867,7 +889,7 @@
mResponse.setParameter(1, length);
// transfer the file
- int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
+ int ret = sHandle->sendFile(mfr);
ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
result = MTP_RESPONSE_OK;
if (ret < 0) {
@@ -995,6 +1017,8 @@
int ret, initialData;
bool isCanceled = false;
+ auto start = std::chrono::steady_clock::now();
+
if (mSendObjectHandle == kInvalidObjectHandle) {
ALOGE("Expected SendObjectInfo before SendObject");
result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
@@ -1002,7 +1026,7 @@
}
// read the header, and possibly some data
- ret = mData.read(mFD);
+ ret = mData.read(sHandle);
if (ret < MTP_CONTAINER_HEADER_SIZE) {
result = MTP_RESPONSE_GENERAL_ERROR;
goto done;
@@ -1038,19 +1062,19 @@
mfr.length = mSendObjectFileSize - initialData;
}
- ALOGV("receiving %s\n", (const char *)mSendObjectFilePath);
// transfer the file
- ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
+ ret = sHandle->receiveFile(mfr);
if ((ret < 0) && (errno == ECANCELED)) {
isCanceled = true;
}
-
- ALOGV("MTP_RECEIVE_FILE returned %d\n", ret);
}
}
+ struct stat sstat;
+ fstat(mfr.fd, &sstat);
close(mfr.fd);
if (ret < 0) {
+ ALOGE("Mtp receive file got error %s", strerror(errno));
unlink(mSendObjectFilePath);
if (isCanceled)
result = MTP_RESPONSE_TRANSACTION_CANCELLED;
@@ -1066,6 +1090,12 @@
result == MTP_RESPONSE_OK);
mSendObjectHandle = kInvalidObjectHandle;
mSendObjectFormat = 0;
+
+ auto end = std::chrono::steady_clock::now();
+ std::chrono::duration<double> diff = end - start;
+ uint64_t finalsize = sstat.st_size;
+ ALOGV("Got a file over MTP. Time: %fs, Size: %" PRIu64 ", Rate: %f bytes/s",
+ diff.count(), finalsize, ((double) finalsize) / diff.count());
return result;
}
@@ -1209,7 +1239,7 @@
ALOGV("receiving partial %s %" PRIu64 " %" PRIu32, filePath, offset, length);
// read the header, and possibly some data
- int ret = mData.read(mFD);
+ int ret = mData.read(sHandle);
if (ret < MTP_CONTAINER_HEADER_SIZE)
return MTP_RESPONSE_GENERAL_ERROR;
int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
@@ -1231,11 +1261,10 @@
mfr.length = length;
// transfer the file
- ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
+ ret = sHandle->receiveFile(mfr);
if ((ret < 0) && (errno == ECANCELED)) {
isCanceled = true;
}
- ALOGV("MTP_RECEIVE_FILE returned %d", ret);
}
}
if (ret < 0) {
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index b3a11e0..c80e6a8 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -23,8 +23,12 @@
#include "MtpEventPacket.h"
#include "mtp.h"
#include "MtpUtils.h"
+#include "IMtpHandle.h"
#include <utils/threads.h>
+#include <queue>
+#include <memory>
+#include <mutex>
namespace android {
@@ -34,9 +38,6 @@
class MtpServer {
private:
- // file descriptor for MTP kernel driver
- int mFD;
-
MtpDatabase* mDatabase;
// appear as a PTP device
@@ -56,10 +57,13 @@
MtpRequestPacket mRequest;
MtpDataPacket mData;
MtpResponsePacket mResponse;
+
MtpEventPacket mEvent;
MtpStorageList mStorages;
+ static IMtpHandle* sHandle;
+
// handle for new object, set by SendObjectInfo and used by SendObject
MtpObjectHandle mSendObjectHandle;
MtpObjectFormat mSendObjectFormat;
@@ -90,7 +94,7 @@
Vector<ObjectEdit*> mObjectEditList;
public:
- MtpServer(int fd, MtpDatabase* database, bool ptp,
+ MtpServer(MtpDatabase* database, bool ptp,
int fileGroup, int filePerm, int directoryPerm);
virtual ~MtpServer();
@@ -100,6 +104,7 @@
void addStorage(MtpStorage* storage);
void removeStorage(MtpStorage* storage);
+ static int configure(bool usePtp);
void run();
void sendObjectAdded(MtpObjectHandle handle);
diff --git a/media/mtp/tests/Android.mk b/media/mtp/tests/Android.mk
new file mode 100644
index 0000000..ace0d40
--- /dev/null
+++ b/media/mtp/tests/Android.mk
@@ -0,0 +1,51 @@
+# Build the unit tests.
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_MODULE := mtp_ffs_handle_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ MtpFfsHandle_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libcutils \
+ libmedia \
+ libmtp \
+ libutils \
+ liblog
+
+LOCAL_C_INCLUDES := \
+ frameworks/av/media/mtp \
+
+LOCAL_CFLAGS += -Werror -Wall
+
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_MODULE := async_io_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+ AsyncIO_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libcutils \
+ libmedia \
+ libmtp \
+ libutils \
+ liblog
+
+LOCAL_C_INCLUDES := \
+ frameworks/av/media/mtp \
+
+LOCAL_CFLAGS += -Werror -Wall
+
+include $(BUILD_NATIVE_TEST)
diff --git a/media/mtp/tests/AsyncIO_test.cpp b/media/mtp/tests/AsyncIO_test.cpp
new file mode 100644
index 0000000..b5f4538
--- /dev/null
+++ b/media/mtp/tests/AsyncIO_test.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2016 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 "AsyncIO_test.cpp"
+
+#include <android-base/test_utils.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <string>
+#include <unistd.h>
+#include <utils/Log.h>
+
+#include "AsyncIO.h"
+
+namespace android {
+
+constexpr int TEST_PACKET_SIZE = 512;
+constexpr int POOL_COUNT = 10;
+
+static const std::string dummyDataStr =
+ "/*\n * Copyright 2015 The Android Open Source Project\n *\n * Licensed un"
+ "der the Apache License, Version 2.0 (the \"License\");\n * you may not us"
+ "e this file except in compliance with the License.\n * You may obtain a c"
+ "opy of the License at\n *\n * http://www.apache.org/licenses/LICENSE"
+ "-2.0\n *\n * Unless required by applicable law or agreed to in writing, s"
+ "oftware\n * distributed under the License is distributed on an \"AS IS\" "
+ "BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express o"
+ "r implied.\n * Se";
+
+
+class AsyncIOTest : public ::testing::Test {
+protected:
+ TemporaryFile dummy_file;
+
+ AsyncIOTest() {}
+ ~AsyncIOTest() {}
+};
+
+TEST_F(AsyncIOTest, testRead) {
+ char buf[TEST_PACKET_SIZE + 1];
+ buf[TEST_PACKET_SIZE] = '\0';
+ EXPECT_EQ(write(dummy_file.fd, dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+ struct aiocb aio;
+ struct aiocb *aiol[] = {&aio};
+ aio.aio_fildes = dummy_file.fd;
+ aio.aio_buf = buf;
+ aio.aio_offset = 0;
+ aio.aio_nbytes = TEST_PACKET_SIZE;
+
+ EXPECT_EQ(aio_read(&aio), 0);
+ EXPECT_EQ(aio_suspend(aiol, 1, nullptr), 0);
+ EXPECT_EQ(aio_return(&aio), TEST_PACKET_SIZE);
+ EXPECT_STREQ(buf, dummyDataStr.c_str());
+}
+
+TEST_F(AsyncIOTest, testWrite) {
+ char buf[TEST_PACKET_SIZE + 1];
+ buf[TEST_PACKET_SIZE] = '\0';
+ struct aiocb aio;
+ struct aiocb *aiol[] = {&aio};
+ aio.aio_fildes = dummy_file.fd;
+ aio.aio_buf = const_cast<char*>(dummyDataStr.c_str());
+ aio.aio_offset = 0;
+ aio.aio_nbytes = TEST_PACKET_SIZE;
+
+ EXPECT_EQ(aio_write(&aio), 0);
+ EXPECT_EQ(aio_suspend(aiol, 1, nullptr), 0);
+ EXPECT_EQ(aio_return(&aio), TEST_PACKET_SIZE);
+ EXPECT_EQ(read(dummy_file.fd, buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+ EXPECT_STREQ(buf, dummyDataStr.c_str());
+}
+
+TEST_F(AsyncIOTest, testError) {
+ char buf[TEST_PACKET_SIZE + 1];
+ buf[TEST_PACKET_SIZE] = '\0';
+ struct aiocb aio;
+ struct aiocb *aiol[] = {&aio};
+ aio.aio_fildes = -1;
+ aio.aio_buf = const_cast<char*>(dummyDataStr.c_str());
+ aio.aio_offset = 0;
+ aio.aio_nbytes = TEST_PACKET_SIZE;
+
+ EXPECT_EQ(aio_write(&aio), 0);
+ EXPECT_EQ(aio_suspend(aiol, 1, nullptr), 0);
+ EXPECT_EQ(aio_return(&aio), -1);
+ EXPECT_EQ(aio_error(&aio), EBADF);
+}
+
+TEST_F(AsyncIOTest, testSpliceRead) {
+ char buf[TEST_PACKET_SIZE + 1];
+ buf[TEST_PACKET_SIZE] = '\0';
+ int pipeFd[2];
+ EXPECT_EQ(pipe(pipeFd), 0);
+ EXPECT_EQ(write(dummy_file.fd, dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+ struct aiocb aio;
+ struct aiocb *aiol[] = {&aio};
+ aio.aio_fildes = dummy_file.fd;
+ aio.aio_sink = pipeFd[1];
+ aio.aio_offset = 0;
+ aio.aio_nbytes = TEST_PACKET_SIZE;
+
+ EXPECT_EQ(aio_splice_read(&aio), 0);
+ EXPECT_EQ(aio_suspend(aiol, 1, nullptr), 0);
+ EXPECT_EQ(aio_return(&aio), TEST_PACKET_SIZE);
+
+ EXPECT_EQ(read(pipeFd[0], buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+ EXPECT_STREQ(buf, dummyDataStr.c_str());
+}
+
+TEST_F(AsyncIOTest, testSpliceWrite) {
+ char buf[TEST_PACKET_SIZE + 1];
+ buf[TEST_PACKET_SIZE] = '\0';
+ int pipeFd[2];
+ EXPECT_EQ(pipe(pipeFd), 0);
+ EXPECT_EQ(write(pipeFd[1], dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+ struct aiocb aio;
+ struct aiocb *aiol[] = {&aio};
+ aio.aio_fildes = pipeFd[0];
+ aio.aio_sink = dummy_file.fd;
+ aio.aio_offset = 0;
+ aio.aio_nbytes = TEST_PACKET_SIZE;
+
+ EXPECT_EQ(aio_splice_write(&aio), 0);
+ EXPECT_EQ(aio_suspend(aiol, 1, nullptr), 0);
+ EXPECT_EQ(aio_return(&aio), TEST_PACKET_SIZE);
+ EXPECT_EQ(read(dummy_file.fd, buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+ EXPECT_STREQ(buf, dummyDataStr.c_str());
+}
+
+TEST_F(AsyncIOTest, testPoolWrite) {
+ aio_pool_write_init();
+ char buf[TEST_PACKET_SIZE * POOL_COUNT + 1];
+ buf[TEST_PACKET_SIZE * POOL_COUNT] = '\0';
+
+ for (int i = 0; i < POOL_COUNT; i++) {
+ struct aiocb *aiop = new struct aiocb;
+ aiop->aio_fildes = dummy_file.fd;
+ aiop->aio_pool_buf = std::unique_ptr<char[]>(new char[TEST_PACKET_SIZE]);
+ memcpy(aiop->aio_pool_buf.get(), dummyDataStr.c_str(), TEST_PACKET_SIZE);
+ aiop->aio_offset = i * TEST_PACKET_SIZE;
+ aiop->aio_nbytes = TEST_PACKET_SIZE;
+ EXPECT_EQ(aio_pool_write(aiop), 0);
+ }
+ aio_pool_end();
+ EXPECT_EQ(read(dummy_file.fd, buf, TEST_PACKET_SIZE * POOL_COUNT), TEST_PACKET_SIZE * POOL_COUNT);
+
+ std::stringstream ss;
+ for (int i = 0; i < POOL_COUNT; i++)
+ ss << dummyDataStr;
+
+ EXPECT_STREQ(buf, ss.str().c_str());
+}
+
+TEST_F(AsyncIOTest, testSplicePoolWrite) {
+ aio_pool_splice_init();
+ char buf[TEST_PACKET_SIZE * POOL_COUNT + 1];
+ buf[TEST_PACKET_SIZE * POOL_COUNT] = '\0';
+
+ for (int i = 0; i < POOL_COUNT; i++) {
+ int pipeFd[2];
+ EXPECT_EQ(pipe(pipeFd), 0);
+ EXPECT_EQ(write(pipeFd[1], dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+ struct aiocb *aiop = new struct aiocb;
+ aiop->aio_fildes = pipeFd[0];
+ aiop->aio_sink = dummy_file.fd;
+ aiop->aio_offset = i * TEST_PACKET_SIZE;
+ aiop->aio_nbytes = TEST_PACKET_SIZE;
+ EXPECT_EQ(aio_pool_write(aiop), 0);
+ }
+ aio_pool_end();
+ EXPECT_EQ(read(dummy_file.fd, buf, TEST_PACKET_SIZE * POOL_COUNT), TEST_PACKET_SIZE * POOL_COUNT);
+
+ std::stringstream ss;
+ for (int i = 0; i < POOL_COUNT; i++)
+ ss << dummyDataStr;
+
+ EXPECT_STREQ(buf, ss.str().c_str());
+}
+
+} // namespace android
diff --git a/media/mtp/tests/MtpFfsHandle_test.cpp b/media/mtp/tests/MtpFfsHandle_test.cpp
new file mode 100644
index 0000000..b511041
--- /dev/null
+++ b/media/mtp/tests/MtpFfsHandle_test.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2016 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 "MtpFfsHandle_test.cpp"
+
+#include <android-base/unique_fd.h>
+#include <android-base/test_utils.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <memory>
+#include <string>
+#include <unistd.h>
+#include <utils/Log.h>
+
+#include "MtpFfsHandle.h"
+
+namespace android {
+
+constexpr int TEST_PACKET_SIZE = 512;
+constexpr int SMALL_MULT = 30;
+constexpr int MED_MULT = 510;
+
+static const std::string dummyDataStr =
+ "/*\n * Copyright 2015 The Android Open Source Project\n *\n * Licensed un"
+ "der the Apache License, Version 2.0 (the \"License\");\n * you may not us"
+ "e this file except in compliance with the License.\n * You may obtain a c"
+ "opy of the License at\n *\n * http://www.apache.org/licenses/LICENSE"
+ "-2.0\n *\n * Unless required by applicable law or agreed to in writing, s"
+ "oftware\n * distributed under the License is distributed on an \"AS IS\" "
+ "BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express o"
+ "r implied.\n * Se";
+
+class MtpFfsHandleTest : public ::testing::Test {
+protected:
+ std::unique_ptr<IMtpHandle> handle;
+
+ // Pipes for reading endpoint data
+ android::base::unique_fd bulk_in;
+ android::base::unique_fd bulk_out;
+ android::base::unique_fd intr;
+
+ TemporaryFile dummy_file;
+
+ MtpFfsHandleTest() {
+ int fd[2];
+ handle = std::unique_ptr<IMtpHandle>(get_ffs_handle());
+ MtpFfsHandle *ffs_handle = static_cast<MtpFfsHandle*>(handle.get());
+ EXPECT_TRUE(ffs_handle != NULL);
+
+ EXPECT_EQ(pipe(fd), 0);
+ EXPECT_EQ(fcntl(fd[0], F_SETPIPE_SZ, 1048576), 1048576);
+ bulk_in.reset(fd[0]);
+ ffs_handle->mBulkIn.reset(fd[1]);
+
+ EXPECT_EQ(pipe(fd), 0);
+ EXPECT_EQ(fcntl(fd[0], F_SETPIPE_SZ, 1048576), 1048576);
+ bulk_out.reset(fd[1]);
+ ffs_handle->mBulkOut.reset(fd[0]);
+
+ EXPECT_EQ(pipe(fd), 0);
+ intr.reset(fd[0]);
+ ffs_handle->mIntr.reset(fd[1]);
+ }
+
+ ~MtpFfsHandleTest() {}
+};
+
+TEST_F(MtpFfsHandleTest, testRead) {
+ EXPECT_EQ(write(bulk_out, dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+ char buf[TEST_PACKET_SIZE + 1];
+ buf[TEST_PACKET_SIZE] = '\0';
+ EXPECT_EQ(handle->read(buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+ EXPECT_STREQ(buf, dummyDataStr.c_str());
+}
+
+TEST_F(MtpFfsHandleTest, testWrite) {
+ char buf[TEST_PACKET_SIZE + 1];
+ buf[TEST_PACKET_SIZE] = '\0';
+ EXPECT_EQ(handle->write(dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+ EXPECT_EQ(read(bulk_in, buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+ EXPECT_STREQ(buf, dummyDataStr.c_str());
+}
+
+TEST_F(MtpFfsHandleTest, testReceiveFileSmall) {
+ std::stringstream ss;
+ mtp_file_range mfr;
+ int size = TEST_PACKET_SIZE * SMALL_MULT;
+ char buf[size + 1];
+ buf[size] = '\0';
+
+ mfr.length = size;
+ mfr.fd = dummy_file.fd;
+ for (int i = 0; i < SMALL_MULT; i++)
+ ss << dummyDataStr;
+
+ EXPECT_EQ(write(bulk_out, ss.str().c_str(), size), size);
+ EXPECT_EQ(handle->receiveFile(mfr), 0);
+
+ EXPECT_EQ(read(dummy_file.fd, buf, size), size);
+
+ EXPECT_STREQ(buf, ss.str().c_str());
+}
+
+TEST_F(MtpFfsHandleTest, testReceiveFileMed) {
+ std::stringstream ss;
+ mtp_file_range mfr;
+ int size = TEST_PACKET_SIZE * MED_MULT;
+ char buf[size + 1];
+ buf[size] = '\0';
+
+ mfr.length = size;
+ mfr.fd = dummy_file.fd;
+ for (int i = 0; i < MED_MULT; i++)
+ ss << dummyDataStr;
+
+ EXPECT_EQ(write(bulk_out, ss.str().c_str(), size), size);
+ EXPECT_EQ(handle->receiveFile(mfr), 0);
+
+ EXPECT_EQ(read(dummy_file.fd, buf, size), size);
+
+ EXPECT_STREQ(buf, ss.str().c_str());
+}
+
+TEST_F(MtpFfsHandleTest, testSendFileSmall) {
+ std::stringstream ss;
+ mtp_file_range mfr;
+ mfr.command = 42;
+ mfr.transaction_id = 1337;
+ int size = TEST_PACKET_SIZE * SMALL_MULT;
+ char buf[size + sizeof(mtp_data_header) + 1];
+ buf[size + sizeof(mtp_data_header)] = '\0';
+
+ mfr.length = size;
+ mfr.fd = dummy_file.fd;
+ for (int i = 0; i < SMALL_MULT; i++)
+ ss << dummyDataStr;
+
+ EXPECT_EQ(write(dummy_file.fd, ss.str().c_str(), size), size);
+ EXPECT_EQ(handle->sendFile(mfr), 0);
+
+ EXPECT_EQ(read(bulk_in, buf, size + sizeof(mtp_data_header)),
+ static_cast<long>(size + sizeof(mtp_data_header)));
+
+ struct mtp_data_header *header = reinterpret_cast<struct mtp_data_header*>(buf);
+ EXPECT_STREQ(buf + sizeof(mtp_data_header), ss.str().c_str());
+ EXPECT_EQ(header->length, static_cast<unsigned int>(size + sizeof(mtp_data_header)));
+ EXPECT_EQ(header->type, static_cast<unsigned int>(2));
+ EXPECT_EQ(header->command, static_cast<unsigned int>(42));
+ EXPECT_EQ(header->transaction_id, static_cast<unsigned int>(1337));
+}
+
+TEST_F(MtpFfsHandleTest, testSendFileMed) {
+ std::stringstream ss;
+ mtp_file_range mfr;
+ mfr.command = 42;
+ mfr.transaction_id = 1337;
+ int size = TEST_PACKET_SIZE * MED_MULT;
+ char buf[size + sizeof(mtp_data_header) + 1];
+ buf[size + sizeof(mtp_data_header)] = '\0';
+
+ mfr.length = size;
+ mfr.fd = dummy_file.fd;
+ for (int i = 0; i < MED_MULT; i++)
+ ss << dummyDataStr;
+
+ EXPECT_EQ(write(dummy_file.fd, ss.str().c_str(), size), size);
+ EXPECT_EQ(handle->sendFile(mfr), 0);
+
+ EXPECT_EQ(read(bulk_in, buf, size + sizeof(mtp_data_header)),
+ static_cast<long>(size + sizeof(mtp_data_header)));
+
+ struct mtp_data_header *header = reinterpret_cast<struct mtp_data_header*>(buf);
+ EXPECT_STREQ(buf + sizeof(mtp_data_header), ss.str().c_str());
+ EXPECT_EQ(header->length, static_cast<unsigned int>(size + sizeof(mtp_data_header)));
+ EXPECT_EQ(header->type, static_cast<unsigned int>(2));
+ EXPECT_EQ(header->command, static_cast<unsigned int>(42));
+ EXPECT_EQ(header->transaction_id, static_cast<unsigned int>(1337));
+}
+
+TEST_F(MtpFfsHandleTest, testSendEvent) {
+ struct mtp_event event;
+ event.length = TEST_PACKET_SIZE;
+ event.data = const_cast<char*>(dummyDataStr.c_str());
+ char buf[TEST_PACKET_SIZE + 1];
+ buf[TEST_PACKET_SIZE] = '\0';
+
+ handle->sendEvent(event);
+ read(intr, buf, TEST_PACKET_SIZE);
+ EXPECT_STREQ(buf, dummyDataStr.c_str());
+}
+
+} // namespace android
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index 89f2d9c..805eafb 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -86,7 +86,6 @@
jobject service = NULL;
if (env == NULL) {
ALOGE("setDataSource(path) must be called from Java thread");
- env->ExceptionClear();
return AMEDIA_ERROR_UNSUPPORTED;
}
diff --git a/media/utils/BatteryNotifier.cpp b/media/utils/BatteryNotifier.cpp
index 7a7321f..09bc042 100644
--- a/media/utils/BatteryNotifier.cpp
+++ b/media/utils/BatteryNotifier.cpp
@@ -38,7 +38,7 @@
}
}
-void BatteryNotifier::noteStartVideo(int uid) {
+void BatteryNotifier::noteStartVideo(uid_t uid) {
Mutex::Autolock _l(mLock);
sp<IBatteryStats> batteryService = getBatteryService_l();
if (mVideoRefCounts[uid] == 0 && batteryService != nullptr) {
@@ -47,7 +47,7 @@
mVideoRefCounts[uid]++;
}
-void BatteryNotifier::noteStopVideo(int uid) {
+void BatteryNotifier::noteStopVideo(uid_t uid) {
Mutex::Autolock _l(mLock);
if (mVideoRefCounts.find(uid) == mVideoRefCounts.end()) {
ALOGW("%s: video refcount is broken for uid(%d).", __FUNCTION__, (int)uid);
@@ -74,7 +74,7 @@
}
}
-void BatteryNotifier::noteStartAudio(int uid) {
+void BatteryNotifier::noteStartAudio(uid_t uid) {
Mutex::Autolock _l(mLock);
sp<IBatteryStats> batteryService = getBatteryService_l();
if (mAudioRefCounts[uid] == 0 && batteryService != nullptr) {
@@ -83,7 +83,7 @@
mAudioRefCounts[uid]++;
}
-void BatteryNotifier::noteStopAudio(int uid) {
+void BatteryNotifier::noteStopAudio(uid_t uid) {
Mutex::Autolock _l(mLock);
if (mAudioRefCounts.find(uid) == mAudioRefCounts.end()) {
ALOGW("%s: audio refcount is broken for uid(%d).", __FUNCTION__, (int)uid);
@@ -110,11 +110,11 @@
}
}
-void BatteryNotifier::noteFlashlightOn(const String8& id, int uid) {
+void BatteryNotifier::noteFlashlightOn(const String8& id, uid_t uid) {
Mutex::Autolock _l(mLock);
sp<IBatteryStats> batteryService = getBatteryService_l();
- std::pair<String8, int> k = std::make_pair(id, uid);
+ std::pair<String8, uid_t> k = std::make_pair(id, uid);
if (!mFlashlightState[k]) {
mFlashlightState[k] = true;
if (batteryService != nullptr) {
@@ -123,11 +123,11 @@
}
}
-void BatteryNotifier::noteFlashlightOff(const String8& id, int uid) {
+void BatteryNotifier::noteFlashlightOff(const String8& id, uid_t uid) {
Mutex::Autolock _l(mLock);
sp<IBatteryStats> batteryService = getBatteryService_l();
- std::pair<String8, int> k = std::make_pair(id, uid);
+ std::pair<String8, uid_t> k = std::make_pair(id, uid);
if (mFlashlightState[k]) {
mFlashlightState[k] = false;
if (batteryService != nullptr) {
@@ -145,10 +145,10 @@
}
}
-void BatteryNotifier::noteStartCamera(const String8& id, int uid) {
+void BatteryNotifier::noteStartCamera(const String8& id, uid_t uid) {
Mutex::Autolock _l(mLock);
sp<IBatteryStats> batteryService = getBatteryService_l();
- std::pair<String8, int> k = std::make_pair(id, uid);
+ std::pair<String8, uid_t> k = std::make_pair(id, uid);
if (!mCameraState[k]) {
mCameraState[k] = true;
if (batteryService != nullptr) {
@@ -157,10 +157,10 @@
}
}
-void BatteryNotifier::noteStopCamera(const String8& id, int uid) {
+void BatteryNotifier::noteStopCamera(const String8& id, uid_t uid) {
Mutex::Autolock _l(mLock);
sp<IBatteryStats> batteryService = getBatteryService_l();
- std::pair<String8, int> k = std::make_pair(id, uid);
+ std::pair<String8, uid_t> k = std::make_pair(id, uid);
if (mCameraState[k]) {
mCameraState[k] = false;
if (batteryService != nullptr) {
@@ -211,7 +211,7 @@
// Notify start now if mediaserver or audioserver is already started.
// 1) mediaserver and audioserver is started before batterystats service
// 2) batterystats server may have crashed.
- std::map<int, int>::iterator it = mVideoRefCounts.begin();
+ std::map<uid_t, int>::iterator it = mVideoRefCounts.begin();
for (; it != mVideoRefCounts.end(); ++it) {
mBatteryStatService->noteStartVideo(it->first);
}
diff --git a/media/utils/include/mediautils/BatteryNotifier.h b/media/utils/include/mediautils/BatteryNotifier.h
index 2ba4c76..a4e42ad 100644
--- a/media/utils/include/mediautils/BatteryNotifier.h
+++ b/media/utils/include/mediautils/BatteryNotifier.h
@@ -37,17 +37,17 @@
public:
~BatteryNotifier();
- void noteStartVideo(int uid);
- void noteStopVideo(int uid);
+ void noteStartVideo(uid_t uid);
+ void noteStopVideo(uid_t uid);
void noteResetVideo();
- void noteStartAudio(int uid);
- void noteStopAudio(int uid);
+ void noteStartAudio(uid_t uid);
+ void noteStopAudio(uid_t uid);
void noteResetAudio();
- void noteFlashlightOn(const String8& id, int uid);
- void noteFlashlightOff(const String8& id, int uid);
+ void noteFlashlightOn(const String8& id, uid_t uid);
+ void noteFlashlightOff(const String8& id, uid_t uid);
void noteResetFlashlight();
- void noteStartCamera(const String8& id, int uid);
- void noteStopCamera(const String8& id, int uid);
+ void noteStartCamera(const String8& id, uid_t uid);
+ void noteStopCamera(const String8& id, uid_t uid);
void noteResetCamera();
private:
@@ -58,10 +58,10 @@
};
Mutex mLock;
- std::map<int, int> mVideoRefCounts;
- std::map<int, int> mAudioRefCounts;
- std::map<std::pair<String8, int>, bool> mFlashlightState;
- std::map<std::pair<String8, int>, bool> mCameraState;
+ std::map<uid_t, int> mVideoRefCounts;
+ std::map<uid_t, int> mAudioRefCounts;
+ std::map<std::pair<String8, uid_t>, bool> mFlashlightState;
+ std::map<std::pair<String8, uid_t>, bool> mCameraState;
sp<IBatteryStats> mBatteryStatService;
sp<DeathNotifier> mDeathNotifier;
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index e2a93ad..51d785a 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -57,11 +57,11 @@
libserviceutility \
libsonic \
libmediautils \
- libmemunreachable
+ libmemunreachable \
+ libmedia_helper
LOCAL_STATIC_LIBRARIES := \
libcpustats \
- libmedia_helper
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 8917c26..d08309b 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2609,6 +2609,7 @@
audio_io_handle_t io,
audio_session_t sessionId,
const String16& opPackageName,
+ pid_t pid,
status_t *status,
int *id,
int *enabled)
@@ -2617,7 +2618,15 @@
sp<EffectHandle> handle;
effect_descriptor_t desc;
- pid_t pid = IPCThreadState::self()->getCallingPid();
+ const uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ if (pid == -1 || !isTrustedCallingUid(callingUid)) {
+ const pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ ALOGW_IF(pid != -1 && pid != callingPid,
+ "%s uid %d pid %d tried to pass itself off as pid %d",
+ __func__, callingUid, callingPid, pid);
+ pid = callingPid;
+ }
+
ALOGV("createEffect pid %d, effectClient %p, priority %d, sessionId %d, io %d, factory %p",
pid, effectClient.get(), priority, sessionId, io, mEffectsFactoryHal.get());
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index b046ab5..08d94c4 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -226,6 +226,7 @@
audio_io_handle_t io,
audio_session_t sessionId,
const String16& opPackageName,
+ pid_t pid,
status_t *status /*non-NULL*/,
int *id,
int *enabled);
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
index 9f3488d..a1420ff 100644
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -23,10 +23,10 @@
libaudioclient \
libhardware_legacy \
libserviceutility \
- libaudiopolicymanager
+ libaudiopolicymanager \
+ libmedia_helper
LOCAL_STATIC_LIBRARIES := \
- libmedia_helper \
libaudiopolicycomponents
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
@@ -76,7 +76,7 @@
LOCAL_STATIC_LIBRARIES := \
libaudiopolicycomponents
-LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper
+LOCAL_SHARED_LIBRARIES += libmedia_helper
ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1)
LOCAL_STATIC_LIBRARIES += libxml2
diff --git a/services/audiopolicy/engineconfigurable/Android.mk b/services/audiopolicy/engineconfigurable/Android.mk
index 08c677e..341357c 100644
--- a/services/audiopolicy/engineconfigurable/Android.mk
+++ b/services/audiopolicy/engineconfigurable/Android.mk
@@ -40,8 +40,6 @@
LOCAL_MODULE := libaudiopolicyengineconfigurable
LOCAL_MODULE_TAGS := optional
-LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper
-
LOCAL_STATIC_LIBRARIES := \
libaudiopolicypfwwrapper \
libaudiopolicycomponents \
@@ -53,7 +51,8 @@
libutils \
liblog \
libaudioutils \
- libparameter
+ libparameter \
+ libmedia_helper
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audiopolicy/engineconfigurable/wrapper/Android.mk b/services/audiopolicy/engineconfigurable/wrapper/Android.mk
index d031bc3..066ee0d 100644
--- a/services/audiopolicy/engineconfigurable/wrapper/Android.mk
+++ b/services/audiopolicy/engineconfigurable/wrapper/Android.mk
@@ -14,10 +14,9 @@
LOCAL_SRC_FILES:= ParameterManagerWrapper.cpp
-LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper
-
LOCAL_SHARED_LIBRARIES := \
libparameter \
+ libmedia_helper
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk
index c1bb3fb..47dc545 100644
--- a/services/audiopolicy/enginedefault/Android.mk
+++ b/services/audiopolicy/enginedefault/Android.mk
@@ -34,8 +34,6 @@
LOCAL_MODULE := libaudiopolicyenginedefault
LOCAL_MODULE_TAGS := optional
-LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper
-
LOCAL_STATIC_LIBRARIES := \
libaudiopolicycomponents \
libxml2
@@ -44,5 +42,6 @@
liblog \
libcutils \
libutils \
+ libmedia_helper
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index c2b282e..2f25020 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -341,7 +341,7 @@
DeviceVector deviceList;
uint32_t muteWaitMs = 0;
- if(!hasPrimaryOutput()) {
+ if(!hasPrimaryOutput() || mPrimaryOutput->device() == AUDIO_DEVICE_OUT_STUB) {
return muteWaitMs;
}
audio_devices_t txDevice = getDeviceAndMixForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION);
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 6586bea..654465d 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -27,6 +27,7 @@
#include <utils/Vector.h>
#include <utils/SortedVector.h>
#include <cutils/config_utils.h>
+#include <binder/IPCThreadState.h>
#include "AudioPolicyEffects.h"
#include "ServiceUtilities.h"
@@ -105,6 +106,7 @@
ALOGV("addInputEffects(): input: %d, refCount: %d", input, sessionDesc->mRefCount);
if (sessionDesc->mRefCount == 1) {
+ int64_t token = IPCThreadState::self()->clearCallingIdentity();
Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects;
for (size_t i = 0; i < effects.size(); i++) {
EffectDesc *effect = effects[i];
@@ -125,6 +127,7 @@
sessionDesc->mEffects.add(fx);
}
sessionDesc->setProcessorEnabled(true);
+ IPCThreadState::self()->restoreCallingIdentity(token);
}
return status;
}
@@ -251,6 +254,8 @@
ALOGV("addOutputSessionEffects(): session: %d, refCount: %d",
audioSession, procDesc->mRefCount);
if (procDesc->mRefCount == 1) {
+ // make sure effects are associated to audio server even if we are executing a binder call
+ int64_t token = IPCThreadState::self()->clearCallingIdentity();
Vector <EffectDesc *> effects = mOutputStreams.valueAt(index)->mEffects;
for (size_t i = 0; i < effects.size(); i++) {
EffectDesc *effect = effects[i];
@@ -269,6 +274,7 @@
}
procDesc->setProcessorEnabled(true);
+ IPCThreadState::self()->restoreCallingIdentity(token);
}
return status;
}
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 7feed6b..ef2e8d9 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -67,7 +67,8 @@
libhardware \
libcamera_metadata \
libjpeg \
- libmemunreachable
+ libmemunreachable \
+ android.hardware.camera.common@1.0
LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder libcamera_client
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 85faac6..1d9ccb1 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -69,7 +69,11 @@
namespace android {
using binder::Status;
-using namespace hardware;
+using hardware::ICamera;
+using hardware::ICameraClient;
+using hardware::ICameraServiceListener;
+using hardware::camera::common::V1_0::CameraDeviceStatus;
+using hardware::camera::common::V1_0::TorchModeStatus;
// ----------------------------------------------------------------------------
// Logging support -- this is for debugging only
@@ -103,9 +107,24 @@
int new_status) {
sp<CameraService> cs = const_cast<CameraService*>(
static_cast<const CameraService*>(callbacks));
+ String8 id = String8::format("%d", camera_id);
- cs->onDeviceStatusChanged(camera_id,
- static_cast<camera_device_status_t>(new_status));
+ CameraDeviceStatus newStatus{CameraDeviceStatus::NOT_PRESENT};
+ switch (new_status) {
+ case CAMERA_DEVICE_STATUS_NOT_PRESENT:
+ newStatus = CameraDeviceStatus::NOT_PRESENT;
+ break;
+ case CAMERA_DEVICE_STATUS_PRESENT:
+ newStatus = CameraDeviceStatus::PRESENT;
+ break;
+ case CAMERA_DEVICE_STATUS_ENUMERATING:
+ newStatus = CameraDeviceStatus::ENUMERATING;
+ break;
+ default:
+ ALOGW("Unknown device status change to %d", new_status);
+ break;
+ }
+ cs->onDeviceStatusChanged(id, newStatus);
}
static void torch_mode_status_change(
@@ -119,16 +138,16 @@
sp<CameraService> cs = const_cast<CameraService*>(
static_cast<const CameraService*>(callbacks));
- int32_t status;
+ TorchModeStatus status;
switch (new_status) {
case TORCH_MODE_STATUS_NOT_AVAILABLE:
- status = ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE;
+ status = TorchModeStatus::NOT_AVAILABLE;
break;
case TORCH_MODE_STATUS_AVAILABLE_OFF:
- status = ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF;
+ status = TorchModeStatus::AVAILABLE_OFF;
break;
case TORCH_MODE_STATUS_AVAILABLE_ON:
- status = ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON;
+ status = TorchModeStatus::AVAILABLE_ON;
break;
default:
ALOGE("Unknown torch status %d", new_status);
@@ -261,7 +280,7 @@
if (mFlashlight->hasFlashUnit(cameraId)) {
mTorchStatusMap.add(cameraId,
- ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
+ TorchModeStatus::AVAILABLE_OFF);
}
}
@@ -276,7 +295,10 @@
sp<ICameraServiceProxy> proxyBinder = nullptr;
#ifndef __BRILLO__
sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("media.camera.proxy"));
+ // Use checkService because cameraserver normally starts before the
+ // system server and the proxy service. So the long timeout that getService
+ // has before giving up is inappropriate.
+ sp<IBinder> binder = sm->checkService(String16("media.camera.proxy"));
if (binder != nullptr) {
proxyBinder = interface_cast<ICameraServiceProxy>(binder);
}
@@ -299,27 +321,28 @@
gCameraService = nullptr;
}
-void CameraService::onDeviceStatusChanged(int cameraId,
- camera_device_status_t newStatus) {
- ALOGI("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__,
- cameraId, newStatus);
+void CameraService::onDeviceStatusChanged(const String8& id,
+ CameraDeviceStatus newHalStatus) {
+ ALOGI("%s: Status changed for cameraId=%s, newStatus=%d", __FUNCTION__,
+ id.string(), newHalStatus);
- String8 id = String8::format("%d", cameraId);
+ StatusInternal newStatus = mapToInternal(newHalStatus);
+
std::shared_ptr<CameraState> state = getCameraState(id);
if (state == nullptr) {
- ALOGE("%s: Bad camera ID %d", __FUNCTION__, cameraId);
+ ALOGE("%s: Bad camera ID %s", __FUNCTION__, id.string());
return;
}
- int32_t oldStatus = state->getStatus();
+ StatusInternal oldStatus = state->getStatus();
- if (oldStatus == static_cast<int32_t>(newStatus)) {
+ if (oldStatus == newStatus) {
ALOGE("%s: State transition to the same status %#x not allowed", __FUNCTION__, newStatus);
return;
}
- if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) {
+ if (newStatus == StatusInternal::NOT_PRESENT) {
logDeviceRemoved(id, String8::format("Device status changed from %d to %d", oldStatus,
newStatus));
sp<BasicClient> clientToDisconnect;
@@ -329,7 +352,7 @@
// 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);
+ updateStatus(StatusInternal::NOT_PRESENT, id);
// Remove cached shim parameters
state->setShimParams(CameraParameters());
@@ -355,27 +378,27 @@
}
} else {
- if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT) {
+ if (oldStatus == StatusInternal::NOT_PRESENT) {
logDeviceAdded(id, String8::format("Device status changed from %d to %d", oldStatus,
newStatus));
}
- updateStatus(static_cast<int32_t>(newStatus), id);
+ updateStatus(newStatus, id);
}
}
void CameraService::onTorchStatusChanged(const String8& cameraId,
- int32_t newStatus) {
+ TorchModeStatus newStatus) {
Mutex::Autolock al(mTorchStatusMutex);
onTorchStatusChangedLocked(cameraId, newStatus);
}
void CameraService::onTorchStatusChangedLocked(const String8& cameraId,
- int32_t newStatus) {
+ TorchModeStatus newStatus) {
ALOGI("%s: Torch status changed for cameraId=%s, newStatus=%d",
__FUNCTION__, cameraId.string(), newStatus);
- int32_t status;
+ TorchModeStatus status;
status_t res = getTorchStatusLocked(cameraId, &status);
if (res) {
ALOGE("%s: cannot get torch status of camera %s: %s (%d)",
@@ -403,16 +426,16 @@
BatteryNotifier& notifier(BatteryNotifier::getInstance());
if (oldUid != newUid) {
// If the UID has changed, log the status and update current UID in mTorchUidMap
- if (status == ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON) {
+ if (status == TorchModeStatus::AVAILABLE_ON) {
notifier.noteFlashlightOff(cameraId, oldUid);
}
- if (newStatus == ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON) {
+ if (newStatus == TorchModeStatus::AVAILABLE_ON) {
notifier.noteFlashlightOn(cameraId, newUid);
}
iter->second.second = newUid;
} else {
// If the UID has not changed, log the status
- if (newStatus == ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON) {
+ if (newStatus == TorchModeStatus::AVAILABLE_ON) {
notifier.noteFlashlightOn(cameraId, oldUid);
} else {
notifier.noteFlashlightOff(cameraId, oldUid);
@@ -424,7 +447,7 @@
{
Mutex::Autolock lock(mStatusListenerLock);
for (auto& i : mListenerList) {
- i->onTorchStatusChanged(newStatus, String16{cameraId});
+ i->onTorchStatusChanged(mapToInterface(newStatus), String16{cameraId});
}
}
}
@@ -487,101 +510,8 @@
return ret;
}
-Status CameraService::generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo) {
- ATRACE_CALL();
-
- Status ret = Status::ok();
-
- struct CameraInfo info;
- if (!(ret = getCameraInfo(cameraId, &info)).isOk()) {
- return ret;
- }
-
- CameraMetadata shimInfo;
- int32_t orientation = static_cast<int32_t>(info.orientation);
- status_t rc;
- if ((rc = shimInfo.update(ANDROID_SENSOR_ORIENTATION, &orientation, 1)) != OK) {
- return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
- "Error updating metadata: %d (%s)", rc, strerror(-rc));
- }
-
- uint8_t facing = (info.facing == CAMERA_FACING_FRONT) ?
- ANDROID_LENS_FACING_FRONT : ANDROID_LENS_FACING_BACK;
- if ((rc = shimInfo.update(ANDROID_LENS_FACING, &facing, 1)) != OK) {
- return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
- "Error updating metadata: %d (%s)", rc, strerror(-rc));
- }
-
- CameraParameters shimParams;
- if (!(ret = getLegacyParametersLazy(cameraId, /*out*/&shimParams)).isOk()) {
- // Error logged by callee
- return ret;
- }
-
- Vector<Size> sizes;
- Vector<Size> jpegSizes;
- Vector<int32_t> formats;
- {
- shimParams.getSupportedPreviewSizes(/*out*/sizes);
- shimParams.getSupportedPreviewFormats(/*out*/formats);
- shimParams.getSupportedPictureSizes(/*out*/jpegSizes);
- }
-
- // Always include IMPLEMENTATION_DEFINED
- formats.add(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
-
- const size_t INTS_PER_CONFIG = 4;
-
- // Build available stream configurations metadata
- size_t streamConfigSize = (sizes.size() * formats.size() + jpegSizes.size()) * INTS_PER_CONFIG;
-
- Vector<int32_t> streamConfigs;
- streamConfigs.setCapacity(streamConfigSize);
-
- for (size_t i = 0; i < formats.size(); ++i) {
- for (size_t j = 0; j < sizes.size(); ++j) {
- streamConfigs.add(formats[i]);
- streamConfigs.add(sizes[j].width);
- streamConfigs.add(sizes[j].height);
- streamConfigs.add(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
- }
- }
-
- for (size_t i = 0; i < jpegSizes.size(); ++i) {
- streamConfigs.add(HAL_PIXEL_FORMAT_BLOB);
- streamConfigs.add(jpegSizes[i].width);
- streamConfigs.add(jpegSizes[i].height);
- streamConfigs.add(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
- }
-
- if ((rc = shimInfo.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
- streamConfigs.array(), streamConfigSize)) != OK) {
- return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
- "Error updating metadata: %d (%s)", rc, strerror(-rc));
- }
-
- int64_t fakeMinFrames[0];
- // TODO: Fixme, don't fake min frame durations.
- if ((rc = shimInfo.update(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
- fakeMinFrames, 0)) != OK) {
- return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
- "Error updating metadata: %d (%s)", rc, strerror(-rc));
- }
-
- int64_t fakeStalls[0];
- // TODO: Fixme, don't fake stall durations.
- if ((rc = shimInfo.update(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
- fakeStalls, 0)) != OK) {
- return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
- "Error updating metadata: %d (%s)", rc, strerror(-rc));
- }
-
- *cameraInfo = shimInfo;
- return ret;
-}
-
-Status CameraService::getCameraCharacteristics(int cameraId,
- CameraMetadata* cameraInfo) {
+Status CameraService::getCameraCharacteristics(const String16& id,
+ CameraMetadata* cameraInfo) {
ATRACE_CALL();
if (!cameraInfo) {
ALOGE("%s: cameraInfo is NULL", __FUNCTION__);
@@ -594,6 +524,8 @@
"Camera subsystem is not available");;
}
+ int cameraId = cameraIdToInt(String8(id));
+
if (cameraId < 0 || cameraId >= mNumberOfCameras) {
ALOGE("%s: Invalid camera id: %d", __FUNCTION__, cameraId);
return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
@@ -604,26 +536,14 @@
Status ret;
if (mModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_0 ||
getDeviceVersion(cameraId, &facing) < CAMERA_DEVICE_API_VERSION_3_0) {
- /**
- * Backwards compatibility mode for old HALs:
- * - Convert CameraInfo into static CameraMetadata properties.
- * - Retrieve cached CameraParameters for this camera. If none exist,
- * attempt to open CameraClient and retrieve the CameraParameters.
- * - Convert cached CameraParameters into static CameraMetadata
- * properties.
- */
- ALOGI("%s: Switching to HAL1 shim implementation...", __FUNCTION__);
-
- ret = generateShimMetadata(cameraId, cameraInfo);
- } else {
- /**
- * Normal HAL 2.1+ codepath.
- */
- struct camera_info info;
- ret = filterGetInfoErrorCode(mModule->getCameraInfo(cameraId, &info));
- if (ret.isOk()) {
- *cameraInfo = info.static_camera_characteristics;
- }
+ return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT, "Can't get camera characteristics"
+ " for devices with HAL version < 3.0, %d is version %x", cameraId,
+ getDeviceVersion(cameraId, &facing));
+ }
+ struct camera_info info;
+ ret = filterGetInfoErrorCode(mModule->getCameraInfo(cameraId, &info));
+ if (ret.isOk()) {
+ *cameraInfo = info.static_camera_characteristics;
}
return ret;
@@ -700,10 +620,10 @@
switch(err) {
case NO_ERROR:
return Status::ok();
- case -EINVAL:
+ case BAD_VALUE:
return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT,
"CameraId is not valid for HAL module");
- case -ENODEV:
+ case NO_INIT:
return STATUS_ERROR(ERROR_DISCONNECTED,
"Camera device not available");
default:
@@ -831,6 +751,66 @@
return s;
}
+int32_t CameraService::mapToInterface(TorchModeStatus status) {
+ int32_t serviceStatus = ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE;
+ switch (status) {
+ case TorchModeStatus::NOT_AVAILABLE:
+ serviceStatus = ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE;
+ break;
+ case TorchModeStatus::AVAILABLE_OFF:
+ serviceStatus = ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF;
+ break;
+ case TorchModeStatus::AVAILABLE_ON:
+ serviceStatus = ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON;
+ break;
+ default:
+ ALOGW("Unknown new flash status: %d", status);
+ }
+ return serviceStatus;
+}
+
+CameraService::StatusInternal CameraService::mapToInternal(CameraDeviceStatus status) {
+ StatusInternal serviceStatus = StatusInternal::NOT_PRESENT;
+ switch (status) {
+ case CameraDeviceStatus::NOT_PRESENT:
+ serviceStatus = StatusInternal::NOT_PRESENT;
+ break;
+ case CameraDeviceStatus::PRESENT:
+ serviceStatus = StatusInternal::PRESENT;
+ break;
+ case CameraDeviceStatus::ENUMERATING:
+ serviceStatus = StatusInternal::ENUMERATING;
+ break;
+ default:
+ ALOGW("Unknown new HAL device status: %d", status);
+ }
+ return serviceStatus;
+}
+
+int32_t CameraService::mapToInterface(StatusInternal status) {
+ int32_t serviceStatus = ICameraServiceListener::STATUS_NOT_PRESENT;
+ switch (status) {
+ case StatusInternal::NOT_PRESENT:
+ serviceStatus = ICameraServiceListener::STATUS_NOT_PRESENT;
+ break;
+ case StatusInternal::PRESENT:
+ serviceStatus = ICameraServiceListener::STATUS_PRESENT;
+ break;
+ case StatusInternal::ENUMERATING:
+ serviceStatus = ICameraServiceListener::STATUS_ENUMERATING;
+ break;
+ case StatusInternal::NOT_AVAILABLE:
+ serviceStatus = ICameraServiceListener::STATUS_NOT_AVAILABLE;
+ break;
+ case StatusInternal::UNKNOWN:
+ serviceStatus = ICameraServiceListener::STATUS_UNKNOWN;
+ break;
+ default:
+ ALOGW("Unknown new internal device status: %d", status);
+ }
+ return serviceStatus;
+}
+
Status CameraService::initializeShimMetadata(int cameraId) {
int uid = getCallingUid();
@@ -1042,12 +1022,12 @@
return -ENODEV;
}
- int32_t currentStatus = cameraState->getStatus();
- if (currentStatus == ICameraServiceListener::STATUS_NOT_PRESENT) {
+ StatusInternal currentStatus = cameraState->getStatus();
+ if (currentStatus == StatusInternal::NOT_PRESENT) {
ALOGE("CameraService::connect X (PID %d) rejected (camera %s is not connected)",
callingPid, cameraId.string());
return -ENODEV;
- } else if (currentStatus == ICameraServiceListener::STATUS_ENUMERATING) {
+ } else if (currentStatus == StatusInternal::ENUMERATING) {
ALOGE("CameraService::connect X (PID %d) rejected, (camera %s is initializing)",
callingPid, cameraId.string());
return -EBUSY;
@@ -1345,7 +1325,7 @@
Status CameraService::connectDevice(
const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
- int cameraId,
+ const String16& cameraId,
const String16& clientPackageName,
int clientUid,
/*out*/
@@ -1353,7 +1333,7 @@
ATRACE_CALL();
Status ret = Status::ok();
- String8 id = String8::format("%d", cameraId);
+ String8 id = String8(cameraId);
sp<CameraDeviceClient> client = nullptr;
ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id,
CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName,
@@ -1371,6 +1351,166 @@
return ret;
}
+template<class CALLBACK, class CLIENT>
+Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
+ int halVersion, const String16& clientPackageName, int clientUid, int clientPid,
+ apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
+ /*out*/sp<CLIENT>& device) {
+ binder::Status ret = binder::Status::ok();
+
+ String8 clientName8(clientPackageName);
+
+ int originalClientPid = 0;
+
+ ALOGI("CameraService::connect call (PID %d \"%s\", camera ID %s) for HAL version %s and "
+ "Camera API version %d", clientPid, clientName8.string(), cameraId.string(),
+ (halVersion == -1) ? "default" : std::to_string(halVersion).c_str(),
+ static_cast<int>(effectiveApiLevel));
+
+ sp<CLIENT> client = nullptr;
+ {
+ // Acquire mServiceLock and prevent other clients from connecting
+ std::unique_ptr<AutoConditionLock> lock =
+ AutoConditionLock::waitAndAcquire(mServiceLockWrapper, DEFAULT_CONNECT_TIMEOUT_NS);
+
+ if (lock == nullptr) {
+ ALOGE("CameraService::connect (PID %d) rejected (too many other clients connecting)."
+ , clientPid);
+ return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE,
+ "Cannot open camera %s for \"%s\" (PID %d): Too many other clients connecting",
+ cameraId.string(), clientName8.string(), clientPid);
+ }
+
+ // Enforce client permissions and do basic sanity checks
+ if(!(ret = validateConnectLocked(cameraId, clientName8,
+ /*inout*/clientUid, /*inout*/clientPid, /*out*/originalClientPid)).isOk()) {
+ return ret;
+ }
+
+ // Check the shim parameters after acquiring lock, if they have already been updated and
+ // we were doing a shim update, return immediately
+ if (shimUpdateOnly) {
+ auto cameraState = getCameraState(cameraId);
+ if (cameraState != nullptr) {
+ if (!cameraState->getShimParams().isEmpty()) return ret;
+ }
+ }
+
+ status_t err;
+
+ sp<BasicClient> clientTmp = nullptr;
+ std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>> partial;
+ if ((err = handleEvictionsLocked(cameraId, originalClientPid, effectiveApiLevel,
+ IInterface::asBinder(cameraCb), clientName8, /*out*/&clientTmp,
+ /*out*/&partial)) != NO_ERROR) {
+ switch (err) {
+ case -ENODEV:
+ return STATUS_ERROR_FMT(ERROR_DISCONNECTED,
+ "No camera device with ID \"%s\" currently available",
+ cameraId.string());
+ case -EBUSY:
+ return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE,
+ "Higher-priority client using camera, ID \"%s\" currently unavailable",
+ cameraId.string());
+ default:
+ return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
+ "Unexpected error %s (%d) opening camera \"%s\"",
+ strerror(-err), err, cameraId.string());
+ }
+ }
+
+ if (clientTmp.get() != nullptr) {
+ // Handle special case for API1 MediaRecorder where the existing client is returned
+ device = static_cast<CLIENT*>(clientTmp.get());
+ return ret;
+ }
+
+ // give flashlight a chance to close devices if necessary.
+ mFlashlight->prepareDeviceOpen(cameraId);
+
+ // 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 STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
+ "Bad camera ID \"%s\" passed to camera open", cameraId.string());
+ }
+
+ int facing = -1;
+ int deviceVersion = getDeviceVersion(id, /*out*/&facing);
+ sp<BasicClient> tmp = nullptr;
+ if(!(ret = makeClient(this, cameraCb, clientPackageName, id, facing, clientPid,
+ clientUid, getpid(), legacyMode, halVersion, deviceVersion, effectiveApiLevel,
+ /*out*/&tmp)).isOk()) {
+ return ret;
+ }
+ client = static_cast<CLIENT*>(tmp.get());
+
+ LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",
+ __FUNCTION__);
+
+ if ((err = client->initialize(mModule)) != OK) {
+ ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__);
+ // Errors could be from the HAL module open call or from AppOpsManager
+ switch(err) {
+ case BAD_VALUE:
+ return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
+ "Illegal argument to HAL module for camera \"%s\"", cameraId.string());
+ case -EBUSY:
+ return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE,
+ "Camera \"%s\" is already open", cameraId.string());
+ case -EUSERS:
+ return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE,
+ "Too many cameras already open, cannot open camera \"%s\"",
+ cameraId.string());
+ case PERMISSION_DENIED:
+ return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
+ "No permission to open camera \"%s\"", cameraId.string());
+ case -EACCES:
+ return STATUS_ERROR_FMT(ERROR_DISABLED,
+ "Camera \"%s\" disabled by policy", cameraId.string());
+ case -ENODEV:
+ default:
+ return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
+ "Failed to initialize camera \"%s\": %s (%d)", cameraId.string(),
+ strerror(-err), err);
+ }
+ }
+
+ // Update shim paremeters for legacy clients
+ if (effectiveApiLevel == API_1) {
+ // Assume we have always received a Client subclass for API1
+ sp<Client> shimClient = reinterpret_cast<Client*>(client.get());
+ String8 rawParams = shimClient->getParameters();
+ CameraParameters params(rawParams);
+
+ auto cameraState = getCameraState(cameraId);
+ if (cameraState != nullptr) {
+ cameraState->setShimParams(params);
+ } else {
+ ALOGE("%s: Cannot update shim parameters for camera %s, no such device exists.",
+ __FUNCTION__, cameraId.string());
+ }
+ }
+
+ if (shimUpdateOnly) {
+ // If only updating legacy shim parameters, immediately disconnect client
+ mServiceLock.unlock();
+ client->disconnect();
+ mServiceLock.lock();
+ } else {
+ // Otherwise, add client to active clients list
+ finishConnectLocked(client, partial);
+ }
+ } // lock is destroyed, allow further connect calls
+
+ // Important: release the mutex here so the client can call back into the service from its
+ // destructor (can be at the end of the call)
+ device = client;
+ return ret;
+}
+
Status CameraService::setTorchMode(const String16& cameraId, bool enabled,
const sp<IBinder>& clientBinder) {
@@ -1392,9 +1532,9 @@
"Camera ID \"%s\" is a not valid camera ID", id.string());
}
- int32_t cameraStatus = state->getStatus();
- if (cameraStatus != ICameraServiceListener::STATUS_PRESENT &&
- cameraStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) {
+ StatusInternal cameraStatus = state->getStatus();
+ if (cameraStatus != StatusInternal::PRESENT &&
+ cameraStatus != StatusInternal::NOT_PRESENT) {
ALOGE("%s: camera id is invalid %s", __FUNCTION__, id.string());
return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
"Camera ID \"%s\" is a not valid camera ID", id.string());
@@ -1402,7 +1542,7 @@
{
Mutex::Autolock al(mTorchStatusMutex);
- int32_t status;
+ TorchModeStatus status;
status_t err = getTorchStatusLocked(id, &status);
if (err != OK) {
if (err == NAME_NOT_FOUND) {
@@ -1416,8 +1556,8 @@
strerror(-err), err);
}
- if (status == ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE) {
- if (cameraStatus == ICameraServiceListener::STATUS_NOT_AVAILABLE) {
+ if (status == TorchModeStatus::NOT_AVAILABLE) {
+ if (cameraStatus == StatusInternal::NOT_PRESENT) {
ALOGE("%s: torch mode of camera %s is not available because "
"camera is in use", __FUNCTION__, id.string());
return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE,
@@ -1506,7 +1646,9 @@
return Status::ok();
}
-Status CameraService::addListener(const sp<ICameraServiceListener>& listener) {
+Status CameraService::addListener(const sp<ICameraServiceListener>& listener,
+ /*out*/
+ std::vector<hardware::CameraStatus> *cameraStatuses) {
ATRACE_CALL();
ALOGV("%s: Add listener %p", __FUNCTION__, listener.get());
@@ -1531,25 +1673,23 @@
mListenerList.push_back(listener);
}
-
- /* Immediately signal current status to this listener only */
+ /* Collect current devices and status */
{
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);
+ cameraStatuses->emplace_back(i.first, mapToInterface(i.second->getStatus()));
}
}
- /* Immediately signal current torch status to this listener only */
+ /*
+ * Immediately signal current torch status to this listener only
+ * This may be a subset of all the devices, so don't include it in the response directly
+ */
{
Mutex::Autolock al(mTorchStatusMutex);
for (size_t i = 0; i < mTorchStatusMap.size(); i++ ) {
String16 id = String16(mTorchStatusMap.keyAt(i).string());
- listener->onTorchStatusChanged(mTorchStatusMap.valueAt(i), id);
+ listener->onTorchStatusChanged(mapToInterface(mTorchStatusMap.valueAt(i)), id);
}
}
@@ -1610,10 +1750,11 @@
return ret;
}
-Status CameraService::supportsCameraApi(int cameraId, int apiVersion, bool *isSupported) {
+Status CameraService::supportsCameraApi(const String16& cameraId, int apiVersion,
+ /*out*/ bool *isSupported) {
ATRACE_CALL();
- ALOGV("%s: for camera ID = %d", __FUNCTION__, cameraId);
+ ALOGV("%s: for camera ID = %s", __FUNCTION__, String8(cameraId).string());
switch (apiVersion) {
case API_VERSION_1:
@@ -1626,7 +1767,9 @@
}
int facing = -1;
- int deviceVersion = getDeviceVersion(cameraId, &facing);
+
+ int id = cameraIdToInt(String8(cameraId));
+ int deviceVersion = getDeviceVersion(id, &facing);
switch(deviceVersion) {
case CAMERA_DEVICE_API_VERSION_1_0:
@@ -1634,11 +1777,11 @@
case CAMERA_DEVICE_API_VERSION_3_1:
if (apiVersion == API_VERSION_2) {
ALOGV("%s: Camera id %d uses HAL version %d <3.2, doesn't support api2 without shim",
- __FUNCTION__, cameraId, deviceVersion);
+ __FUNCTION__, id, deviceVersion);
*isSupported = false;
} else { // if (apiVersion == API_VERSION_1) {
ALOGV("%s: Camera id %d uses older HAL before 3.2, but api1 is always supported",
- __FUNCTION__, cameraId);
+ __FUNCTION__, id);
*isSupported = true;
}
break;
@@ -1646,17 +1789,17 @@
case CAMERA_DEVICE_API_VERSION_3_3:
case CAMERA_DEVICE_API_VERSION_3_4:
ALOGV("%s: Camera id %d uses HAL3.2 or newer, supports api1/api2 directly",
- __FUNCTION__, cameraId);
+ __FUNCTION__, id);
*isSupported = true;
break;
case -1: {
- String8 msg = String8::format("Unknown camera ID %d", cameraId);
+ String8 msg = String8::format("Unknown camera ID %d", id);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string());
}
default: {
String8 msg = String8::format("Unknown device version %d for device %d",
- deviceVersion, cameraId);
+ deviceVersion, id);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(ERROR_INVALID_OPERATION, msg.string());
}
@@ -2212,7 +2355,7 @@
mOpsActive = true;
// Transition device availability listeners from PRESENT -> NOT_AVAILABLE
- mCameraService->updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE,
+ mCameraService->updateStatus(StatusInternal::NOT_AVAILABLE,
String8::format("%d", mCameraId));
// Transition device state to OPEN
@@ -2232,11 +2375,11 @@
mClientPackageName);
mOpsActive = false;
- std::initializer_list<int32_t> rejected = {ICameraServiceListener::STATUS_NOT_PRESENT,
- ICameraServiceListener::STATUS_ENUMERATING};
+ std::initializer_list<StatusInternal> rejected = {StatusInternal::PRESENT,
+ StatusInternal::ENUMERATING};
// Transition to PRESENT if the camera is not in either of the rejected states
- mCameraService->updateStatus(ICameraServiceListener::STATUS_PRESENT,
+ mCameraService->updateStatus(StatusInternal::PRESENT,
String8::format("%d", mCameraId), rejected);
// Transition device state to CLOSED
@@ -2336,11 +2479,11 @@
CameraService::CameraState::CameraState(const String8& id, int cost,
const std::set<String8>& conflicting) : mId(id),
- mStatus(ICameraServiceListener::STATUS_PRESENT), mCost(cost), mConflicting(conflicting) {}
+ mStatus(StatusInternal::PRESENT), mCost(cost), mConflicting(conflicting) {}
CameraService::CameraState::~CameraState() {}
-int32_t CameraService::CameraState::getStatus() const {
+CameraService::StatusInternal CameraService::CameraState::getStatus() const {
Mutex::Autolock lock(mStatusLock);
return mStatus;
}
@@ -2719,12 +2862,12 @@
__FUNCTION__);
}
-void CameraService::updateStatus(int32_t status, const String8& cameraId) {
+void CameraService::updateStatus(StatusInternal status, const String8& cameraId) {
updateStatus(status, cameraId, {});
}
-void CameraService::updateStatus(int32_t status, const String8& cameraId,
- std::initializer_list<int32_t> rejectSourceStates) {
+void CameraService::updateStatus(StatusInternal status, const String8& cameraId,
+ std::initializer_list<StatusInternal> rejectSourceStates) {
// Do not lock mServiceLock here or can get into a deadlock from
// connect() -> disconnect -> updateStatus
@@ -2739,18 +2882,18 @@
// 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, int32_t status) {
+ (const String8& cameraId, StatusInternal status) {
- if (status != ICameraServiceListener::STATUS_ENUMERATING) {
+ if (status != StatusInternal::ENUMERATING) {
// Update torch status if it has a flash unit.
Mutex::Autolock al(mTorchStatusMutex);
- int32_t torchStatus;
+ TorchModeStatus torchStatus;
if (getTorchStatusLocked(cameraId, &torchStatus) !=
NAME_NOT_FOUND) {
- int32_t newTorchStatus =
- status == ICameraServiceListener::STATUS_PRESENT ?
- ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF :
- ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE;
+ TorchModeStatus newTorchStatus =
+ status == StatusInternal::PRESENT ?
+ TorchModeStatus::AVAILABLE_OFF :
+ TorchModeStatus::NOT_AVAILABLE;
if (torchStatus != newTorchStatus) {
onTorchStatusChangedLocked(cameraId, newTorchStatus);
}
@@ -2760,13 +2903,54 @@
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);
+ listener->onStatusChanged(mapToInterface(status), String16(cameraId));
}
});
}
+template<class Func>
+void CameraService::CameraState::updateStatus(StatusInternal status,
+ const String8& cameraId,
+ std::initializer_list<StatusInternal> rejectSourceStates,
+ Func onStatusUpdatedLocked) {
+ Mutex::Autolock lock(mStatusLock);
+ StatusInternal 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 == StatusInternal::NOT_PRESENT &&
+ (status != StatusInternal::PRESENT &&
+ status != StatusInternal::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);
+}
+
void CameraService::updateProxyDeviceState(ICameraServiceProxy::CameraState newState,
const String8& cameraId) {
sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
@@ -2777,7 +2961,7 @@
status_t CameraService::getTorchStatusLocked(
const String8& cameraId,
- int32_t *status) const {
+ TorchModeStatus *status) const {
if (!status) {
return BAD_VALUE;
}
@@ -2792,14 +2976,12 @@
}
status_t CameraService::setTorchStatusLocked(const String8& cameraId,
- int32_t status) {
+ TorchModeStatus status) {
ssize_t index = mTorchStatusMap.indexOfKey(cameraId);
if (index == NAME_NOT_FOUND) {
return BAD_VALUE;
}
- int32_t& item =
- mTorchStatusMap.editValueAt(index);
- item = status;
+ mTorchStatusMap.editValueAt(index) = status;
return OK;
}
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index b35f35c..a6c2fa8 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -29,6 +29,8 @@
#include <camera/ICameraServiceProxy.h>
#include <hardware/camera.h>
+#include <android/hardware/camera/common/1.0/types.h>
+
#include <camera/VendorTagDescriptor.h>
#include <camera/CaptureResult.h>
#include <camera/CameraParameters.h>
@@ -95,10 +97,10 @@
/////////////////////////////////////////////////////////////////////
// HAL Callbacks
- virtual void onDeviceStatusChanged(int cameraId,
- camera_device_status_t newStatus);
+ virtual void onDeviceStatusChanged(const String8 &cameraId,
+ hardware::camera::common::V1_0::CameraDeviceStatus newHalStatus);
virtual void onTorchStatusChanged(const String8& cameraId,
- int32_t newStatus);
+ hardware::camera::common::V1_0::TorchModeStatus newStatus);
/////////////////////////////////////////////////////////////////////
// ICameraService
@@ -106,7 +108,7 @@
virtual binder::Status getCameraInfo(int cameraId,
hardware::CameraInfo* cameraInfo);
- virtual binder::Status getCameraCharacteristics(int cameraId,
+ virtual binder::Status getCameraCharacteristics(const String16& id,
CameraMetadata* cameraInfo);
virtual binder::Status getCameraVendorTagDescriptor(
/*out*/
@@ -125,12 +127,14 @@
sp<hardware::ICamera>* device);
virtual binder::Status connectDevice(
- const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb, int32_t cameraId,
+ const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb, const String16& cameraId,
const String16& clientPackageName, int32_t clientUid,
/*out*/
sp<hardware::camera2::ICameraDeviceUser>* device);
- virtual binder::Status addListener(const sp<hardware::ICameraServiceListener>& listener);
+ virtual binder::Status addListener(const sp<hardware::ICameraServiceListener>& listener,
+ /*out*/
+ std::vector<hardware::CameraStatus>* cameraStatuses);
virtual binder::Status removeListener(
const sp<hardware::ICameraServiceListener>& listener);
@@ -147,7 +151,7 @@
// OK = supports api of that version, -EOPNOTSUPP = does not support
virtual binder::Status supportsCameraApi(
- int32_t cameraId, int32_t apiVersion,
+ const String16& cameraId, int32_t apiVersion,
/*out*/
bool *isSupported);
@@ -409,6 +413,20 @@
private:
+ typedef hardware::camera::common::V1_0::CameraDeviceStatus CameraDeviceStatus;
+
+ /**
+ * Typesafe version of device status, containing both the HAL-layer and the service interface-
+ * layer values.
+ */
+ enum class StatusInternal : int32_t {
+ NOT_PRESENT = static_cast<int32_t>(CameraDeviceStatus::NOT_PRESENT),
+ PRESENT = static_cast<int32_t>(CameraDeviceStatus::PRESENT),
+ ENUMERATING = static_cast<int32_t>(CameraDeviceStatus::ENUMERATING),
+ NOT_AVAILABLE = static_cast<int32_t>(hardware::ICameraServiceListener::STATUS_NOT_AVAILABLE),
+ UNKNOWN = static_cast<int32_t>(hardware::ICameraServiceListener::STATUS_UNKNOWN)
+ };
+
/**
* 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
@@ -432,7 +450,7 @@
*
* This method acquires mStatusLock.
*/
- int32_t getStatus() const;
+ StatusInternal getStatus() const;
/**
* This function updates the status for this camera device, unless the given status
@@ -445,8 +463,9 @@
* This method aquires mStatusLock.
*/
template<class Func>
- void updateStatus(int32_t status, const String8& cameraId,
- std::initializer_list<int32_t> rejectSourceStates,
+ void updateStatus(StatusInternal status,
+ const String8& cameraId,
+ std::initializer_list<StatusInternal> rejectSourceStates,
Func onStatusUpdatedLocked);
/**
@@ -477,7 +496,7 @@
private:
const String8 mId;
- int32_t mStatus; // protected by mStatusLock
+ StatusInternal mStatus; // protected by mStatusLock
const int mCost;
std::set<String8> mConflicting;
mutable Mutex mStatusLock;
@@ -671,9 +690,12 @@
* This method must be idempotent.
* This method acquires mStatusLock and mStatusListenerLock.
*/
- void updateStatus(int32_t status, const String8& cameraId,
- std::initializer_list<int32_t> rejectedSourceStates);
- void updateStatus(int32_t status, const String8& cameraId);
+ void updateStatus(StatusInternal status,
+ const String8& cameraId,
+ std::initializer_list<StatusInternal>
+ rejectedSourceStates);
+ void updateStatus(StatusInternal status,
+ const String8& cameraId);
// flashlight control
sp<CameraFlashlight> mFlashlight;
@@ -684,7 +706,8 @@
// guard mTorchUidMap
Mutex mTorchUidMapMutex;
// camera id -> torch status
- KeyedVector<String8, int32_t> mTorchStatusMap;
+ KeyedVector<String8, hardware::camera::common::V1_0::TorchModeStatus>
+ mTorchStatusMap;
// camera id -> torch client binder
// only store the last client that turns on each camera's torch mode
KeyedVector<String8, sp<IBinder>> mTorchClientMap;
@@ -697,15 +720,15 @@
// handle torch mode status change and invoke callbacks. mTorchStatusMutex
// should be locked.
void onTorchStatusChangedLocked(const String8& cameraId,
- int32_t newStatus);
+ hardware::camera::common::V1_0::TorchModeStatus newStatus);
// get a camera's torch status. mTorchStatusMutex should be locked.
status_t getTorchStatusLocked(const String8 &cameraId,
- int32_t *status) const;
+ hardware::camera::common::V1_0::TorchModeStatus *status) const;
// set a camera's torch status. mTorchStatusMutex should be locked.
status_t setTorchStatusLocked(const String8 &cameraId,
- int32_t status);
+ hardware::camera::common::V1_0::TorchModeStatus status);
// IBinder::DeathRecipient implementation
virtual void binderDied(const wp<IBinder> &who);
@@ -729,14 +752,6 @@
*/
binder::Status getLegacyParametersLazy(int cameraId, /*out*/CameraParameters* parameters);
- /**
- * Generate the CameraCharacteristics metadata required by the Camera2 API
- * from the available HAL1 CameraParameters and CameraInfo.
- *
- * Sets Status to a service-specific error on failure
- */
- binder::Status generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo);
-
static int getCallingPid();
static int getCallingUid();
@@ -760,227 +775,15 @@
status_t checkCameraAccess(const String16& opPackageName);
static String8 toString(std::set<userid_t> intSet);
+ static int32_t mapToInterface(hardware::camera::common::V1_0::TorchModeStatus status);
+ static StatusInternal mapToInternal(hardware::camera::common::V1_0::CameraDeviceStatus status);
+ static int32_t mapToInterface(StatusInternal status);
static sp<ICameraServiceProxy> getCameraServiceProxy();
static void pingCameraServiceProxy();
};
-template<class Func>
-void CameraService::CameraState::updateStatus(int32_t status,
- const String8& cameraId,
- std::initializer_list<int32_t> rejectSourceStates,
- Func onStatusUpdatedLocked) {
- Mutex::Autolock lock(mStatusLock);
- int32_t 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 == hardware::ICameraServiceListener::STATUS_NOT_PRESENT &&
- (status != hardware::ICameraServiceListener::STATUS_PRESENT &&
- status != hardware::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);
-}
-
-#define STATUS_ERROR(errorCode, errorString) \
- binder::Status::fromServiceSpecificError(errorCode, \
- String8::format("%s:%d: %s", __FUNCTION__, __LINE__, errorString))
-
-#define STATUS_ERROR_FMT(errorCode, errorString, ...) \
- binder::Status::fromServiceSpecificError(errorCode, \
- String8::format("%s:%d: " errorString, __FUNCTION__, __LINE__, __VA_ARGS__))
-
-
-template<class CALLBACK, class CLIENT>
-binder::Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
- int halVersion, const String16& clientPackageName, int clientUid, int clientPid,
- apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
- /*out*/sp<CLIENT>& device) {
- binder::Status ret = binder::Status::ok();
-
- String8 clientName8(clientPackageName);
-
- int originalClientPid = 0;
-
- ALOGI("CameraService::connect call (PID %d \"%s\", camera ID %s) for HAL version %s and "
- "Camera API version %d", clientPid, clientName8.string(), cameraId.string(),
- (halVersion == -1) ? "default" : std::to_string(halVersion).c_str(),
- static_cast<int>(effectiveApiLevel));
-
- sp<CLIENT> client = nullptr;
- {
- // Acquire mServiceLock and prevent other clients from connecting
- std::unique_ptr<AutoConditionLock> lock =
- AutoConditionLock::waitAndAcquire(mServiceLockWrapper, DEFAULT_CONNECT_TIMEOUT_NS);
-
- if (lock == nullptr) {
- ALOGE("CameraService::connect (PID %d) rejected (too many other clients connecting)."
- , clientPid);
- return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE,
- "Cannot open camera %s for \"%s\" (PID %d): Too many other clients connecting",
- cameraId.string(), clientName8.string(), clientPid);
- }
-
- // Enforce client permissions and do basic sanity checks
- if(!(ret = validateConnectLocked(cameraId, clientName8,
- /*inout*/clientUid, /*inout*/clientPid, /*out*/originalClientPid)).isOk()) {
- return ret;
- }
-
- // Check the shim parameters after acquiring lock, if they have already been updated and
- // we were doing a shim update, return immediately
- if (shimUpdateOnly) {
- auto cameraState = getCameraState(cameraId);
- if (cameraState != nullptr) {
- if (!cameraState->getShimParams().isEmpty()) return ret;
- }
- }
-
- status_t err;
-
- sp<BasicClient> clientTmp = nullptr;
- std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>> partial;
- if ((err = handleEvictionsLocked(cameraId, originalClientPid, effectiveApiLevel,
- IInterface::asBinder(cameraCb), clientName8, /*out*/&clientTmp,
- /*out*/&partial)) != NO_ERROR) {
- switch (err) {
- case -ENODEV:
- return STATUS_ERROR_FMT(ERROR_DISCONNECTED,
- "No camera device with ID \"%s\" currently available",
- cameraId.string());
- case -EBUSY:
- return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE,
- "Higher-priority client using camera, ID \"%s\" currently unavailable",
- cameraId.string());
- default:
- return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
- "Unexpected error %s (%d) opening camera \"%s\"",
- strerror(-err), err, cameraId.string());
- }
- }
-
- if (clientTmp.get() != nullptr) {
- // Handle special case for API1 MediaRecorder where the existing client is returned
- device = static_cast<CLIENT*>(clientTmp.get());
- return ret;
- }
-
- // give flashlight a chance to close devices if necessary.
- mFlashlight->prepareDeviceOpen(cameraId);
-
- // 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 STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
- "Bad camera ID \"%s\" passed to camera open", cameraId.string());
- }
-
- int facing = -1;
- int deviceVersion = getDeviceVersion(id, /*out*/&facing);
- sp<BasicClient> tmp = nullptr;
- if(!(ret = makeClient(this, cameraCb, clientPackageName, id, facing, clientPid,
- clientUid, getpid(), legacyMode, halVersion, deviceVersion, effectiveApiLevel,
- /*out*/&tmp)).isOk()) {
- return ret;
- }
- client = static_cast<CLIENT*>(tmp.get());
-
- LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",
- __FUNCTION__);
-
- if ((err = client->initialize(mModule)) != OK) {
- ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__);
- // Errors could be from the HAL module open call or from AppOpsManager
- switch(err) {
- case BAD_VALUE:
- return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
- "Illegal argument to HAL module for camera \"%s\"", cameraId.string());
- case -EBUSY:
- return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE,
- "Camera \"%s\" is already open", cameraId.string());
- case -EUSERS:
- return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE,
- "Too many cameras already open, cannot open camera \"%s\"",
- cameraId.string());
- case PERMISSION_DENIED:
- return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
- "No permission to open camera \"%s\"", cameraId.string());
- case -EACCES:
- return STATUS_ERROR_FMT(ERROR_DISABLED,
- "Camera \"%s\" disabled by policy", cameraId.string());
- case -ENODEV:
- default:
- return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
- "Failed to initialize camera \"%s\": %s (%d)", cameraId.string(),
- strerror(-err), err);
- }
- }
-
- // Update shim paremeters for legacy clients
- if (effectiveApiLevel == API_1) {
- // Assume we have always received a Client subclass for API1
- sp<Client> shimClient = reinterpret_cast<Client*>(client.get());
- String8 rawParams = shimClient->getParameters();
- CameraParameters params(rawParams);
-
- auto cameraState = getCameraState(cameraId);
- if (cameraState != nullptr) {
- cameraState->setShimParams(params);
- } else {
- ALOGE("%s: Cannot update shim parameters for camera %s, no such device exists.",
- __FUNCTION__, cameraId.string());
- }
- }
-
- if (shimUpdateOnly) {
- // If only updating legacy shim parameters, immediately disconnect client
- mServiceLock.unlock();
- client->disconnect();
- mServiceLock.lock();
- } else {
- // Otherwise, add client to active clients list
- finishConnectLocked(client, partial);
- }
- } // lock is destroyed, allow further connect calls
-
- // Important: release the mutex here so the client can call back into the service from its
- // destructor (can be at the end of the call)
- device = client;
- return ret;
-}
-
-#undef STATUS_ERROR_FMT
-#undef STATUS_ERROR
-
} // namespace android
#endif
diff --git a/services/mediaextractor/minijail/seccomp_policy/mediaextractor-seccomp-arm64.policy b/services/mediaextractor/minijail/seccomp_policy/mediaextractor-seccomp-arm64.policy
index ae6ac05..7e7b858 100644
--- a/services/mediaextractor/minijail/seccomp_policy/mediaextractor-seccomp-arm64.policy
+++ b/services/mediaextractor/minijail/seccomp_policy/mediaextractor-seccomp-arm64.policy
@@ -28,6 +28,8 @@
getrlimit: 1
# for attaching to debuggerd on process crash
+tgkill: 1
+rt_sigprocmask: 1
rt_sigaction: 1
# socket: arg0 == AF_LOCAL
socket: arg0 == 1
diff --git a/services/radio/RadioHalHidl.cpp b/services/radio/RadioHalHidl.cpp
index 07cb4d5..c7a899a 100644
--- a/services/radio/RadioHalHidl.cpp
+++ b/services/radio/RadioHalHidl.cpp
@@ -39,6 +39,11 @@
return new RadioHalHidl(classId);
}
+void RadioHalHidl::crashIfHalIsDead(const Status& status) {
+ LOG_ALWAYS_FATAL_IF(
+ status.transactionError() == DEAD_OBJECT, "HAL server crashed, need to restart");
+}
+
int RadioHalHidl::getProperties(radio_hal_properties_t *properties)
{
ALOGV("%s IN", __FUNCTION__);
@@ -56,10 +61,7 @@
}
});
- if (hidlReturn.getStatus().transactionError() == DEAD_OBJECT) {
- clearService();
- return -EPIPE;
- }
+ crashIfHalIsDead(hidlReturn.getStatus());
if (halResult == Result::OK) {
HidlUtils::convertPropertiesFromHal(properties, &halProperties);
}
@@ -91,10 +93,7 @@
}
});
- if (hidlReturn.getStatus().transactionError() == DEAD_OBJECT) {
- clearService();
- return -EPIPE;
- }
+ crashIfHalIsDead(hidlReturn.getStatus());
if (halResult == Result::OK) {
tunerImpl->setHalTuner(halTuner);
tuner = tunerImpl;
@@ -375,6 +374,7 @@
if (status == DEAD_OBJECT) {
handleHwFailure();
}
+ RadioHalHidl::crashIfHalIsDead(hidlStatus);
return status;
}
diff --git a/services/radio/RadioHalHidl.h b/services/radio/RadioHalHidl.h
index 5211ee2..1657a40 100644
--- a/services/radio/RadioHalHidl.h
+++ b/services/radio/RadioHalHidl.h
@@ -45,6 +45,8 @@
public:
RadioHalHidl(radio_class_t classId);
+ static void crashIfHalIsDead(const Status& status);
+
// RadioInterface
virtual int getProperties(radio_hal_properties_t *properties);
virtual int openTuner(const radio_hal_band_config_t *config,
diff --git a/services/soundtrigger/SoundTriggerHalHidl.cpp b/services/soundtrigger/SoundTriggerHalHidl.cpp
index ecbdec4..c027799 100644
--- a/services/soundtrigger/SoundTriggerHalHidl.cpp
+++ b/services/soundtrigger/SoundTriggerHalHidl.cpp
@@ -59,9 +59,7 @@
}
} else {
ret = (int)hidlReturn.getStatus().transactionError();
- if (ret == -EPIPE) {
- clearService();
- }
+ crashIfHalIsDead(ret);
}
ALOGI("getProperties ret %d", ret);
return ret;
@@ -135,9 +133,7 @@
} else {
ret = (int)hidlReturn.getStatus().transactionError();
ALOGE("loadSoundModel error %d", ret);
- if (ret == -EPIPE) {
- clearService();
- }
+ crashIfHalIsDead(ret);
}
@@ -164,9 +160,7 @@
}
int ret = (int)hidlReturn.getStatus().transactionError();
ALOGE_IF(ret != 0, "unloadSoundModel error %d", ret);
- if (ret == -EPIPE) {
- clearService();
- }
+ crashIfHalIsDead(ret);
if (ret == 0) {
ret = hidlReturn;
}
@@ -205,9 +199,7 @@
int ret = (int)hidlReturn.getStatus().transactionError();
ALOGE_IF(ret != 0, "startRecognition error %d", ret);
- if (ret == -EPIPE) {
- clearService();
- }
+ crashIfHalIsDead(ret);
if (ret == 0) {
ret = hidlReturn;
}
@@ -235,9 +227,7 @@
int ret = (int)hidlReturn.getStatus().transactionError();
ALOGE_IF(ret != 0, "stopRecognition error %d", ret);
- if (ret == -EPIPE) {
- clearService();
- }
+ crashIfHalIsDead(ret);
if (ret == 0) {
ret = hidlReturn;
}
@@ -259,9 +249,7 @@
int ret = (int)hidlReturn.getStatus().transactionError();
ALOGE_IF(ret != 0, "stopAllRecognitions error %d", ret);
- if (ret == -EPIPE) {
- clearService();
- }
+ crashIfHalIsDead(ret);
if (ret == 0) {
ret = hidlReturn;
}
@@ -291,10 +279,9 @@
return mISoundTrigger;
}
-void SoundTriggerHalHidl::clearService()
+void SoundTriggerHalHidl::crashIfHalIsDead(int ret)
{
- AutoMutex lock(mLock);
- mISoundTrigger = 0;
+ LOG_ALWAYS_FATAL_IF(ret == -EPIPE, "HAL server crashed, need to restart");
}
sp<SoundTriggerHalHidl::SoundModel> SoundTriggerHalHidl::getModel(sound_model_handle_t handle)
diff --git a/services/soundtrigger/SoundTriggerHalHidl.h b/services/soundtrigger/SoundTriggerHalHidl.h
index e578dda..b235e1c 100644
--- a/services/soundtrigger/SoundTriggerHalHidl.h
+++ b/services/soundtrigger/SoundTriggerHalHidl.h
@@ -143,7 +143,7 @@
uint32_t nextUniqueId();
sp<ISoundTriggerHw> getService();
- void clearService();
+ void crashIfHalIsDead(int ret);
sp<SoundModel> getModel(sound_model_handle_t handle);
sp<SoundModel> removeModel(sound_model_handle_t handle);