|  | #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 <surfaceflinger/ISurface.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 <ui/Overlay.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"); \ | 
|  | LOGD(__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)) { | 
|  | LOGE("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<OverlayRef> createOverlay( | 
|  | uint32_t w, uint32_t h, int32_t format, int32_t orientation); | 
|  | 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | sp<OverlayRef> MSurface::createOverlay(uint32_t w, uint32_t h, int32_t format, | 
|  | int32_t orientation) { | 
|  | // Not implemented. | 
|  | ASSERT(0); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | // | 
|  | //  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)) { | 
|  | LOGD("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++) { | 
|  | LOGD("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 & FRAME_CALLBACK_FLAG_ENABLE_MASK) == 0) { | 
|  | cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 0); | 
|  | } else { | 
|  | if ((v & 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(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(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)) { | 
|  | LOGD("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"); | 
|  | } |