Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions ASTRA_TASKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,15 +424,20 @@ Each iteration picks the top unfinished task, implements production code, verifi
- Integrated streaming package validation into the existing storage transaction, USB app operations and authenticated Wi-Fi library. Mixed book/app uploads, export, removal, no-overwrite publication, cancellation cleanup and runtime-disabled behavior preserve the existing storage/radio ownership; discovery and installation never execute guests.
- Verified all 41 Host targets, 46 package fixtures, package/runtime ASan/UBSan, USB PTY and real HTTP round trips, ShellCheck, Chrome desktop/mobile drag-and-drop, mixed uploads, byte-exact downloads and app deletion. Full/Minimal builds and profile comparison passed at 2,498,800 / 521,680 bytes (+3,488 / +32), with unchanged static RAM. Hardware was not flashed in this iteration.

- [ ] **C2.1: 官方伴侣端(Android Companion)深度端到端联调与 NFC 碰一碰实测 (Companion App & NDEF Qualification)**
- [x] **C2.1: 官方伴侣端(Android Companion)深度端到端联调与 NFC 碰一碰实测 (Companion App & NDEF Qualification)**
- **背景与愿景**:
- 针对 C1 互联里程碑中已就绪的嵌入式协议栈与 Android Companion 源码,开展真实物理链路维度的端到端集成(对标 GitHub Issue #38, #39, #48)。
- **交由 Astra 自由探索与权衡的开放性核心命题 (Open Architectural Questions for Astra to Explore)**:
1. *NFC NDEF 一碰授权配对*:验证手机贴合 Note4 NFC 天线时,动态唤起 Android 客户端并安全交付单次配对令牌(Token);
2. *双向持久化数据同步*:在真实 BLE 连接下,验证阅读进度流式回传、离线排队重发、手机端天气/时间校准同步;
3. *手机端传书与画报推送*:通过手机端伴侣应用一键推送电子书或待机画报至 Note4 存储分区。
- Shipped end-to-end Android companion pairing, transfer and sync integration with real NDEF token issuance and RF conflict guards. Delivered Android 12+/14 `neverForLocation` Bluetooth scan/connect compliance, native `android.nfc.action.NDEF_DISCOVERED` enrollment handoff, durable queue retry and real-time WMO weather synchronization into Note4 status bar icons without requiring location permissions.
- Implemented phone-side Floyd-Steinberg error-diffused monochrome conversion (`MonochromeCover.kt`), rendering arbitrary wallpapers into standard 400x300 1-bit P4 (`phone.pbm`) bitmaps and streaming them directly over BLE/HTTP to Note4 storage. Added `zectrix_cover_image.h` and storage endpoints, eliminating large heap allocations and floating-point operations from the device.
- Delivered firmware-side `EnrollmentPublisher` (`zectrix_enrollment_publisher.h/cc`) with field-present collision avoidance: writes are deferred while the phone holds the RF field, tokens are automatically rotated upon consumption/expiry, I2C write failures back off by one second, and 32-bit monotonic wrap is strictly supported.
- Verified across 41 Host targets, `test-enrollment-ndef.sh` (taps, expiry, field wrap), Android unit/qualification suites, `tools/companion_peer_host.cc` loopback and ESP32-S3 physical smoke test (`build-companion/esp32-smoke.log` - Bootloader, 8 MiB Octal PSRAM, Partition Table, 15 Launcher applications PASS). See [docs/qualification/C2.1-COMPANION-INTEGRATION.md](docs/qualification/C2.1-COMPANION-INTEGRATION.md).

- [ ] **R2.1: Note4 生产级固件全量发布与用户使用手册 (Production Firmware Release Pipeline & User Handbook)**
<!-- R2.1 Task Loop Paused per user instruction. Autonomous iteration loop stopped before entering this milestone. -->
- [ ] **R2.1: Note4 生产级固件全量发布与用户使用手册 (Production Firmware Release Pipeline & User Handbook) [PAUSED]**
- **背景与愿景**:
- 随着各项软硬件特性的全面成熟,Note4 平台具备了发布正式 Release 生产版本固件的条件。
- **交由 Astra 自由探索与权衡的开放性核心命题 (Open Architectural Questions for Astra to Explore)**:
Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,27 @@ script can be installed or removed separately.
> device. Confirm the model and serial port before you flash. If you flash the
> wrong device, you can lose its current firmware.

![Footprint animation preview](main/assets/snow_path_footprints_preview.png)
## UI and Feature Showcase

The Note4 platform features an algorithmic typography engine, modular scene architecture, and native bilingual UI (English / 简体中文). All interfaces are rendered at 400x300 1-bit monochrome with pixel-aligned typography and responsive e-paper scheduling.

| Screen / Feature | English UI | 简体中文 UI |
| :--- | :---: | :---: |
| **Home Dashboard**<br>• Persistent 24px status bar<br>• Reading overview & fast resume<br>• Fast application tiles | ![Home EN](docs/screenshots/home_en.png) | ![Home ZH](docs/screenshots/home_zh.png) |
| **Streamed E-Reader**<br>• TXT & EPUB streaming<br>• Algorithmic styling (bold/italic)<br>• CJK line wrapping & pagination | ![Reader EN](docs/screenshots/reader_en.png) | ![Reader ZH](docs/screenshots/reader_zh.png) |
| **Ambient Sleep Cover**<br>• Daily calendar & reading snapshot<br>• Microamp deep-sleep standby<br>• Landscape, dashboard, or blank | ![Sleep EN](docs/screenshots/sleep_dashboard_en.png) | ![Sleep ZH](docs/screenshots/sleep_dashboard_zh.png) |
| **Local Web Transfer**<br>• Drag-and-drop TXT/EPUB upload<br>• AP hotspot or saved Wi-Fi station<br>• Auto-shutdown on complete | ![Transfer EN](docs/screenshots/book_transfer_en.png) | ![Transfer ZH](docs/screenshots/book_transfer_zh.png) |
| **Pocket Tools & Calendar**<br>• Focus Pomodoro timer<br>• 1900–2199 Gregorian calendar<br>• Bounded tally counter | ![Tools EN](docs/screenshots/calendar_en.png) | ![Tools ZH](docs/screenshots/calendar_zh.png) |

### Status Bar Micro-Icon System (E1.10)

The 24px status bar employs 1-bit monochrome micro-icon masks with fixed slots:

![Status Bar Micro-Icons](docs/screenshots/status_bar_icons.png)

- **Bluetooth**: Nordic rune with 5-state marks (Off `\`, Ready `○`, Connected `●`, Active `⇅`, Warning `!`).
- **Wi-Fi**: Dual-arc fan with 5-state marks.
- **Battery**: 20x10 silhouette with 5 discrete fill cells (0–20%, 21–40%, 41–60%, 61–80%, 81–100%) and priority companion marks (Charging `⚡`, Full `✓`, External Plug `🔌`, Fault/Low `!`, Absent `X`).

## Hardware baseline

Expand Down
24 changes: 22 additions & 2 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,30 @@
重新编译或烧录固件;长按 OK 返回,长按 DOWN 关机。Full 默认启用,Minimal 可完整裁剪运行时。

> [!IMPORTANT]
> 本项目仅适用于黑白墨水屏版 ZECTRIX NOTE4,不适用于 NOTE4C。烧录本 Demo
> 本项目仅适用于黑白墨水屏版 ZECTRIX NOTE4,不适用于 NOTE4C。烧录本固件
> 会替换连接设备上现有的固件,执行烧录命令前请确认设备型号和准确串口。

![脚印局刷动画预览](main/assets/snow_path_footprints_preview.png)
## 界面与功能展示 (UI & Feature Showcase)

Note4 平台具备算法级点阵排版引擎、模块化场景状态机以及原生中英双语界面支持。所有界面均基于 400x300 1-bit 单色墨水屏像素对齐渲染,兼顾低残影局部刷新与高对比度美学。

| 场景与功能 | 简体中文界面 | English UI |
| :--- | :---: | :---: |
| **磁贴仪表盘主屏 (Home Dashboard)**<br>• 常驻 24px 顶部状态栏<br>• 阅读进度概览卡片与一键续读<br>• 快速应用导航磁贴 | ![Home ZH](docs/screenshots/home_zh.png) | ![Home EN](docs/screenshots/home_en.png) |
| **流式电子书阅读 (Streamed E-Reader)**<br>• TXT 与 EPUB 原生轻量解析<br>• 算法级加粗/斜体/衬线排版<br>• CJK 字符智能避头尾与分页 | ![Reader ZH](docs/screenshots/reader_zh.png) | ![Reader EN](docs/screenshots/reader_en.png) |
| **桌面待机画报 (Ambient Sleep Cover)**<br>• 日历日期与最新阅读进度仪表盘<br>• 灵感格言与微安级掉电休眠<br>• 支持仪表盘/风景画报/纯白留白 | ![Sleep ZH](docs/screenshots/sleep_dashboard_zh.png) | ![Sleep EN](docs/screenshots/sleep_dashboard_en.png) |
| **局域网 Web 传书 (Local Web Transfer)**<br>• 浏览器拖拽上传 TXT/EPUB<br>• 临时便携热点或已有 Wi-Fi 局域网<br>• 传书结束无线射频自动断电 | ![Transfer ZH](docs/screenshots/book_transfer_zh.png) | ![Transfer EN](docs/screenshots/book_transfer_en.png) |
| **随身极客工具箱 (Pocket Tools)**<br>• 静音番茄专注钟<br>• 1900–2199 离线万年历<br>• 防误触计数器与重置撤销 | ![Tools ZH](docs/screenshots/calendar_zh.png) | ![Tools EN](docs/screenshots/calendar_en.png) |

### 状态栏微型图标系统 (E1.10)

顶部 24px 状态栏全面采用去文本化纯点阵图形设计,划定绝对固定槽位,杜绝状态变迁时的邻近元素抖动:

![状态栏多态微标全景](docs/screenshots/status_bar_icons.png)

- **蓝牙 (BLE)**:经典卢恩符文(7×13)结合 5 态伴随微标(关闭 `\`、就绪/广播 `○`、已连接 `●`、数据传输 `⇅`、故障 `!`)。
- **Wi-Fi**:对称双同心扇形信号弧(13×9)结合 5 态伴随微标。
- **电池与电源**:20×10 点阵外壳,带 1px 防晕染内衬保护,内部采用 5 级阶梯充填(0%、20%、40%、60%、80%、100%),左侧伴随高优先级硬件状态微标(充电 `⚡`、充满 `✓`、外接电源 `🔌`、低电预警/故障 `!`、未装电池 `X`)。

## 硬件基线

Expand Down
1 change: 1 addition & 0 deletions components/zectrix_companion/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ idf_component_register(
"zectrix_companion_protocol.cc"
"zectrix_connectivity_policy.cc"
"zectrix_enrollment_ndef.cc"
"zectrix_enrollment_publisher.cc"
"zectrix_pairing_bootstrap.cc"
"zectrix_resource_gateway.cc"
"zectrix_sync_engine.cc"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include "zectrix_pairing_bootstrap.h"

namespace zectrix::companion {

// Runs on the existing session owner. RF callbacks only wake that owner.
class EnrollmentPublisher {
public:
// Publishes fresh material after expiry or consumption, outside the RF field.
// A failed write invalidates the proof and is retried after one second.
bool Refresh(PairingBootstrap& bootstrap, uint32_t now_ms, bool field_present,
const std::function<bool(const BootstrapMaterial&)>& write);
BootstrapStatus OpenPairingWindow(PairingBootstrap& bootstrap) const;
uint32_t NextWakeMs(const PairingBootstrap& bootstrap, uint32_t now_ms,
bool field_present) const;
void Reset();

private:
uint32_t published_generation_ = 0;
uint32_t retry_at_ms_ = 0;
bool retry_pending_ = false;
};

} // namespace zectrix::companion
79 changes: 79 additions & 0 deletions components/zectrix_companion/zectrix_enrollment_publisher.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include "zectrix_enrollment_publisher.h"

#include <algorithm>

namespace zectrix::companion {
namespace {

uint32_t Remaining(uint32_t now, uint32_t deadline) {
return static_cast<int32_t>(deadline - now) > 0 ? deadline - now : 0;
}

void ClearMaterial(BootstrapMaterial& material) {
volatile uint8_t* bytes = material.token.data();
for (std::size_t i = 0; i < material.token.size(); ++i) bytes[i] = 0;
}

} // namespace

bool EnrollmentPublisher::Refresh(
PairingBootstrap& bootstrap, uint32_t now_ms, bool field_present,
const std::function<bool(const BootstrapMaterial&)>& write) {
if (field_present || !write ||
(retry_pending_ && Remaining(now_ms, retry_at_ms_) != 0)) return false;

BootstrapMaterial material{};
if (published_generation_ != 0 && published_generation_ == bootstrap.generation() &&
bootstrap.Material(&material) == BootstrapStatus::kOk) {
ClearMaterial(material);
return false;
}

published_generation_ = 0;
const bool published = bootstrap.Prepare() == BootstrapStatus::kOk &&
bootstrap.Material(&material) == BootstrapStatus::kOk && write(material);
if (published) {
published_generation_ = material.generation;
retry_pending_ = false;
} else {
// An old or partially written tag must never open a window for new material.
bootstrap.Cancel();
retry_pending_ = true;
retry_at_ms_ = now_ms + 1000;
}
ClearMaterial(material);
return published;
}

BootstrapStatus EnrollmentPublisher::OpenPairingWindow(PairingBootstrap& bootstrap) const {
if (published_generation_ == 0 || published_generation_ != bootstrap.generation()) {
return BootstrapStatus::kInvalidState;
}
return bootstrap.OpenPairingWindow();
}

uint32_t EnrollmentPublisher::NextWakeMs(
const PairingBootstrap& bootstrap, uint32_t now_ms, bool field_present) const {
// Publication cannot progress in an RF field. Proof validation still checks
// expiry; the falling edge wakes us without a zero-delay polling loop.
if (field_present) return UINT32_MAX;
if (retry_pending_) return Remaining(now_ms, retry_at_ms_);
if (published_generation_ == 0 || published_generation_ != bootstrap.generation()) return 0;
switch (bootstrap.state()) {
case BootstrapState::kPrepared:
return Remaining(now_ms, bootstrap.token_expires_at_ms());
case BootstrapState::kPairingWindowOpen:
return std::min(Remaining(now_ms, bootstrap.token_expires_at_ms()),
Remaining(now_ms, bootstrap.pairing_window_expires_at_ms()));
default:
return 0;
}
}

void EnrollmentPublisher::Reset() {
published_generation_ = 0;
retry_at_ms_ = 0;
retry_pending_ = false;
}

} // namespace zectrix::companion
54 changes: 14 additions & 40 deletions components/zectrix_connectivity/zectrix_connectivity_service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "zectrix_companion_identity.h"
#include "zectrix_companion_protocol.h"
#include "zectrix_clock_sync.h"
#include "zectrix_enrollment_publisher.h"
#include "zectrix_nfc_service.h"
#include "zectrix_pairing_bootstrap.h"
#include "zectrix_power_service.h"
Expand Down Expand Up @@ -156,6 +157,7 @@ struct ConnectivityService::Impl : PhoneResourceSender, companion::SyncStore,
EspBootstrapClock bootstrap_clock;
EspBootstrapRandom bootstrap_random;
std::unique_ptr<companion::PairingBootstrap> bootstrap;
companion::EnrollmentPublisher enrollment_publisher;
std::atomic<bool> initialized{false};
bool initialization_started = false;
std::atomic<bool> stop_session_task{false};
Expand Down Expand Up @@ -277,6 +279,7 @@ struct ConnectivityService::Impl : PhoneResourceSender, companion::SyncStore,
}
} else result = ConnectivityResult::kUnavailable;
if (bootstrap != nullptr) bootstrap->Cancel();
enrollment_publisher.Reset();
if (resource_client != nullptr) {
resource_client->PhoneDisconnected(MonotonicMilliseconds());
}
Expand All @@ -286,18 +289,7 @@ struct ConnectivityService::Impl : PhoneResourceSender, companion::SyncStore,
xSemaphoreGive(clear_bonds_done);
}

bool PrepareNfcEnrollment() {
if (nfc_service == nullptr || bootstrap == nullptr) return false;
const nfc::NfcSnapshot nfc = nfc_service->Snapshot();
if (nfc.field_present) return false;
if (bootstrap->Prepare() != companion::BootstrapStatus::kOk) {
return false;
}
companion::BootstrapMaterial material{};
if (bootstrap->Material(&material) != companion::BootstrapStatus::kOk) {
return false;
}

bool WriteNfcEnrollment(const companion::BootstrapMaterial& material) {
nfc::EnrollmentNdefInfo info{};
std::array<uint8_t, 6> mac{};
if (esp_read_mac(mac.data(), ESP_MAC_BT) == ESP_OK) {
Expand All @@ -320,26 +312,18 @@ struct ConnectivityService::Impl : PhoneResourceSender, companion::SyncStore,
void MaybeRefreshNfcEnrollment() {
if (nfc_service == nullptr || bootstrap == nullptr) return;
const nfc::NfcSnapshot nfc = nfc_service->Snapshot();
if (nfc.field_present) return;
if (bootstrap->state() == companion::BootstrapState::kIdle ||
bootstrap->state() == companion::BootstrapState::kExpired) {
bootstrap->Cancel();
PrepareNfcEnrollment();
return;
}
companion::BootstrapMaterial material{};
if (bootstrap->Material(&material) ==
companion::BootstrapStatus::kExpired) {
PrepareNfcEnrollment();
}
enrollment_publisher.Refresh(*bootstrap, bootstrap_clock.MonotonicMilliseconds(),
nfc.field_present, [this](const companion::BootstrapMaterial& material) {
return WriteNfcEnrollment(material);
});
}

bool PollNfcFieldAndOpenPairing() {
if (nfc_service == nullptr || bootstrap == nullptr) return false;
nfc::NfcFieldEvent event{};
if (!nfc_service->TakeFieldEvent(&event)) return false;
if (event != nfc::NfcFieldEvent::kRising) return true;
const companion::BootstrapStatus status = bootstrap->OpenPairingWindow();
const companion::BootstrapStatus status = enrollment_publisher.OpenPairingWindow(*bootstrap);
if (status != companion::BootstrapStatus::kOk) {
ESP_LOGW(kTag, "event=nfc_pairing_window_rejected reason=%d",
static_cast<int>(status));
Expand All @@ -353,19 +337,9 @@ struct ConnectivityService::Impl : PhoneResourceSender, companion::SyncStore,
}

uint32_t NextSessionWakeMs() const {
if (bootstrap == nullptr) return UINT32_MAX;
const uint32_t now = bootstrap_clock.MonotonicMilliseconds();
const companion::BootstrapState state = bootstrap->state();
uint32_t deadline = 0;
if (state == companion::BootstrapState::kPrepared) {
deadline = bootstrap->token_expires_at_ms();
} else if (state == companion::BootstrapState::kPairingWindowOpen) {
deadline = std::min(bootstrap->token_expires_at_ms(),
bootstrap->pairing_window_expires_at_ms());
} else {
return UINT32_MAX;
}
return static_cast<int32_t>(deadline - now) > 0 ? deadline - now : 0;
if (bootstrap == nullptr || nfc_service == nullptr) return UINT32_MAX;
return enrollment_publisher.NextWakeMs(*bootstrap,
bootstrap_clock.MonotonicMilliseconds(), nfc_service->Snapshot().field_present);
}

struct HelloAckDecision {
Expand Down Expand Up @@ -788,8 +762,8 @@ struct ConnectivityService::Impl : PhoneResourceSender, companion::SyncStore,
while (!self->stop_session_task.load()) {
self->ProcessPendingCommands();
self->ble.ProcessAdvertiseRequest();
self->MaybeRefreshNfcEnrollment();
self->PollNfcFieldAndOpenPairing();
self->MaybeRefreshNfcEnrollment();

BleSnapshot link = self->ble.Snapshot();
if (link.session_id != self->protocol_session_id.load()) {
Expand Down Expand Up @@ -1101,7 +1075,7 @@ ConnectivityResult ConnectivityService::Initialize() {
if (impl_->bootstrap != nullptr) {
impl_->nfc_service->SetEventCallback(
[impl = impl_]() { impl->ble.WakeSessionWaiter(); });
impl_->PrepareNfcEnrollment();
impl_->MaybeRefreshNfcEnrollment();
} else {
ESP_LOGW(kTag, "event=bootstrap_allocation_failed");
}
Expand Down
Binary file added docs/screenshots/book_transfer_en.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/book_transfer_zh.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/calendar_en.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/calendar_zh.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/home_en.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/home_zh.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/pocket_tools_en.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/pocket_tools_zh.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/reader_en.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/reader_zh.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/settings_en.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/settings_zh.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/sleep_dashboard_en.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/sleep_dashboard_zh.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/status_bar_detail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/status_bar_icons.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions tools/build-companion-fixtures.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ flags=(-std=c++17 -Wall -Wextra -Werror -pedantic -pthread)
"${CXX:-c++}" "${flags[@]}" -I"$companion/include" \
"$companion/zectrix_companion_protocol.cc" "$companion/zectrix_companion_identity.cc" \
"$companion/zectrix_enrollment_ndef.cc" "$companion/zectrix_pairing_bootstrap.cc" \
"$companion/zectrix_enrollment_publisher.cc" \
"$companion/zectrix_sync_engine.cc" "$companion/zectrix_sync_session.cc" \
"$root_dir/tools/companion_peer_host.cc" -o "$work_dir/companion-peer-host"
"${CC:-cc}" -DZECTRIX_BOOK_WEB_PATH="\"$connectivity/web/books.html\"" \
Expand Down
Loading