| #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(__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(__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); | 
 |  | 
 |     // 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(__func__); | 
 |     Mutex::Autolock _l(mLock); | 
 |     ++registerBuffersCount; | 
 |     mCond.signal(); | 
 |     return NO_ERROR; | 
 | } | 
 |  | 
 | void MSurface::postBuffer(ssize_t offset) { | 
 |     // INFO(__func__); | 
 |     Mutex::Autolock _l(mLock); | 
 |     ++postBufferCount; | 
 |     mCond.signal(); | 
 | } | 
 |  | 
 | void MSurface::unregisterBuffers() { | 
 |     INFO(__func__); | 
 |     Mutex::Autolock _l(mLock); | 
 |     ++unregisterBuffersCount; | 
 |     mCond.signal(); | 
 | } | 
 |  | 
 | sp<GraphicBuffer> MSurface::requestBuffer(int bufferIdx, int usage) { | 
 |     INFO(__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(__func__); | 
 |     getHolder()->put(obj); | 
 | } | 
 |  | 
 | sp<IBinder> getTempObject() { | 
 |     INFO(__func__); | 
 |     return getHolder()->get(); | 
 | } | 
 |  | 
 | void clearTempObject() { | 
 |     INFO(__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(__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(__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(__func__); | 
 |     sp<ICamera> c = interface_cast<ICamera>(getTempObject()); | 
 |     sp<MCameraClient> cc = new MCameraClient(); | 
 |     ASSERT(c->connect(cc) != NO_ERROR); | 
 | } | 
 |  | 
 | void testReconnectSuccess() { | 
 |     INFO(__func__); | 
 |     sp<ICamera> c = interface_cast<ICamera>(getTempObject()); | 
 |     sp<MCameraClient> cc = new MCameraClient(); | 
 |     ASSERT(c->connect(cc) == NO_ERROR); | 
 |     c->disconnect(); | 
 | } | 
 |  | 
 | void testLockFailed() { | 
 |     INFO(__func__); | 
 |     sp<ICamera> c = interface_cast<ICamera>(getTempObject()); | 
 |     ASSERT(c->lock() != NO_ERROR); | 
 | } | 
 |  | 
 | void testLockUnlockSuccess() { | 
 |     INFO(__func__); | 
 |     sp<ICamera> c = interface_cast<ICamera>(getTempObject()); | 
 |     ASSERT(c->lock() == NO_ERROR); | 
 |     ASSERT(c->unlock() == NO_ERROR); | 
 | } | 
 |  | 
 | void testLockSuccess() { | 
 |     INFO(__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(__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(__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(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"); | 
 | } |