| /* |
| * Copyright (C) 2010 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 "CameraServiceTest" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <unistd.h> |
| #include <camera/Camera.h> |
| #include <camera/CameraParameters.h> |
| #include <ui/GraphicBuffer.h> |
| #include <camera/ICamera.h> |
| #include <camera/ICameraClient.h> |
| #include <camera/ICameraService.h> |
| #include <binder/IPCThreadState.h> |
| #include <binder/IServiceManager.h> |
| #include <binder/ProcessState.h> |
| #include <utils/KeyedVector.h> |
| #include <utils/Log.h> |
| #include <utils/Vector.h> |
| #include <utils/threads.h> |
| |
| using namespace android; |
| |
| // |
| // Assertion and Logging utilities |
| // |
| #define INFO(...) \ |
| do { \ |
| printf(__VA_ARGS__); \ |
| printf("\n"); \ |
| ALOGD(__VA_ARGS__); \ |
| } while(0) |
| |
| void assert_fail(const char *file, int line, const char *func, const char *expr) { |
| INFO("assertion failed at file %s, line %d, function %s:", |
| file, line, func); |
| INFO("%s", expr); |
| abort(); |
| } |
| |
| void assert_eq_fail(const char *file, int line, const char *func, |
| const char *expr, int actual) { |
| INFO("assertion failed at file %s, line %d, function %s:", |
| file, line, func); |
| INFO("(expected) %s != (actual) %d", expr, actual); |
| abort(); |
| } |
| |
| #define ASSERT(e) \ |
| do { \ |
| if (!(e)) \ |
| assert_fail(__FILE__, __LINE__, __func__, #e); \ |
| } while(0) |
| |
| #define ASSERT_EQ(expected, actual) \ |
| do { \ |
| int _x = (actual); \ |
| if (_x != (expected)) \ |
| assert_eq_fail(__FILE__, __LINE__, __func__, #expected, _x); \ |
| } while(0) |
| |
| // |
| // Holder service for pass objects between processes. |
| // |
| class IHolder : public IInterface { |
| protected: |
| enum { |
| HOLDER_PUT = IBinder::FIRST_CALL_TRANSACTION, |
| HOLDER_GET, |
| HOLDER_CLEAR |
| }; |
| public: |
| DECLARE_META_INTERFACE(Holder); |
| |
| virtual void put(sp<IBinder> obj) = 0; |
| virtual sp<IBinder> get() = 0; |
| virtual void clear() = 0; |
| }; |
| |
| class BnHolder : public BnInterface<IHolder> { |
| virtual status_t onTransact(uint32_t code, |
| const Parcel& data, |
| Parcel* reply, |
| uint32_t flags = 0); |
| }; |
| |
| class BpHolder : public BpInterface<IHolder> { |
| public: |
| BpHolder(const sp<IBinder>& impl) |
| : BpInterface<IHolder>(impl) { |
| } |
| |
| virtual void put(sp<IBinder> obj) { |
| Parcel data, reply; |
| data.writeStrongBinder(obj); |
| remote()->transact(HOLDER_PUT, data, &reply, IBinder::FLAG_ONEWAY); |
| } |
| |
| virtual sp<IBinder> get() { |
| Parcel data, reply; |
| remote()->transact(HOLDER_GET, data, &reply); |
| return reply.readStrongBinder(); |
| } |
| |
| virtual void clear() { |
| Parcel data, reply; |
| remote()->transact(HOLDER_CLEAR, data, &reply); |
| } |
| }; |
| |
| IMPLEMENT_META_INTERFACE(Holder, "CameraServiceTest.Holder"); |
| |
| status_t BnHolder::onTransact( |
| uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { |
| switch(code) { |
| case HOLDER_PUT: { |
| put(data.readStrongBinder()); |
| return NO_ERROR; |
| } break; |
| case HOLDER_GET: { |
| reply->writeStrongBinder(get()); |
| return NO_ERROR; |
| } break; |
| case HOLDER_CLEAR: { |
| clear(); |
| return NO_ERROR; |
| } break; |
| default: |
| return BBinder::onTransact(code, data, reply, flags); |
| } |
| } |
| |
| class HolderService : public BnHolder { |
| virtual void put(sp<IBinder> obj) { |
| mObj = obj; |
| } |
| virtual sp<IBinder> get() { |
| return mObj; |
| } |
| virtual void clear() { |
| mObj.clear(); |
| } |
| private: |
| sp<IBinder> mObj; |
| }; |
| |
| // |
| // A mock CameraClient |
| // |
| class MCameraClient : public BnCameraClient { |
| public: |
| virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2); |
| virtual void dataCallback(int32_t msgType, const sp<IMemory>& data); |
| virtual void dataCallbackTimestamp(nsecs_t timestamp, |
| int32_t msgType, const sp<IMemory>& data); |
| |
| // new functions |
| void clearStat(); |
| enum OP { EQ, GE, LE, GT, LT }; |
| void assertNotify(int32_t msgType, OP op, int count); |
| void assertData(int32_t msgType, OP op, int count); |
| void waitNotify(int32_t msgType, OP op, int count); |
| void waitData(int32_t msgType, OP op, int count); |
| void assertDataSize(int32_t msgType, OP op, int dataSize); |
| |
| void setReleaser(ICamera *releaser) { |
| mReleaser = releaser; |
| } |
| private: |
| Mutex mLock; |
| Condition mCond; |
| DefaultKeyedVector<int32_t, int> mNotifyCount; |
| DefaultKeyedVector<int32_t, int> mDataCount; |
| DefaultKeyedVector<int32_t, int> mDataSize; |
| bool test(OP op, int v1, int v2); |
| void assertTest(OP op, int v1, int v2); |
| |
| ICamera *mReleaser; |
| }; |
| |
| void MCameraClient::clearStat() { |
| Mutex::Autolock _l(mLock); |
| mNotifyCount.clear(); |
| mDataCount.clear(); |
| mDataSize.clear(); |
| } |
| |
| bool MCameraClient::test(OP op, int v1, int v2) { |
| switch (op) { |
| case EQ: return v1 == v2; |
| case GT: return v1 > v2; |
| case LT: return v1 < v2; |
| case GE: return v1 >= v2; |
| case LE: return v1 <= v2; |
| default: ASSERT(0); break; |
| } |
| return false; |
| } |
| |
| void MCameraClient::assertTest(OP op, int v1, int v2) { |
| if (!test(op, v1, v2)) { |
| ALOGE("assertTest failed: op=%d, v1=%d, v2=%d", op, v1, v2); |
| ASSERT(0); |
| } |
| } |
| |
| void MCameraClient::assertNotify(int32_t msgType, OP op, int count) { |
| Mutex::Autolock _l(mLock); |
| int v = mNotifyCount.valueFor(msgType); |
| assertTest(op, v, count); |
| } |
| |
| void MCameraClient::assertData(int32_t msgType, OP op, int count) { |
| Mutex::Autolock _l(mLock); |
| int v = mDataCount.valueFor(msgType); |
| assertTest(op, v, count); |
| } |
| |
| void MCameraClient::assertDataSize(int32_t msgType, OP op, int dataSize) { |
| Mutex::Autolock _l(mLock); |
| int v = mDataSize.valueFor(msgType); |
| assertTest(op, v, dataSize); |
| } |
| |
| void MCameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) { |
| INFO("%s", __func__); |
| Mutex::Autolock _l(mLock); |
| ssize_t i = mNotifyCount.indexOfKey(msgType); |
| if (i < 0) { |
| mNotifyCount.add(msgType, 1); |
| } else { |
| ++mNotifyCount.editValueAt(i); |
| } |
| mCond.signal(); |
| } |
| |
| void MCameraClient::dataCallback(int32_t msgType, const sp<IMemory>& data) { |
| INFO("%s", __func__); |
| int dataSize = data->size(); |
| INFO("data type = %d, size = %d", msgType, dataSize); |
| Mutex::Autolock _l(mLock); |
| ssize_t i = mDataCount.indexOfKey(msgType); |
| if (i < 0) { |
| mDataCount.add(msgType, 1); |
| mDataSize.add(msgType, dataSize); |
| } else { |
| ++mDataCount.editValueAt(i); |
| mDataSize.editValueAt(i) = dataSize; |
| } |
| mCond.signal(); |
| |
| if (msgType == CAMERA_MSG_VIDEO_FRAME) { |
| ASSERT(mReleaser != NULL); |
| mReleaser->releaseRecordingFrame(data); |
| } |
| } |
| |
| void MCameraClient::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, |
| const sp<IMemory>& data) { |
| dataCallback(msgType, data); |
| } |
| |
| void MCameraClient::waitNotify(int32_t msgType, OP op, int count) { |
| INFO("waitNotify: %d, %d, %d", msgType, op, count); |
| Mutex::Autolock _l(mLock); |
| while (true) { |
| int v = mNotifyCount.valueFor(msgType); |
| if (test(op, v, count)) { |
| break; |
| } |
| mCond.wait(mLock); |
| } |
| } |
| |
| void MCameraClient::waitData(int32_t msgType, OP op, int count) { |
| INFO("waitData: %d, %d, %d", msgType, op, count); |
| Mutex::Autolock _l(mLock); |
| while (true) { |
| int v = mDataCount.valueFor(msgType); |
| if (test(op, v, count)) { |
| break; |
| } |
| mCond.wait(mLock); |
| } |
| } |
| |
| // |
| // A mock Surface |
| // |
| class MSurface : public BnSurface { |
| public: |
| virtual status_t registerBuffers(const BufferHeap& buffers); |
| virtual void postBuffer(ssize_t offset); |
| virtual void unregisterBuffers(); |
| virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage); |
| virtual status_t setBufferCount(int bufferCount); |
| |
| // new functions |
| void clearStat(); |
| void waitUntil(int c0, int c1, int c2); |
| |
| private: |
| // check callback count |
| Condition mCond; |
| Mutex mLock; |
| int registerBuffersCount; |
| int postBufferCount; |
| int unregisterBuffersCount; |
| }; |
| |
| status_t MSurface::registerBuffers(const BufferHeap& buffers) { |
| INFO("%s", __func__); |
| Mutex::Autolock _l(mLock); |
| ++registerBuffersCount; |
| mCond.signal(); |
| return NO_ERROR; |
| } |
| |
| void MSurface::postBuffer(ssize_t offset) { |
| // INFO("%s", __func__); |
| Mutex::Autolock _l(mLock); |
| ++postBufferCount; |
| mCond.signal(); |
| } |
| |
| void MSurface::unregisterBuffers() { |
| INFO("%s", __func__); |
| Mutex::Autolock _l(mLock); |
| ++unregisterBuffersCount; |
| mCond.signal(); |
| } |
| |
| sp<GraphicBuffer> MSurface::requestBuffer(int bufferIdx, int usage) { |
| INFO("%s", __func__); |
| return NULL; |
| } |
| |
| status_t MSurface::setBufferCount(int bufferCount) { |
| INFO("%s", __func__); |
| return NULL; |
| } |
| |
| void MSurface::clearStat() { |
| Mutex::Autolock _l(mLock); |
| registerBuffersCount = 0; |
| postBufferCount = 0; |
| unregisterBuffersCount = 0; |
| } |
| |
| void MSurface::waitUntil(int c0, int c1, int c2) { |
| INFO("waitUntil: %d %d %d", c0, c1, c2); |
| Mutex::Autolock _l(mLock); |
| while (true) { |
| if (registerBuffersCount >= c0 && |
| postBufferCount >= c1 && |
| unregisterBuffersCount >= c2) { |
| break; |
| } |
| mCond.wait(mLock); |
| } |
| } |
| |
| // |
| // Utilities to use the Holder service |
| // |
| sp<IHolder> getHolder() { |
| sp<IServiceManager> sm = defaultServiceManager(); |
| ASSERT(sm != 0); |
| sp<IBinder> binder = sm->getService(String16("CameraServiceTest.Holder")); |
| ASSERT(binder != 0); |
| sp<IHolder> holder = interface_cast<IHolder>(binder); |
| ASSERT(holder != 0); |
| return holder; |
| } |
| |
| void putTempObject(sp<IBinder> obj) { |
| INFO("%s", __func__); |
| getHolder()->put(obj); |
| } |
| |
| sp<IBinder> getTempObject() { |
| INFO("%s", __func__); |
| return getHolder()->get(); |
| } |
| |
| void clearTempObject() { |
| INFO("%s", __func__); |
| getHolder()->clear(); |
| } |
| |
| // |
| // Get a Camera Service |
| // |
| sp<ICameraService> getCameraService() { |
| sp<IServiceManager> sm = defaultServiceManager(); |
| ASSERT(sm != 0); |
| sp<IBinder> binder = sm->getService(String16("media.camera")); |
| ASSERT(binder != 0); |
| sp<ICameraService> cs = interface_cast<ICameraService>(binder); |
| ASSERT(cs != 0); |
| return cs; |
| } |
| |
| int getNumberOfCameras() { |
| sp<ICameraService> cs = getCameraService(); |
| return cs->getNumberOfCameras(); |
| } |
| |
| // |
| // Various Connect Tests |
| // |
| void testConnect(int cameraId) { |
| INFO("%s", __func__); |
| sp<ICameraService> cs = getCameraService(); |
| sp<MCameraClient> cc = new MCameraClient(); |
| sp<ICamera> c = cs->connect(cc, cameraId); |
| ASSERT(c != 0); |
| c->disconnect(); |
| } |
| |
| void testAllowConnectOnceOnly(int cameraId) { |
| INFO("%s", __func__); |
| sp<ICameraService> cs = getCameraService(); |
| // Connect the first client. |
| sp<MCameraClient> cc = new MCameraClient(); |
| sp<ICamera> c = cs->connect(cc, cameraId); |
| ASSERT(c != 0); |
| // Same client -- ok. |
| ASSERT(cs->connect(cc, cameraId) != 0); |
| // Different client -- not ok. |
| sp<MCameraClient> cc2 = new MCameraClient(); |
| ASSERT(cs->connect(cc2, cameraId) == 0); |
| c->disconnect(); |
| } |
| |
| void testReconnectFailed() { |
| INFO("%s", __func__); |
| sp<ICamera> c = interface_cast<ICamera>(getTempObject()); |
| sp<MCameraClient> cc = new MCameraClient(); |
| ASSERT(c->connect(cc) != NO_ERROR); |
| } |
| |
| void testReconnectSuccess() { |
| INFO("%s", __func__); |
| sp<ICamera> c = interface_cast<ICamera>(getTempObject()); |
| sp<MCameraClient> cc = new MCameraClient(); |
| ASSERT(c->connect(cc) == NO_ERROR); |
| c->disconnect(); |
| } |
| |
| void testLockFailed() { |
| INFO("%s", __func__); |
| sp<ICamera> c = interface_cast<ICamera>(getTempObject()); |
| ASSERT(c->lock() != NO_ERROR); |
| } |
| |
| void testLockUnlockSuccess() { |
| INFO("%s", __func__); |
| sp<ICamera> c = interface_cast<ICamera>(getTempObject()); |
| ASSERT(c->lock() == NO_ERROR); |
| ASSERT(c->unlock() == NO_ERROR); |
| } |
| |
| void testLockSuccess() { |
| INFO("%s", __func__); |
| sp<ICamera> c = interface_cast<ICamera>(getTempObject()); |
| ASSERT(c->lock() == NO_ERROR); |
| c->disconnect(); |
| } |
| |
| // |
| // Run the connect tests in another process. |
| // |
| const char *gExecutable; |
| |
| struct FunctionTableEntry { |
| const char *name; |
| void (*func)(); |
| }; |
| |
| FunctionTableEntry function_table[] = { |
| #define ENTRY(x) {#x, &x} |
| ENTRY(testReconnectFailed), |
| ENTRY(testReconnectSuccess), |
| ENTRY(testLockUnlockSuccess), |
| ENTRY(testLockFailed), |
| ENTRY(testLockSuccess), |
| #undef ENTRY |
| }; |
| |
| void runFunction(const char *tag) { |
| INFO("runFunction: %s", tag); |
| int entries = sizeof(function_table) / sizeof(function_table[0]); |
| for (int i = 0; i < entries; i++) { |
| if (strcmp(function_table[i].name, tag) == 0) { |
| (*function_table[i].func)(); |
| return; |
| } |
| } |
| ASSERT(0); |
| } |
| |
| void runInAnotherProcess(const char *tag) { |
| pid_t pid = fork(); |
| if (pid == 0) { |
| execlp(gExecutable, gExecutable, tag, NULL); |
| ASSERT(0); |
| } else { |
| int status; |
| ASSERT_EQ(pid, wait(&status)); |
| ASSERT_EQ(0, status); |
| } |
| } |
| |
| void testReconnect(int cameraId) { |
| INFO("%s", __func__); |
| sp<ICameraService> cs = getCameraService(); |
| sp<MCameraClient> cc = new MCameraClient(); |
| sp<ICamera> c = cs->connect(cc, cameraId); |
| ASSERT(c != 0); |
| // Reconnect to the same client -- ok. |
| ASSERT(c->connect(cc) == NO_ERROR); |
| // Reconnect to a different client (but the same pid) -- ok. |
| sp<MCameraClient> cc2 = new MCameraClient(); |
| ASSERT(c->connect(cc2) == NO_ERROR); |
| c->disconnect(); |
| cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); |
| } |
| |
| void testLockUnlock(int cameraId) { |
| sp<ICameraService> cs = getCameraService(); |
| sp<MCameraClient> cc = new MCameraClient(); |
| sp<ICamera> c = cs->connect(cc, cameraId); |
| ASSERT(c != 0); |
| // We can lock as many times as we want. |
| ASSERT(c->lock() == NO_ERROR); |
| ASSERT(c->lock() == NO_ERROR); |
| // Lock from a different process -- not ok. |
| putTempObject(c->asBinder()); |
| runInAnotherProcess("testLockFailed"); |
| // Unlock then lock from a different process -- ok. |
| ASSERT(c->unlock() == NO_ERROR); |
| runInAnotherProcess("testLockUnlockSuccess"); |
| // Unlock then lock from a different process -- ok. |
| runInAnotherProcess("testLockSuccess"); |
| clearTempObject(); |
| } |
| |
| void testReconnectFromAnotherProcess(int cameraId) { |
| INFO("%s", __func__); |
| |
| sp<ICameraService> cs = getCameraService(); |
| sp<MCameraClient> cc = new MCameraClient(); |
| sp<ICamera> c = cs->connect(cc, cameraId); |
| ASSERT(c != 0); |
| // Reconnect from a different process -- not ok. |
| putTempObject(c->asBinder()); |
| runInAnotherProcess("testReconnectFailed"); |
| // Unlock then reconnect from a different process -- ok. |
| ASSERT(c->unlock() == NO_ERROR); |
| runInAnotherProcess("testReconnectSuccess"); |
| clearTempObject(); |
| } |
| |
| // We need to flush the command buffer after the reference |
| // to ICamera is gone. The sleep is for the server to run |
| // the destructor for it. |
| static void flushCommands() { |
| IPCThreadState::self()->flushCommands(); |
| usleep(200000); // 200ms |
| } |
| |
| // Run a test case |
| #define RUN(class_name, cameraId) do { \ |
| { \ |
| INFO(#class_name); \ |
| class_name instance; \ |
| instance.init(cameraId); \ |
| instance.run(); \ |
| } \ |
| flushCommands(); \ |
| } while(0) |
| |
| // Base test case after the the camera is connected. |
| class AfterConnect { |
| public: |
| void init(int cameraId) { |
| cs = getCameraService(); |
| cc = new MCameraClient(); |
| c = cs->connect(cc, cameraId); |
| ASSERT(c != 0); |
| } |
| |
| protected: |
| sp<ICameraService> cs; |
| sp<MCameraClient> cc; |
| sp<ICamera> c; |
| |
| ~AfterConnect() { |
| c->disconnect(); |
| c.clear(); |
| cc.clear(); |
| cs.clear(); |
| } |
| }; |
| |
| class TestSetPreviewDisplay : public AfterConnect { |
| public: |
| void run() { |
| sp<MSurface> surface = new MSurface(); |
| ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); |
| c->disconnect(); |
| cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); |
| } |
| }; |
| |
| class TestStartPreview : public AfterConnect { |
| public: |
| void run() { |
| sp<MSurface> surface = new MSurface(); |
| ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); |
| |
| ASSERT(c->startPreview() == NO_ERROR); |
| ASSERT(c->previewEnabled() == true); |
| |
| surface->waitUntil(1, 10, 0); // needs 1 registerBuffers and 10 postBuffer |
| surface->clearStat(); |
| |
| sp<MSurface> another_surface = new MSurface(); |
| c->setPreviewDisplay(another_surface); // just to make sure unregisterBuffers |
| // is called. |
| surface->waitUntil(0, 0, 1); // needs unregisterBuffers |
| |
| cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); |
| } |
| }; |
| |
| class TestStartPreviewWithoutDisplay : public AfterConnect { |
| public: |
| void run() { |
| ASSERT(c->startPreview() == NO_ERROR); |
| ASSERT(c->previewEnabled() == true); |
| c->disconnect(); |
| cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); |
| } |
| }; |
| |
| // Base test case after the the camera is connected and the preview is started. |
| class AfterStartPreview : public AfterConnect { |
| public: |
| void init(int cameraId) { |
| AfterConnect::init(cameraId); |
| surface = new MSurface(); |
| ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); |
| ASSERT(c->startPreview() == NO_ERROR); |
| } |
| |
| protected: |
| sp<MSurface> surface; |
| |
| ~AfterStartPreview() { |
| surface.clear(); |
| } |
| }; |
| |
| class TestAutoFocus : public AfterStartPreview { |
| public: |
| void run() { |
| cc->assertNotify(CAMERA_MSG_FOCUS, MCameraClient::EQ, 0); |
| c->autoFocus(); |
| cc->waitNotify(CAMERA_MSG_FOCUS, MCameraClient::EQ, 1); |
| c->disconnect(); |
| cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); |
| } |
| }; |
| |
| class TestStopPreview : public AfterStartPreview { |
| public: |
| void run() { |
| ASSERT(c->previewEnabled() == true); |
| c->stopPreview(); |
| ASSERT(c->previewEnabled() == false); |
| c->disconnect(); |
| cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); |
| } |
| }; |
| |
| class TestTakePicture: public AfterStartPreview { |
| public: |
| void run() { |
| ASSERT(c->takePicture() == NO_ERROR); |
| cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1); |
| cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); |
| cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); |
| c->stopPreview(); |
| c->disconnect(); |
| cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); |
| } |
| }; |
| |
| class TestTakeMultiplePictures: public AfterStartPreview { |
| public: |
| void run() { |
| for (int i = 0; i < 10; i++) { |
| cc->clearStat(); |
| ASSERT(c->takePicture() == NO_ERROR); |
| cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1); |
| cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); |
| cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); |
| } |
| c->disconnect(); |
| cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); |
| } |
| }; |
| |
| class TestGetParameters: public AfterStartPreview { |
| public: |
| void run() { |
| String8 param_str = c->getParameters(); |
| INFO("%s", static_cast<const char*>(param_str)); |
| } |
| }; |
| |
| static bool getNextSize(const char **ptrS, int *w, int *h) { |
| const char *s = *ptrS; |
| |
| // skip over ',' |
| if (*s == ',') s++; |
| |
| // remember start position in p |
| const char *p = s; |
| while (*s != '\0' && *s != 'x') { |
| s++; |
| } |
| if (*s == '\0') return false; |
| |
| // get the width |
| *w = atoi(p); |
| |
| // skip over 'x' |
| ASSERT(*s == 'x'); |
| p = s + 1; |
| while (*s != '\0' && *s != ',') { |
| s++; |
| } |
| |
| // get the height |
| *h = atoi(p); |
| *ptrS = s; |
| return true; |
| } |
| |
| class TestPictureSize : public AfterStartPreview { |
| public: |
| void checkOnePicture(int w, int h) { |
| const float rate = 0.9; // byte per pixel limit |
| int pixels = w * h; |
| |
| CameraParameters param(c->getParameters()); |
| param.setPictureSize(w, h); |
| // disable thumbnail to get more accurate size. |
| param.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, 0); |
| param.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, 0); |
| c->setParameters(param.flatten()); |
| |
| cc->clearStat(); |
| ASSERT(c->takePicture() == NO_ERROR); |
| cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); |
| //cc->assertDataSize(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, pixels*3/2); |
| cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); |
| cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::LT, |
| int(pixels * rate)); |
| cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::GT, 0); |
| cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); |
| } |
| |
| void run() { |
| CameraParameters param(c->getParameters()); |
| int w, h; |
| const char *s = param.get(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES); |
| while (getNextSize(&s, &w, &h)) { |
| ALOGD("checking picture size %dx%d", w, h); |
| checkOnePicture(w, h); |
| } |
| } |
| }; |
| |
| class TestPreviewCallbackFlag : public AfterConnect { |
| public: |
| void run() { |
| sp<MSurface> surface = new MSurface(); |
| ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); |
| |
| // Try all flag combinations. |
| for (int v = 0; v < 8; v++) { |
| ALOGD("TestPreviewCallbackFlag: flag=%d", v); |
| usleep(100000); // sleep a while to clear the in-flight callbacks. |
| cc->clearStat(); |
| c->setPreviewCallbackFlag(v); |
| ASSERT(c->previewEnabled() == false); |
| ASSERT(c->startPreview() == NO_ERROR); |
| ASSERT(c->previewEnabled() == true); |
| sleep(2); |
| c->stopPreview(); |
| if ((v & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) == 0) { |
| cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 0); |
| } else { |
| if ((v & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) == 0) { |
| cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 10); |
| } else { |
| cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 1); |
| } |
| } |
| } |
| } |
| }; |
| |
| class TestRecording : public AfterConnect { |
| public: |
| void run() { |
| ASSERT(c->recordingEnabled() == false); |
| sp<MSurface> surface = new MSurface(); |
| ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); |
| c->setPreviewCallbackFlag(CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK); |
| cc->setReleaser(c.get()); |
| c->startRecording(); |
| ASSERT(c->recordingEnabled() == true); |
| sleep(2); |
| c->stopRecording(); |
| usleep(100000); // sleep a while to clear the in-flight callbacks. |
| cc->setReleaser(NULL); |
| cc->assertData(CAMERA_MSG_VIDEO_FRAME, MCameraClient::GE, 10); |
| } |
| }; |
| |
| class TestPreviewSize : public AfterStartPreview { |
| public: |
| void checkOnePicture(int w, int h) { |
| int size = w*h*3/2; // should read from parameters |
| |
| c->stopPreview(); |
| |
| CameraParameters param(c->getParameters()); |
| param.setPreviewSize(w, h); |
| c->setPreviewCallbackFlag(CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK); |
| c->setParameters(param.flatten()); |
| |
| c->startPreview(); |
| |
| cc->clearStat(); |
| cc->waitData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 1); |
| cc->assertDataSize(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, size); |
| } |
| |
| void run() { |
| CameraParameters param(c->getParameters()); |
| int w, h; |
| const char *s = param.get(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES); |
| while (getNextSize(&s, &w, &h)) { |
| ALOGD("checking preview size %dx%d", w, h); |
| checkOnePicture(w, h); |
| } |
| } |
| }; |
| |
| void runHolderService() { |
| defaultServiceManager()->addService( |
| String16("CameraServiceTest.Holder"), new HolderService()); |
| ProcessState::self()->startThreadPool(); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| if (argc != 1) { |
| runFunction(argv[1]); |
| return 0; |
| } |
| INFO("CameraServiceTest start"); |
| gExecutable = argv[0]; |
| runHolderService(); |
| int n = getNumberOfCameras(); |
| INFO("%d Cameras available", n); |
| |
| for (int id = 0; id < n; id++) { |
| INFO("Testing camera %d", id); |
| testConnect(id); flushCommands(); |
| testAllowConnectOnceOnly(id); flushCommands(); |
| testReconnect(id); flushCommands(); |
| testLockUnlock(id); flushCommands(); |
| testReconnectFromAnotherProcess(id); flushCommands(); |
| |
| RUN(TestSetPreviewDisplay, id); |
| RUN(TestStartPreview, id); |
| RUN(TestStartPreviewWithoutDisplay, id); |
| RUN(TestAutoFocus, id); |
| RUN(TestStopPreview, id); |
| RUN(TestTakePicture, id); |
| RUN(TestTakeMultiplePictures, id); |
| RUN(TestGetParameters, id); |
| RUN(TestPictureSize, id); |
| RUN(TestPreviewCallbackFlag, id); |
| RUN(TestRecording, id); |
| RUN(TestPreviewSize, id); |
| } |
| |
| INFO("CameraServiceTest finished"); |
| } |