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.
98 lines
4.5 KiB
Python
98 lines
4.5 KiB
Python
# SPDX-License-Identifier: MPL-2.0
|
|
"""Reduce Android init to display HAL services for the read-only Sargo trial.
|
|
|
|
Input files retain their upstream copyright notices. Removed commands and services
|
|
are reported; this is not a general Android init parser or a production policy.
|
|
"""
|
|
import shlex
|
|
|
|
SERVICES = frozenset(('ueventd','logd','servicemanager','hwservicemanager','vndservicemanager',
|
|
'vendor.hwcomposer-2-2','vendor.qti.hardware.display.allocator','vendor.configstore-hal',
|
|
'vendor.power-hal-1-3','vendor.thermal-engine','thermal-hal-1-1','hidl_memory'))
|
|
COMMANDS = frozenset(('mkdir','chmod','chown','write','setprop','symlink','copy',
|
|
'restorecon','restorecon_recursive','trigger','class_start','class_stop',
|
|
'load_system_props','load_persist_props','export','setrlimit','hostname','domainname','loglevel','sysclktz','start','stop','restart'))
|
|
PSEUDO_FS = frozenset(('cgroup','cgroup2','cpuset','tmpfs','proc','sysfs','debugfs','pstore','bpf'))
|
|
|
|
# The native network trial supplies rmt_storage with RAM copies of NV files.
|
|
# It retains the blanket denial of block-device access for this container.
|
|
NETWORK_SERVICES = frozenset(('irsc_util', 'vendor.per_mgr', 'vendor.rmt_storage',
|
|
'vendor.tftp_server', 'cnss-daemon', 'pd_mapper'))
|
|
|
|
|
|
HARDWARE_SERVICES = frozenset(('vendor.bluetooth-1-0', 'vendor.qcrild', 'vendor.google.radioext@1.0',
|
|
'vendor.audio-hal-2-0', 'vendor.camera-provider-2-4',
|
|
'camera_service', 'minimedia', 'minisf', 'miniaf'))
|
|
|
|
def is_init_script(path):
|
|
# ueventd uses a different grammar: these files define device permissions.
|
|
return not path.name.startswith('ueventd')
|
|
|
|
|
|
def command_allowed(tokens):
|
|
if not tokens:
|
|
return True
|
|
op=tokens[0]
|
|
if op=='onrestart':
|
|
return command_allowed(tokens[1:])
|
|
if op in ('start','stop','restart'):
|
|
return len(tokens)==2 and tokens[1] in SERVICES
|
|
if op=='mount':
|
|
return len(tokens)>3 and tokens[1] in PSEUDO_FS and not tokens[2].startswith('/dev/')
|
|
if op not in COMMANDS:
|
|
return False
|
|
if any('/dev/block' in t or '/dev/mmcblk' in t or '/sys/kernel/config' in t or '/sys/class/android_usb' in t or '/config/usb_gadget' in t or '/proc/sys/kernel/panic' in t or '/proc/sysrq-trigger' in t for t in tokens[1:]):
|
|
return False
|
|
if op=='setprop' and len(tokens)>2 and tokens[1].startswith('ctl.'):
|
|
return tokens[1] in ('ctl.start','ctl.stop','ctl.restart') and tokens[2] in SERVICES
|
|
return True
|
|
|
|
|
|
def logical_lines(text):
|
|
pending = ''
|
|
first = 1
|
|
for number, line in enumerate(text.splitlines(), 1):
|
|
if not pending: first = number
|
|
pending += line
|
|
if pending.endswith('\\'):
|
|
pending = pending[:-1]+' '
|
|
continue
|
|
try:
|
|
shlex.split(pending.strip(), comments=True)
|
|
except ValueError:
|
|
pending += '\n'
|
|
continue
|
|
yield first, pending
|
|
pending = ''
|
|
if pending:
|
|
raise ValueError('Unterminated Android init command at line '+str(first))
|
|
|
|
|
|
def filter_rc(text):
|
|
output=[];removed=[];kept=[];section=None;keep=True
|
|
for number,line in logical_lines(text):
|
|
stripped=line.strip()
|
|
if not stripped or stripped.startswith('#'):
|
|
if keep:output.append(line)
|
|
continue
|
|
tokens=shlex.split(stripped,comments=True)
|
|
if not line[0].isspace():
|
|
section=tokens[0];keep=True
|
|
if section=='service':
|
|
keep=len(tokens)>2 and tokens[1] in SERVICES and not any('_DISABLED' in t for t in tokens[2:])
|
|
if keep:kept.append(tokens[1])
|
|
elif section=='import':
|
|
# USB, recovery, and Android app processes are outside this trial.
|
|
keep=not any(word in stripped for word in ('usb','recovery','zygote'))
|
|
elif section!='on':
|
|
keep=False
|
|
if keep:output.append(line)
|
|
else:removed.append({'line':number,'text':line})
|
|
continue
|
|
accepted=keep and (section=='service' or command_allowed(tokens))
|
|
# Keep failed trial services inspectable instead of rebooting the device.
|
|
if section=='service' and tokens[0]=='critical':accepted=False
|
|
if section=='service' and tokens[0]=='onrestart':accepted=keep and command_allowed(tokens)
|
|
if accepted:output.append(line)
|
|
else:removed.append({'line':number,'text':line})
|
|
return '\n'.join(output)+'\n',{'services':kept,'removed':removed}
|