Update shared Gaia workflows and Sargo development tooling

Fix camera switching and Contacts/SMS activity routing across shared hosts.
Integrate Marketplace installation and native standalone recovery tools,
Bluetooth runtime packages and trusted certificates.

Update English/French READMEs and the shared changelog with tested scope
and remaining radio/audio/Bluetooth limitations. Preview17 remains the
existing APK; no new native image is released.

Validation: shared JavaScript and native Python tests; Android packaging
and payload parity from an isolated publication checkout.
This commit is contained in:
OverSu 2026-10-08 14:27:17 +02:00
commit 0041cd9660
101 changed files with 3883 additions and 335 deletions

View file

@ -0,0 +1,80 @@
# SPDX-License-Identifier: MPL-2.0
import importlib.util
from pathlib import Path
import struct
import sys
import tempfile
import unittest
from unittest.mock import patch
spec=importlib.util.spec_from_file_location('flash',Path(__file__).resolve().parents[1]/'tools/flash-standalone-sargo.py')
flash=importlib.util.module_from_spec(spec);spec.loader.exec_module(flash)
class FlashTests(unittest.TestCase):
def plan(self):
return {'userdataBytes':8*1024**3,'partitions':{name:{'bytes':4096} for name in flash.PARTITIONS}}
def values(self,**changes):
values={'product':'sargo','unlocked':'yes','current-slot':'b',
**{'partition-size:'+name:hex(64*1024**3 if name=='userdata' else 64*1024**2) for name in flash.PARTITIONS}}
values.update(changes);return values
def test_both_slots_accepted_for_full_install(self):
for slot in ('a','b'):
values=self.values(**{'current-slot':slot})
with patch.object(flash,'getvar',side_effect=lambda _,__,name:values[name]):
self.assertEqual(flash.preflight('fastboot','serial',self.plan())['current-slot'],slot)
def test_wrong_model_locked_or_small_partition_rejected(self):
for change in ({'product':'bonito'},{'unlocked':'no'},{'partition-size:userdata':'0x1000'}):
values=self.values(**change)
with patch.object(flash,'getvar',side_effect=lambda _,__,name:values[name]):
with self.assertRaises(ValueError):flash.preflight('fastboot','serial',self.plan())
def test_review_never_contacts_device(self):
with patch.object(sys,'argv',['flash']),patch.object(flash,'package',return_value=self.plan()),patch.object(flash.subprocess,'run') as run,patch.object(flash.subprocess,'check_output') as read:
flash.main();run.assert_not_called();read.assert_not_called()
def test_install_requires_explicit_userdata_replacement(self):
with patch.object(sys,'argv',['flash','--install']),patch.object(flash,'package',return_value=self.plan()),patch.object(flash.subprocess,'run') as run:
with self.assertRaises(SystemExit):flash.main()
run.assert_not_called()
def test_unqualified_package_requires_experimental_flag(self):
with patch.object(sys,'argv',['flash','--install','--erase-userdata']),patch.object(flash,'package',return_value=self.plan()),patch.object(flash.subprocess,'run') as run:
with self.assertRaises(SystemExit):flash.main()
run.assert_not_called()
def test_sparse_geometry(self):
with tempfile.TemporaryDirectory() as directory:
image=Path(directory)/'userdata.img'
image.write_bytes(struct.pack('<I4H4I',0xed26ff3a,1,0,28,12,4096,2097152,3,0))
self.assertEqual(flash.sparse_size(image),8*1024**3)
image.write_bytes(b'bad')
with self.assertRaises(ValueError):flash.sparse_size(image)
def test_incomplete_package_is_rejected_before_manifest_read(self):
with tempfile.TemporaryDirectory() as directory:
(Path(directory)/'.incomplete').touch()
with self.assertRaisesRegex(ValueError,'construction is incomplete'):
flash.package(directory)
def test_install_writes_only_planned_partitions_after_preflight(self):
with tempfile.TemporaryDirectory() as directory:
argv=['flash','--package',directory,'--install','--erase-userdata','--experimental']
with patch.object(sys,'argv',argv),patch.object(flash,'package',return_value=self.plan()), \
patch.object(flash.shutil,'which',return_value='fastboot'), \
patch.object(flash.subprocess,'check_output',return_value='test-device fastboot\n'), \
patch.object(flash,'preflight',return_value={'product':'sargo'}) as preflight, \
patch.object(flash.subprocess,'run') as run:
flash.main()
preflight.assert_called_once()
commands=[call.args[0][3:] for call in run.call_args_list]
self.assertEqual(commands[0],['erase','userdata'])
self.assertEqual([command[:2] for command in commands[1:-1]],
[['flash',partition] for partition in flash.PARTITIONS])
self.assertEqual(commands[-1],['set_active','a'])
if __name__=='__main__':unittest.main()