Release Vulpes OS 2.7-preview.24

This commit is contained in:
OverSu 2026-11-04 12:52:20 +01:00
commit 97ef99f827
135 changed files with 7717 additions and 2524 deletions

View file

@ -0,0 +1,214 @@
// SPDX-License-Identifier: MPL-2.0
// CameraService worker for the Nokia 2720 Flip. The parent broker drops this
// process to Android's camera UID before exec. Gaia talks to it over a local
// socket; it never receives Binder or elevated credentials.
#include <camera/Camera.h>
#include <camera/CameraParameters.h>
#include <binder/ProcessState.h>
#include <gui/BufferQueue.h>
#include <gui/CpuConsumer.h>
#include <gui/GraphicBufferAlloc.h>
#include <hardware/gralloc.h>
#include <utils/Timers.h>
#include <utils/Mutex.h>
#include <utils/String8.h>
#include <utils/String16.h>
#include <condition_variable>
#include <chrono>
#include <atomic>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <unistd.h>
#include <sys/socket.h>
namespace {
bool write_all(int fd, const void *data, size_t size) {
const char *p = static_cast<const char *>(data);
while (size) {
ssize_t n = write(fd, p, size);
if (n <= 0) return false;
p += n; size -= static_cast<size_t>(n);
}
return true;
}
bool read_line(int fd, std::string *line) {
line->clear();
char c;
while (line->size() < 2048) {
ssize_t n = read(fd, &c, 1);
if (n <= 0) return false;
if (c == '\n') return true;
line->push_back(c);
}
return false;
}
bool send_json(int fd, const std::string &json, bool frame=false,
uint32_t sequence=0, const std::vector<uint8_t> *bytes=nullptr) {
uint32_t length = static_cast<uint32_t>(json.size());
if (frame) {
uint32_t header[2] = {sequence, bytes ? static_cast<uint32_t>(bytes->size()) : 0};
return write_all(fd, header, sizeof(header)) && (!bytes || write_all(fd,bytes->data(),bytes->size()));
}
return write_all(fd,&length,sizeof(length)) && write_all(fd,json.data(),json.size());
}
class CameraWorker : public android::CameraListener {
public:
explicit CameraWorker(int fd) : fd_(fd), camera_id_(0), sequence_(0), stopping_(false) {}
void notify(int32_t type, int32_t first, int32_t second) override {
fprintf(stderr,"camera notify type=%d first=%d second=%d\n",type,first,second);
}
void postData(int32_t type, const android::sp<android::IMemory> &data,
camera_frame_metadata_t *) override {
if (data == nullptr) return;
if (type == CAMERA_MSG_COMPRESSED_IMAGE) {
std::lock_guard<std::mutex> lock(mutex_);
jpeg_.assign(static_cast<const uint8_t *>(data->pointer()),
static_cast<const uint8_t *>(data->pointer()) + data->size());
ready_.notify_all();
return;
}
if (type != CAMERA_MSG_PREVIEW_FRAME) return;
const size_t expected = 320u * 240u * 3u / 2u;
if (data->size() < expected) return;
{
std::lock_guard<std::mutex> lock(mutex_);
frame_.assign(static_cast<const uint8_t *>(data->pointer()),
static_cast<const uint8_t *>(data->pointer()) + expected);
++sequence_;
if(sequence_==1) fprintf(stderr,"camera first frame bytes=%zu uid=%d\n",frame_.size(),getuid());
}
ready_.notify_all();
}
void postDataTimestamp(nsecs_t, int32_t,
const android::sp<android::IMemory> &) override {}
int run() {
android::ProcessState::self()->startThreadPool();
std::string line;
while (read_line(fd_, &line)) {
if (line == "start" || line == "start 0") {
if (!start()) { send_json(fd_,"{\"ready\":false,\"error\":\"CAMERA_START_FAILED\"}"); continue; }
if (!send_json(fd_,"{\"ready\":true,\"width\":320,\"height\":240,\"format\":\"nv21\",\"orientation\":270,\"flashModes\":[\"off\",\"auto\",\"on\",\"torch\"],\"pictureSizes\":[{\"width\":1600,\"height\":1200}],\"focusModes\":[\"fixed\"],\"maxFocusAreas\":0,\"zoomRatios\":[1] }")) return 1;
} else if (line == "frame") {
std::vector<uint8_t> frame;
uint32_t seq;
{
std::unique_lock<std::mutex> lock(mutex_);
const uint32_t previous = delivered_;
ready_.wait_for(lock,std::chrono::seconds(2),[&]{return sequence_>previous || stopping_;});
if (sequence_<=previous || frame_.empty()) { if(!send_json(fd_,"",true,sequence_,nullptr)) return 1; continue; }
seq=sequence_; delivered_=seq; frame=frame_;
}
if (!send_json(fd_,"",true,seq,&frame)) return 1;
} else if (line == "stop") {
stop(); send_json(fd_,"{\"ok\":true}"); return 0;
} else if (line.find("\"action\":\"flash\"") != std::string::npos) {
const size_t key=line.find("\"mode\":\"");
const size_t begin=key==std::string::npos?key:key+8;
const size_t end=begin==std::string::npos?begin:line.find('"',begin);
if(begin==std::string::npos || end==std::string::npos) {
send_json(fd_,"{\"ok\":false,\"error\":\"CAMERA_INVALID_FLASH_MODE\"}");continue;
}
const std::string mode=line.substr(begin,end-begin);
android::CameraParameters params(camera_->getParameters());
params.set(android::CameraParameters::KEY_FLASH_MODE,mode.c_str());
const android::status_t status=camera_->setParameters(params.flatten());
if(status==android::NO_ERROR) {
camera_->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_CAMERA);
camera_->startPreview();
send_json(fd_,"{\"ok\":true,\"result\":true}");
}
else send_json(fd_,"{\"ok\":false,\"error\":\"CAMERA_FLASH_UNAVAILABLE\"}");
} else if (line.find("\"action\":\"zoom\"") != std::string::npos) {
// This module reports a single zoom ratio (1x), so expose a stable
// no-op instead of asking Camera1 to select an unsupported index.
send_json(fd_,"{\"ok\":true,\"result\":{\"zoom\":1}}");
} else if (line.find("\"action\":\"picture\"") != std::string::npos) {
{ std::lock_guard<std::mutex> lock(mutex_); jpeg_.clear(); }
const android::status_t status=camera_->takePicture(CAMERA_MSG_COMPRESSED_IMAGE);
std::vector<uint8_t> jpeg;
{
std::unique_lock<std::mutex> lock(mutex_);
ready_.wait_for(lock,std::chrono::seconds(12),[&]{return !jpeg_.empty() || stopping_;});
jpeg=jpeg_;
}
camera_->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_CAMERA);
camera_->startPreview();
if(status!=android::NO_ERROR || jpeg.empty()) {
send_json(fd_,"{\"ok\":false,\"error\":\"CAMERA_CAPTURE_FAILED\"}");continue;
}
char reply[128];
std::snprintf(reply,sizeof(reply),"{\"ok\":true,\"jpegLength\":%zu}",jpeg.size());
if(!send_json(fd_,reply) || !write_all(fd_,jpeg.data(),jpeg.size())) return 1;
} else {
send_json(fd_,"{\"ok\":false,\"error\":\"CAMERA_UNSUPPORTED_COMMAND\"}");
}
}
stop(); return 0;
}
private:
bool start() {
if (camera_.get() != nullptr) stop();
fprintf(stderr,"camera count=%d uid=%d\n",android::Camera::getNumberOfCameras(),getuid());
camera_ = android::Camera::connect(camera_id_,android::String16("gonk.camera"),android::Camera::USE_CALLING_UID);
if (camera_.get() == nullptr) {fprintf(stderr,"camera connect failed\n");return false;}
android::CameraParameters params(camera_->getParameters());
params.setPreviewSize(320,240); params.setPictureSize(1600,1200);
params.setPreviewFormat("yuv420sp"); params.set("recording-hint","false");
android::status_t status=camera_->setParameters(params.flatten());
fprintf(stderr,"camera set parameters=%d\n",status);
if (status != android::NO_ERROR) { stop(); return false; }
android::sp<android::IGraphicBufferProducer> producer;
android::sp<android::IGraphicBufferConsumer> consumer;
android::sp<android::GraphicBufferAlloc> allocator = new android::GraphicBufferAlloc();
android::BufferQueue::createBufferQueue(&producer,&consumer,allocator);
consumer->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_RENDER);
consumer->setDefaultBufferSize(320,240);
consumer_ = new android::CpuConsumer(consumer,4);
consumer_->setName(android::String8("Vulpes Camera"));
status=camera_->setPreviewTarget(producer);
fprintf(stderr,"camera set target=%d\n",status);
if (status != android::NO_ERROR) { stop(); return false; }
camera_->setListener(this);
// The broker consumes copied Camera1 preview frames rather than video metadata.
camera_->setPreviewCallbackFlags(CAMERA_FRAME_CALLBACK_FLAG_CAMERA);
status=camera_->startPreview(); fprintf(stderr,"camera start preview=%d\n",status);
if (status != android::NO_ERROR) { stop(); return false; }
stopping_=false; delivered_=sequence_;
consumer_thread_=std::thread([this]{
while(!stopping_) {
android::CpuConsumer::LockedBuffer buffer;
if(consumer_->lockNextBuffer(&buffer)==android::NO_ERROR) consumer_->unlockBuffer(buffer);
else usleep(10000);
}
});
return true;
}
void stop() {
{ std::lock_guard<std::mutex> lock(mutex_); stopping_=true; }
ready_.notify_all();
if (camera_.get() != nullptr) { camera_->stopPreview(); camera_->disconnect(); camera_.clear(); }
if(consumer_thread_.joinable()) consumer_thread_.join();
consumer_.clear();
}
int fd_; int camera_id_;
android::sp<android::Camera> camera_;
android::sp<android::CpuConsumer> consumer_;
std::mutex mutex_; std::condition_variable ready_;
std::thread consumer_thread_;
std::vector<uint8_t> frame_, jpeg_; uint32_t sequence_, delivered_; std::atomic<bool> stopping_;
};
}
int main() {
setvbuf(stderr,nullptr,_IONBF,0);
android::sp<CameraWorker> worker = new CameraWorker(STDIN_FILENO);
return worker->run();
}