// SPDX-License-Identifier: MPL-2.0 // RAM-only diagnostic reproducing Gecko's Android 6 GonkBufferQueue path. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ANDROID_VERSION 23 #include "GonkBufferQueueLL.h" #include "GonkBufferQueueProducer.h" class CameraEvents : public android::CameraListener { public: void notify(int32_t type, int32_t ext1, int32_t ext2) override { fprintf(stderr, "camera_notify=%d,%d,%d\n", type, ext1, ext2); } void postData(int32_t type, const android::sp& memory, camera_frame_metadata_t*) override { if (memory == nullptr) return; const size_t size = memory->size(); const unsigned char* bytes = static_cast(memory->pointer()); unsigned int low = 255, high = 0; uint64_t sum = 0; const size_t ySize = size < 640u * 480u ? size : 640u * 480u; for (size_t i = 0; i < ySize; ++i) { if (bytes[i] < low) low = bytes[i]; if (bytes[i] > high) high = bytes[i]; sum += bytes[i]; } fprintf(stderr, "camera_data=%d size=%zu y=%u..%u mean=%.2f\n", type, size, low, high, ySize ? (double)sum / ySize : 0); static bool saved = false; if (type == CAMERA_MSG_COMPRESSED_IMAGE) { FILE* jpeg = fopen("/data/local/tmp/vulpes-camera-callback.jpg", "wb"); if (jpeg) { fwrite(bytes, 1, size, jpeg); fclose(jpeg); chmod("/data/local/tmp/vulpes-camera-callback.jpg", 0644); } fprintf(stderr, "captured_jpeg=%zu\n", size); } else if (!saved) { FILE* output = fopen("/data/local/tmp/vulpes-camera-callback.yuv", "wb"); if (output) { fwrite(bytes, 1, size, output); fclose(output); chmod("/data/local/tmp/vulpes-camera-callback.yuv", 0644); saved = true; } } } void postDataTimestamp(nsecs_t, int32_t type, const android::sp&) override { fprintf(stderr, "camera_timestamp_data=%d\n", type); } }; class QueueListener : public android::ConsumerListener { public: void onFrameAvailable(const android::BufferItem&) override {} void onFrameReplaced(const android::BufferItem&) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} }; int main() { setvbuf(stderr, nullptr, _IONBF, 0); android::ProcessState::self()->startThreadPool(); const int count = android::Camera::getNumberOfCameras(); fprintf(stderr, "camera_count=%d uid=%d\n", count, getuid()); if (count < 1) return 2; android::sp camera = android::Camera::connect( // Match GonkCameraHwMgr.cpp exactly. CameraService/AppOps on this // firmware treats the client package as part of camera authorization. 0, android::String16("gonk.camera"), android::Camera::USE_CALLING_UID); if (camera == nullptr) return 3; android::CameraParameters params(camera->getParameters()); android::Vector previews, pictures; params.getSupportedPreviewSizes(previews); params.getSupportedPictureSizes(pictures); android::Size picture = {0, 0}; for (size_t i = 0; i < pictures.size(); ++i) if (pictures[i].width * pictures[i].height > picture.width * picture.height) picture = pictures[i]; android::Size preview = {0, 0}; uint64_t bestDelta = UINT64_MAX; for (size_t i = 0; i < previews.size(); ++i) { const android::Size candidate = previews[i]; if (picture.width && candidate.width * picture.height != picture.width * candidate.height) continue; const uint64_t area = (uint64_t)candidate.width * candidate.height; const uint64_t delta = area > 640u * 480u ? area - 640u * 480u : 640u * 480u - area; if (delta < bestDelta) { preview = candidate; bestDelta = delta; } } if (!preview.width && previews.size()) preview = previews[0]; if (!preview.width || !picture.width) { fprintf(stderr, "no supported preview/picture size (preview=%zu picture=%zu)\n", previews.size(), pictures.size()); camera->disconnect(); return 4; } params.setPreviewSize(preview.width, preview.height); params.setPictureSize(picture.width, picture.height); // Gecko applies its recording hint after selecting still/preview sizes and // before starting the preview. Keep the probe's Camera1 state in step. params.set("recording-hint", "false"); android::String8 configured = params.flatten(); android::status_t status = camera->setParameters(configured); fprintf(stderr, "set_parameters=%d requested_preview=%dx%d picture=%dx%d\n", status, preview.width, preview.height, picture.width, picture.height); if (status != android::NO_ERROR) { camera->disconnect(); return 4; } params.unflatten(camera->getParameters()); int width = 0, height = 0; params.getPreviewSize(&width, &height); int pictureWidth = 0, pictureHeight = 0; params.getPictureSize(&pictureWidth, &pictureHeight); fprintf(stderr, "preview=%dx%d picture=%dx%d format=%s\n", width, height, pictureWidth, pictureHeight, params.getPreviewFormat()); android::sp producer; android::sp consumer; android::sp allocator = new android::GraphicBufferAlloc(); android::GonkBufferQueue::createBufferQueue(&producer, &consumer, allocator); status = static_cast(producer.get()) ->setSynchronousMode(false); fprintf(stderr, "async_producer=%d\n", status); status = consumer->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN); fprintf(stderr, "cpu_read_usage=%d\n", status); status = consumer->setMaxAcquiredBufferCount(4); fprintf(stderr, "max_acquired=%d\n", status); android::sp listener = new QueueListener(); android::sp proxy = new android::GonkBufferQueue::ProxyConsumerListener(listener); status = consumer->consumerConnect(proxy, false); fprintf(stderr, "consumer_connect=%d\n", status); android::sp cameraEvents = new CameraEvents(); camera->setListener(cameraEvents); status = camera->setPreviewTarget(producer); fprintf(stderr, "set_preview_target=%d\n", status); camera->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_CAMERA); fprintf(stderr, "preview_callback_flags=%d\n", CAMERA_FRAME_CALLBACK_FLAG_CAMERA); const android::status_t focusEvents = camera->sendCommand( CAMERA_CMD_ENABLE_FOCUS_MOVE_MSG, 1, 0); fprintf(stderr, "focus_move_events=%d\n", focusEvents); status = camera->startPreview(); fprintf(stderr, "start_preview=%d\n", status); if (status) return 4; unsigned acquired = 0; bool saved = false; const int64_t deadline = systemTime(SYSTEM_TIME_MONOTONIC) + 5000000000LL; while (systemTime(SYSTEM_TIME_MONOTONIC) < deadline) { android::IGonkGraphicBufferConsumer::BufferItem item; status = consumer->acquireBuffer(&item, 0); if (status != android::NO_ERROR) { usleep(20000); continue; } ++acquired; android::sp buffer = item.mGraphicBuffer; if (item.mFence != nullptr) item.mFence->waitForever("vulpes-gonk-camera"); void* pixels = nullptr; if (buffer != nullptr && buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &pixels) == android::NO_ERROR && pixels != nullptr) { const size_t bytes = (size_t)buffer->getStride() * buffer->getHeight() * 3 / 2; const unsigned char* y = static_cast(pixels); unsigned int minimum = 255, maximum = 0; uint64_t sum = 0; const size_t yBytes = (size_t)buffer->getStride() * buffer->getHeight(); for (size_t i = 0; i < yBytes; ++i) { if (y[i] < minimum) minimum = y[i]; if (y[i] > maximum) maximum = y[i]; sum += y[i]; } fprintf(stderr, "frame=%u number=%llu timestamp=%llu y=%u..%u mean=%.2f format=%d\n", acquired, (unsigned long long)item.mFrameNumber, (unsigned long long)item.mTimestamp, minimum, maximum, (double)sum / yBytes, buffer->format); if (!saved || acquired % 10 == 0) { FILE* output = fopen("/data/local/tmp/vulpes-camera-gonkqueue.nv21", "wb"); if (output) { fwrite(pixels, 1, bytes, output); fclose(output); chmod("/data/local/tmp/vulpes-camera-gonkqueue.nv21", 0644); FILE* meta = fopen("/data/local/tmp/vulpes-camera-gonkqueue.txt", "w"); if (meta) { fprintf(meta, "%u %u %u %d %zu %u %u %u %u\n", buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->format, bytes, acquired, minimum, maximum, (unsigned)(sum / yBytes)); fclose(meta); chmod("/data/local/tmp/vulpes-camera-gonkqueue.txt", 0644); } saved = true; } } buffer->unlock(); } consumer->releaseBuffer(item.mBuf, item.mFrameNumber, android::Fence::NO_FENCE); } fprintf(stderr, "acquired=%u saved=%d; taking_still\n", acquired, saved); status = camera->takePicture(CAMERA_MSG_SHUTTER | CAMERA_MSG_COMPRESSED_IMAGE); fprintf(stderr, "take_picture=%d\n", status); sleep(3); fprintf(stderr, "stopping_preview\n"); camera->stopPreview(); fprintf(stderr, "camera_stopped; disconnecting\n"); camera->disconnect(); consumer->consumerDisconnect(); fprintf(stderr, "done\n"); return saved ? 0 : 5; }