673b56b41f
Change-Id: I80b498d1bbb8f9ffcec4f27802be7ae9977f70ac
920 lines
25 KiB
C++
920 lines
25 KiB
C++
#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);
|
|
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(__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;
|
|
}
|
|
|
|
status_t MSurface::setBufferCount(int bufferCount) {
|
|
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");
|
|
}
|