Merge "Add missing syscalls to mediaextractor arm64 seccomp filter"
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/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/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/media/liboboe/src/core/OboeAudio.cpp b/media/liboboe/src/core/OboeAudio.cpp
index fc1b021..a02f226 100644
--- a/media/liboboe/src/core/OboeAudio.cpp
+++ b/media/liboboe/src/core/OboeAudio.cpp
@@ -405,7 +405,7 @@
return audioStream->createThread(periodNanoseconds, startRoutine, arg);
}
-OBOE_API oboe_result_t Oboe_joinThread(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_joinThread(OboeStream stream,
void **returnArg,
oboe_nanoseconds_t timeoutNanoseconds)
{
diff --git a/media/liboboe/tests/test_oboe_api.cpp b/media/liboboe/tests/test_oboe_api.cpp
index b3fb24b..acf3000 100644
--- a/media/liboboe/tests/test_oboe_api.cpp
+++ b/media/liboboe/tests/test_oboe_api.cpp
@@ -285,3 +285,57 @@
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/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/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index a714041..8cd11f5 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/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 86c48a4..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);
}
}
@@ -302,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;
@@ -332,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());
@@ -358,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)",
@@ -406,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);
@@ -427,7 +447,7 @@
{
Mutex::Autolock lock(mStatusListenerLock);
for (auto& i : mListenerList) {
- i->onTorchStatusChanged(newStatus, String16{cameraId});
+ i->onTorchStatusChanged(mapToInterface(newStatus), String16{cameraId});
}
}
}
@@ -490,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__);
@@ -597,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,
@@ -607,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;
@@ -703,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:
@@ -834,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();
@@ -1045,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;
@@ -1348,7 +1325,7 @@
Status CameraService::connectDevice(
const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
- int cameraId,
+ const String16& cameraId,
const String16& clientPackageName,
int clientUid,
/*out*/
@@ -1356,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,
@@ -1374,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) {
@@ -1395,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());
@@ -1405,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) {
@@ -1419,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,
@@ -1509,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());
@@ -1534,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);
}
}
@@ -1613,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:
@@ -1629,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:
@@ -1637,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;
@@ -1649,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());
}
@@ -2215,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
@@ -2235,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
@@ -2339,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;
}
@@ -2722,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
@@ -2742,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);
}
@@ -2763,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();
@@ -2780,7 +2961,7 @@
status_t CameraService::getTorchStatusLocked(
const String8& cameraId,
- int32_t *status) const {
+ TorchModeStatus *status) const {
if (!status) {
return BAD_VALUE;
}
@@ -2795,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