remove comma two support (#24248)
* remove comma two support * cleanup release files * little more * more libs * no more gralloc * add snpe backpull/24255/head
parent
233a167cc9
commit
5c48e7bc86
361 changed files with 203 additions and 55816 deletions
@ -1,2 +0,0 @@ |
|||||||
#!/usr/bin/bash |
|
||||||
echo "this is a compatability shim for old updaters" |
|
@ -1 +0,0 @@ |
|||||||
README.md |
|
File diff suppressed because it is too large
Load Diff
@ -1,106 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <atomic> |
|
||||||
#include <cstdint> |
|
||||||
#include <memory> |
|
||||||
|
|
||||||
#include "cereal/messaging/messaging.h" |
|
||||||
#include "cereal/visionipc/visionbuf.h" |
|
||||||
#include "selfdrive/camerad/cameras/camera_common.h" |
|
||||||
#include "selfdrive/camerad/imgproc/utils.h" |
|
||||||
#include "selfdrive/camerad/include/msm_cam_sensor.h" |
|
||||||
#include "selfdrive/camerad/include/msmb_camera.h" |
|
||||||
#include "selfdrive/camerad/include/msmb_isp.h" |
|
||||||
#include "selfdrive/camerad/include/msmb_ispif.h" |
|
||||||
#include "selfdrive/common/mat.h" |
|
||||||
#include "selfdrive/common/util.h" |
|
||||||
|
|
||||||
#define FRAME_BUF_COUNT 4 |
|
||||||
#define METADATA_BUF_COUNT 4 |
|
||||||
|
|
||||||
#define NUM_FOCUS 8 |
|
||||||
|
|
||||||
#define LP3_AF_DAC_DOWN 366 |
|
||||||
#define LP3_AF_DAC_UP 634 |
|
||||||
#define LP3_AF_DAC_M 440 |
|
||||||
#define LP3_AF_DAC_3SIG 52 |
|
||||||
|
|
||||||
#define FOCUS_RECOVER_PATIENCE 50 // 2.5 seconds of complete blur
|
|
||||||
#define FOCUS_RECOVER_STEPS 240 // 6 seconds
|
|
||||||
|
|
||||||
typedef struct CameraState CameraState; |
|
||||||
|
|
||||||
typedef int (*camera_apply_exposure_func)(CameraState *s, int gain, int integ_lines, uint32_t frame_length); |
|
||||||
|
|
||||||
typedef struct StreamState { |
|
||||||
struct msm_isp_buf_request buf_request; |
|
||||||
struct msm_vfe_axi_stream_request_cmd stream_req; |
|
||||||
struct msm_isp_qbuf_info qbuf_info[FRAME_BUF_COUNT]; |
|
||||||
VisionBuf *bufs; |
|
||||||
} StreamState; |
|
||||||
|
|
||||||
typedef struct CameraState { |
|
||||||
int camera_num; |
|
||||||
int camera_id; |
|
||||||
|
|
||||||
int fps; |
|
||||||
CameraInfo ci; |
|
||||||
|
|
||||||
unique_fd csid_fd; |
|
||||||
unique_fd csiphy_fd; |
|
||||||
unique_fd sensor_fd; |
|
||||||
unique_fd isp_fd; |
|
||||||
|
|
||||||
struct msm_vfe_axi_stream_cfg_cmd stream_cfg; |
|
||||||
|
|
||||||
StreamState ss[3]; |
|
||||||
CameraBuf buf; |
|
||||||
|
|
||||||
std::mutex frame_info_lock; |
|
||||||
FrameMetadata frame_metadata[METADATA_BUF_COUNT]; |
|
||||||
int frame_metadata_idx; |
|
||||||
|
|
||||||
// exposure
|
|
||||||
uint32_t pixel_clock, line_length_pclk; |
|
||||||
uint32_t frame_length; |
|
||||||
unsigned int max_gain; |
|
||||||
float cur_exposure_frac, cur_gain_frac; |
|
||||||
int cur_gain, cur_integ_lines; |
|
||||||
|
|
||||||
float measured_grey_fraction; |
|
||||||
float target_grey_fraction; |
|
||||||
|
|
||||||
std::atomic<float> digital_gain; |
|
||||||
camera_apply_exposure_func apply_exposure; |
|
||||||
|
|
||||||
// rear camera only,used for focusing
|
|
||||||
unique_fd actuator_fd; |
|
||||||
std::atomic<float> focus_err; |
|
||||||
std::atomic<float> lens_true_pos; |
|
||||||
std::atomic<int> self_recover; // af recovery counter, neg is patience, pos is active
|
|
||||||
uint16_t cur_step_pos; |
|
||||||
uint16_t cur_lens_pos; |
|
||||||
int16_t focus[NUM_FOCUS]; |
|
||||||
uint8_t confidence[NUM_FOCUS]; |
|
||||||
} CameraState; |
|
||||||
|
|
||||||
|
|
||||||
typedef struct MultiCameraState { |
|
||||||
unique_fd ispif_fd; |
|
||||||
unique_fd msmcfg_fd; |
|
||||||
unique_fd v4l_fd; |
|
||||||
uint16_t lapres[(ROI_X_MAX-ROI_X_MIN+1)*(ROI_Y_MAX-ROI_Y_MIN+1)]; |
|
||||||
|
|
||||||
VisionBuf focus_bufs[FRAME_BUF_COUNT]; |
|
||||||
VisionBuf stats_bufs[FRAME_BUF_COUNT]; |
|
||||||
|
|
||||||
CameraState road_cam; |
|
||||||
CameraState driver_cam; |
|
||||||
|
|
||||||
SubMaster *sm; |
|
||||||
PubMaster *pm; |
|
||||||
LapConv *lap_conv; |
|
||||||
} MultiCameraState; |
|
||||||
|
|
||||||
void actuator_move(CameraState *s, uint16_t target); |
|
||||||
int sensor_write_regs(CameraState *s, struct msm_camera_i2c_reg_array* arr, size_t size, int data_type); |
|
@ -1,91 +0,0 @@ |
|||||||
#!/usr/bin/env python3 |
|
||||||
import os |
|
||||||
import time |
|
||||||
import psutil |
|
||||||
from typing import Optional |
|
||||||
|
|
||||||
import cereal.messaging as messaging |
|
||||||
from common.realtime import set_core_affinity, set_realtime_priority |
|
||||||
from selfdrive.swaglog import cloudlog |
|
||||||
|
|
||||||
|
|
||||||
MAX_MODEM_CRASHES = 3 |
|
||||||
MODEM_PATH = "/sys/devices/soc/2080000.qcom,mss/subsys5" |
|
||||||
WATCHED_PROCS = ["zygote", "zygote64", "system_server", "/system/bin/servicemanager", "/system/bin/surfaceflinger"] |
|
||||||
|
|
||||||
|
|
||||||
def get_modem_crash_count() -> Optional[int]: |
|
||||||
try: |
|
||||||
with open(os.path.join(MODEM_PATH, "crash_count")) as f: |
|
||||||
return int(f.read()) |
|
||||||
except Exception: |
|
||||||
cloudlog.exception("Error reading modem crash count") |
|
||||||
return None |
|
||||||
|
|
||||||
def get_modem_state() -> str: |
|
||||||
try: |
|
||||||
with open(os.path.join(MODEM_PATH, "state")) as f: |
|
||||||
return f.read().strip() |
|
||||||
except Exception: |
|
||||||
cloudlog.exception("Error reading modem state") |
|
||||||
return "" |
|
||||||
|
|
||||||
def main(): |
|
||||||
set_core_affinity(1) |
|
||||||
set_realtime_priority(1) |
|
||||||
|
|
||||||
procs = {} |
|
||||||
crash_count = 0 |
|
||||||
modem_killed = False |
|
||||||
modem_state = "ONLINE" |
|
||||||
androidLog = messaging.sub_sock('androidLog') |
|
||||||
|
|
||||||
while True: |
|
||||||
# check critical android services |
|
||||||
if any(p is None or not p.is_running() for p in procs.values()) or not len(procs): |
|
||||||
cur = {p: None for p in WATCHED_PROCS} |
|
||||||
for p in psutil.process_iter(attrs=['cmdline']): |
|
||||||
cmdline = None if not len(p.info['cmdline']) else p.info['cmdline'][0] |
|
||||||
if cmdline in WATCHED_PROCS: |
|
||||||
cur[cmdline] = p |
|
||||||
|
|
||||||
if len(procs): |
|
||||||
for p in WATCHED_PROCS: |
|
||||||
if cur[p] != procs[p]: |
|
||||||
cloudlog.event("android service pid changed", proc=p, cur=cur[p], prev=procs[p], error=True) |
|
||||||
procs.update(cur) |
|
||||||
|
|
||||||
# log caught NetworkPolicy exceptions |
|
||||||
msgs = messaging.drain_sock(androidLog) |
|
||||||
for m in msgs: |
|
||||||
try: |
|
||||||
if m.androidLog.tag == "NetworkPolicy" and m.androidLog.message.startswith("problem with advise persist threshold"): |
|
||||||
cloudlog.event("network policy exception caught", androidLog=m.androidLog, error=True) |
|
||||||
except UnicodeDecodeError: |
|
||||||
pass |
|
||||||
|
|
||||||
if os.path.exists(MODEM_PATH): |
|
||||||
# check modem state |
|
||||||
state = get_modem_state() |
|
||||||
if state != modem_state and not modem_killed: |
|
||||||
cloudlog.event("modem state changed", state=state) |
|
||||||
modem_state = state |
|
||||||
|
|
||||||
# check modem crashes |
|
||||||
cnt = get_modem_crash_count() |
|
||||||
if cnt is not None: |
|
||||||
if cnt > crash_count: |
|
||||||
cloudlog.event("modem crash", count=cnt) |
|
||||||
crash_count = cnt |
|
||||||
|
|
||||||
# handle excessive modem crashes |
|
||||||
if crash_count > MAX_MODEM_CRASHES and not modem_killed: |
|
||||||
cloudlog.event("killing modem", error=True) |
|
||||||
with open("/sys/kernel/debug/msm_subsys/modem", "w") as f: |
|
||||||
f.write("put") |
|
||||||
modem_killed = True |
|
||||||
|
|
||||||
time.sleep(1) |
|
||||||
|
|
||||||
if __name__ == "__main__": |
|
||||||
main() |
|
@ -1,73 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <cstdlib> |
|
||||||
#include <fstream> |
|
||||||
|
|
||||||
#include <gui/ISurfaceComposer.h> |
|
||||||
#include <gui/SurfaceComposerClient.h> |
|
||||||
#include <hardware/hwcomposer_defs.h> |
|
||||||
|
|
||||||
#include "selfdrive/common/util.h" |
|
||||||
#include "selfdrive/hardware/base.h" |
|
||||||
|
|
||||||
class HardwareEon : public HardwareNone { |
|
||||||
public: |
|
||||||
static constexpr float MAX_VOLUME = 1.0; |
|
||||||
static constexpr float MIN_VOLUME = 0.5; |
|
||||||
|
|
||||||
static bool EON() { return true; } |
|
||||||
static std::string get_os_version() { |
|
||||||
return "NEOS " + util::read_file("/VERSION"); |
|
||||||
}; |
|
||||||
|
|
||||||
static void reboot() { std::system("reboot"); }; |
|
||||||
static void poweroff() { std::system("LD_LIBRARY_PATH= svc power shutdown"); }; |
|
||||||
static void set_brightness(int percent) { |
|
||||||
std::ofstream brightness_control("/sys/class/leds/lcd-backlight/brightness"); |
|
||||||
if (brightness_control.is_open()) { |
|
||||||
brightness_control << (int)(percent * (255/100.)) << "\n"; |
|
||||||
brightness_control.close(); |
|
||||||
} |
|
||||||
}; |
|
||||||
static void set_display_power(bool on) { |
|
||||||
auto dtoken = android::SurfaceComposerClient::getBuiltInDisplay(android::ISurfaceComposer::eDisplayIdMain); |
|
||||||
android::SurfaceComposerClient::setDisplayPowerMode(dtoken, on ? HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF); |
|
||||||
}; |
|
||||||
|
|
||||||
static bool get_ssh_enabled() { |
|
||||||
return std::system("getprop persist.neos.ssh | grep -qF '1'") == 0; |
|
||||||
}; |
|
||||||
static void set_ssh_enabled(bool enabled) { |
|
||||||
std::string cmd = util::string_format("setprop persist.neos.ssh %d", enabled ? 1 : 0); |
|
||||||
std::system(cmd.c_str()); |
|
||||||
}; |
|
||||||
|
|
||||||
// android only
|
|
||||||
inline static bool launched_activity = false; |
|
||||||
static void check_activity() { |
|
||||||
int ret = std::system("dumpsys SurfaceFlinger --list | grep -Fq 'com.android.settings'"); |
|
||||||
launched_activity = ret == 0; |
|
||||||
} |
|
||||||
|
|
||||||
static void close_activities() { |
|
||||||
if(launched_activity) { |
|
||||||
std::system("pm disable com.android.settings && pm enable com.android.settings"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void launch_activity(std::string activity, std::string opts = "") { |
|
||||||
if (!launched_activity) { |
|
||||||
std::string cmd = "am start -n " + activity + " " + opts + |
|
||||||
" --ez extra_prefs_show_button_bar true \
|
|
||||||
--es extra_prefs_set_next_text ''"; |
|
||||||
std::system(cmd.c_str()); |
|
||||||
} |
|
||||||
launched_activity = true; |
|
||||||
} |
|
||||||
static void launch_wifi() { |
|
||||||
launch_activity("com.android.settings/.wifi.WifiPickerActivity", "-a android.net.wifi.PICK_WIFI_NETWORK"); |
|
||||||
} |
|
||||||
static void launch_tethering() { |
|
||||||
launch_activity("com.android.settings/.TetherSettings"); |
|
||||||
} |
|
||||||
}; |
|
@ -1,425 +0,0 @@ |
|||||||
import binascii |
|
||||||
import itertools |
|
||||||
import os |
|
||||||
import re |
|
||||||
import serial |
|
||||||
import struct |
|
||||||
import subprocess |
|
||||||
from typing import List, Union |
|
||||||
|
|
||||||
from cereal import log |
|
||||||
from selfdrive.hardware.base import HardwareBase, ThermalConfig |
|
||||||
|
|
||||||
try: |
|
||||||
from common.params import Params |
|
||||||
except Exception: |
|
||||||
# openpilot is not built yet |
|
||||||
Params = None |
|
||||||
|
|
||||||
NetworkType = log.DeviceState.NetworkType |
|
||||||
NetworkStrength = log.DeviceState.NetworkStrength |
|
||||||
|
|
||||||
MODEM_PATH = "/dev/smd11" |
|
||||||
|
|
||||||
def service_call(call: List[str]) -> Union[bytes, None]: |
|
||||||
try: |
|
||||||
ret = subprocess.check_output(["service", "call", *call], encoding='utf8').strip() |
|
||||||
if 'Parcel' not in ret: |
|
||||||
return None |
|
||||||
return parse_service_call_bytes(ret) |
|
||||||
except subprocess.CalledProcessError: |
|
||||||
return None |
|
||||||
|
|
||||||
|
|
||||||
def parse_service_call_unpack(r, fmt) -> Union[bytes, None]: |
|
||||||
try: |
|
||||||
return struct.unpack(fmt, r)[0] |
|
||||||
except Exception: |
|
||||||
return None |
|
||||||
|
|
||||||
|
|
||||||
def parse_service_call_string(r: bytes) -> Union[str, None]: |
|
||||||
try: |
|
||||||
r = r[8:] # Cut off length field |
|
||||||
r_str = r.decode('utf_16_be') |
|
||||||
|
|
||||||
# All pairs of two characters seem to be swapped. Not sure why |
|
||||||
result = "" |
|
||||||
for a, b, in itertools.zip_longest(r_str[::2], r_str[1::2], fillvalue='\x00'): |
|
||||||
result += b + a |
|
||||||
|
|
||||||
return result.replace('\x00', '') |
|
||||||
except Exception: |
|
||||||
return None |
|
||||||
|
|
||||||
|
|
||||||
def parse_service_call_bytes(ret: str) -> Union[bytes, None]: |
|
||||||
try: |
|
||||||
r = b"" |
|
||||||
for hex_part in re.findall(r'[ (]([0-9a-f]{8})', ret): |
|
||||||
r += binascii.unhexlify(hex_part) |
|
||||||
return r |
|
||||||
except Exception: |
|
||||||
return None |
|
||||||
|
|
||||||
|
|
||||||
def getprop(key: str) -> Union[str, None]: |
|
||||||
try: |
|
||||||
return subprocess.check_output(["getprop", key], encoding='utf8').strip() |
|
||||||
except subprocess.CalledProcessError: |
|
||||||
return None |
|
||||||
|
|
||||||
|
|
||||||
class Android(HardwareBase): |
|
||||||
def get_os_version(self): |
|
||||||
with open("/VERSION") as f: |
|
||||||
return f.read().strip() |
|
||||||
|
|
||||||
def get_device_type(self): |
|
||||||
try: |
|
||||||
if int(Params().get("LastPeripheralPandaType")) == log.PandaState.PandaType.uno: |
|
||||||
return "two" |
|
||||||
except Exception: |
|
||||||
pass |
|
||||||
return "eon" |
|
||||||
|
|
||||||
def get_sound_card_online(self): |
|
||||||
return (os.path.isfile('/proc/asound/card0/state') and |
|
||||||
open('/proc/asound/card0/state').read().strip() == 'ONLINE') |
|
||||||
|
|
||||||
def get_imei(self, slot): |
|
||||||
slot = str(slot) |
|
||||||
if slot not in ("0", "1"): |
|
||||||
raise ValueError("SIM slot must be 0 or 1") |
|
||||||
|
|
||||||
return parse_service_call_string(service_call(["iphonesubinfo", "3", "i32", str(slot)])) |
|
||||||
|
|
||||||
def get_serial(self): |
|
||||||
ret = getprop("ro.serialno") |
|
||||||
if len(ret) == 0: |
|
||||||
ret = "cccccccc" |
|
||||||
return ret |
|
||||||
|
|
||||||
def get_subscriber_info(self): |
|
||||||
ret = parse_service_call_string(service_call(["iphonesubinfo", "7"])) |
|
||||||
if ret is None or len(ret) < 8: |
|
||||||
return "" |
|
||||||
return ret |
|
||||||
|
|
||||||
def reboot(self, reason=None): |
|
||||||
# e.g. reason="recovery" to go into recover mode |
|
||||||
if reason is None: |
|
||||||
reason_args = ["null"] |
|
||||||
else: |
|
||||||
reason_args = ["s16", reason] |
|
||||||
|
|
||||||
subprocess.check_output([ |
|
||||||
"service", "call", "power", "16", # IPowerManager.reboot |
|
||||||
"i32", "0", # no confirmation, |
|
||||||
*reason_args, |
|
||||||
"i32", "1" # wait |
|
||||||
]) |
|
||||||
|
|
||||||
def uninstall(self): |
|
||||||
with open('/cache/recovery/command', 'w') as f: |
|
||||||
f.write('--wipe_data\n') |
|
||||||
# IPowerManager.reboot(confirm=false, reason="recovery", wait=true) |
|
||||||
self.reboot(reason="recovery") |
|
||||||
|
|
||||||
def get_sim_info(self): |
|
||||||
# Used for athena |
|
||||||
# TODO: build using methods from this class |
|
||||||
sim_state = getprop("gsm.sim.state").split(",") |
|
||||||
network_type = getprop("gsm.network.type").split(',') |
|
||||||
mcc_mnc = getprop("gsm.sim.operator.numeric") or None |
|
||||||
|
|
||||||
sim_id = parse_service_call_string(service_call(['iphonesubinfo', '11'])) |
|
||||||
cell_data_state = parse_service_call_unpack(service_call(['phone', '46']), ">q") |
|
||||||
cell_data_connected = (cell_data_state == 2) |
|
||||||
|
|
||||||
return { |
|
||||||
'sim_id': sim_id, |
|
||||||
'mcc_mnc': mcc_mnc, |
|
||||||
'network_type': network_type, |
|
||||||
'sim_state': sim_state, |
|
||||||
'data_connected': cell_data_connected |
|
||||||
} |
|
||||||
|
|
||||||
def get_network_info(self): |
|
||||||
msg = log.DeviceState.NetworkInfo.new_message() |
|
||||||
msg.state = getprop("gsm.sim.state") or "" |
|
||||||
msg.technology = getprop("gsm.network.type") or "" |
|
||||||
msg.operator = getprop("gsm.sim.operator.numeric") or "" |
|
||||||
|
|
||||||
try: |
|
||||||
modem = serial.Serial(MODEM_PATH, 115200, timeout=0.1) |
|
||||||
modem.write(b"AT$QCRSRP?\r") |
|
||||||
msg.extra = modem.read_until(b"OK\r\n").decode('utf-8') |
|
||||||
|
|
||||||
rsrp = msg.extra.split("$QCRSRP: ")[1].split("\r")[0].split(",") |
|
||||||
msg.channel = int(rsrp[1]) |
|
||||||
except Exception: |
|
||||||
pass |
|
||||||
|
|
||||||
return msg |
|
||||||
|
|
||||||
def get_network_type(self): |
|
||||||
wifi_check = parse_service_call_string(service_call(["connectivity", "2"])) |
|
||||||
if wifi_check is None: |
|
||||||
return NetworkType.none |
|
||||||
elif 'WIFI' in wifi_check: |
|
||||||
return NetworkType.wifi |
|
||||||
else: |
|
||||||
cell_check = parse_service_call_unpack(service_call(['phone', '59']), ">q") |
|
||||||
# from TelephonyManager.java |
|
||||||
cell_networks = { |
|
||||||
0: NetworkType.none, |
|
||||||
1: NetworkType.cell2G, |
|
||||||
2: NetworkType.cell2G, |
|
||||||
3: NetworkType.cell3G, |
|
||||||
4: NetworkType.cell2G, |
|
||||||
5: NetworkType.cell3G, |
|
||||||
6: NetworkType.cell3G, |
|
||||||
7: NetworkType.cell3G, |
|
||||||
8: NetworkType.cell3G, |
|
||||||
9: NetworkType.cell3G, |
|
||||||
10: NetworkType.cell3G, |
|
||||||
11: NetworkType.cell2G, |
|
||||||
12: NetworkType.cell3G, |
|
||||||
13: NetworkType.cell4G, |
|
||||||
14: NetworkType.cell4G, |
|
||||||
15: NetworkType.cell3G, |
|
||||||
16: NetworkType.cell2G, |
|
||||||
17: NetworkType.cell3G, |
|
||||||
18: NetworkType.cell4G, |
|
||||||
19: NetworkType.cell4G |
|
||||||
} |
|
||||||
return cell_networks.get(cell_check, NetworkType.none) |
|
||||||
|
|
||||||
def get_network_strength(self, network_type): |
|
||||||
network_strength = NetworkStrength.unknown |
|
||||||
|
|
||||||
# from SignalStrength.java |
|
||||||
def get_lte_level(rsrp, rssnr): |
|
||||||
INT_MAX = 2147483647 |
|
||||||
if rsrp == INT_MAX: |
|
||||||
lvl_rsrp = NetworkStrength.unknown |
|
||||||
elif rsrp >= -95: |
|
||||||
lvl_rsrp = NetworkStrength.great |
|
||||||
elif rsrp >= -105: |
|
||||||
lvl_rsrp = NetworkStrength.good |
|
||||||
elif rsrp >= -115: |
|
||||||
lvl_rsrp = NetworkStrength.moderate |
|
||||||
else: |
|
||||||
lvl_rsrp = NetworkStrength.poor |
|
||||||
if rssnr == INT_MAX: |
|
||||||
lvl_rssnr = NetworkStrength.unknown |
|
||||||
elif rssnr >= 45: |
|
||||||
lvl_rssnr = NetworkStrength.great |
|
||||||
elif rssnr >= 10: |
|
||||||
lvl_rssnr = NetworkStrength.good |
|
||||||
elif rssnr >= -30: |
|
||||||
lvl_rssnr = NetworkStrength.moderate |
|
||||||
else: |
|
||||||
lvl_rssnr = NetworkStrength.poor |
|
||||||
return max(lvl_rsrp, lvl_rssnr) |
|
||||||
|
|
||||||
def get_tdscdma_level(tdscmadbm): |
|
||||||
lvl = NetworkStrength.unknown |
|
||||||
if tdscmadbm > -25: |
|
||||||
lvl = NetworkStrength.unknown |
|
||||||
elif tdscmadbm >= -49: |
|
||||||
lvl = NetworkStrength.great |
|
||||||
elif tdscmadbm >= -73: |
|
||||||
lvl = NetworkStrength.good |
|
||||||
elif tdscmadbm >= -97: |
|
||||||
lvl = NetworkStrength.moderate |
|
||||||
elif tdscmadbm >= -110: |
|
||||||
lvl = NetworkStrength.poor |
|
||||||
return lvl |
|
||||||
|
|
||||||
def get_gsm_level(asu): |
|
||||||
if asu <= 2 or asu == 99: |
|
||||||
lvl = NetworkStrength.unknown |
|
||||||
elif asu >= 12: |
|
||||||
lvl = NetworkStrength.great |
|
||||||
elif asu >= 8: |
|
||||||
lvl = NetworkStrength.good |
|
||||||
elif asu >= 5: |
|
||||||
lvl = NetworkStrength.moderate |
|
||||||
else: |
|
||||||
lvl = NetworkStrength.poor |
|
||||||
return lvl |
|
||||||
|
|
||||||
def get_evdo_level(evdodbm, evdosnr): |
|
||||||
lvl_evdodbm = NetworkStrength.unknown |
|
||||||
lvl_evdosnr = NetworkStrength.unknown |
|
||||||
if evdodbm >= -65: |
|
||||||
lvl_evdodbm = NetworkStrength.great |
|
||||||
elif evdodbm >= -75: |
|
||||||
lvl_evdodbm = NetworkStrength.good |
|
||||||
elif evdodbm >= -90: |
|
||||||
lvl_evdodbm = NetworkStrength.moderate |
|
||||||
elif evdodbm >= -105: |
|
||||||
lvl_evdodbm = NetworkStrength.poor |
|
||||||
if evdosnr >= 7: |
|
||||||
lvl_evdosnr = NetworkStrength.great |
|
||||||
elif evdosnr >= 5: |
|
||||||
lvl_evdosnr = NetworkStrength.good |
|
||||||
elif evdosnr >= 3: |
|
||||||
lvl_evdosnr = NetworkStrength.moderate |
|
||||||
elif evdosnr >= 1: |
|
||||||
lvl_evdosnr = NetworkStrength.poor |
|
||||||
return max(lvl_evdodbm, lvl_evdosnr) |
|
||||||
|
|
||||||
def get_cdma_level(cdmadbm, cdmaecio): |
|
||||||
lvl_cdmadbm = NetworkStrength.unknown |
|
||||||
lvl_cdmaecio = NetworkStrength.unknown |
|
||||||
if cdmadbm >= -75: |
|
||||||
lvl_cdmadbm = NetworkStrength.great |
|
||||||
elif cdmadbm >= -85: |
|
||||||
lvl_cdmadbm = NetworkStrength.good |
|
||||||
elif cdmadbm >= -95: |
|
||||||
lvl_cdmadbm = NetworkStrength.moderate |
|
||||||
elif cdmadbm >= -100: |
|
||||||
lvl_cdmadbm = NetworkStrength.poor |
|
||||||
if cdmaecio >= -90: |
|
||||||
lvl_cdmaecio = NetworkStrength.great |
|
||||||
elif cdmaecio >= -110: |
|
||||||
lvl_cdmaecio = NetworkStrength.good |
|
||||||
elif cdmaecio >= -130: |
|
||||||
lvl_cdmaecio = NetworkStrength.moderate |
|
||||||
elif cdmaecio >= -150: |
|
||||||
lvl_cdmaecio = NetworkStrength.poor |
|
||||||
return max(lvl_cdmadbm, lvl_cdmaecio) |
|
||||||
|
|
||||||
if network_type == NetworkType.none: |
|
||||||
return network_strength |
|
||||||
if network_type == NetworkType.wifi: |
|
||||||
out = subprocess.check_output('dumpsys connectivity', shell=True).decode('utf-8') |
|
||||||
network_strength = NetworkStrength.unknown |
|
||||||
for line in out.split('\n'): |
|
||||||
signal_str = "SignalStrength: " |
|
||||||
if signal_str in line: |
|
||||||
lvl_idx_start = line.find(signal_str) + len(signal_str) |
|
||||||
lvl_idx_end = line.find(']', lvl_idx_start) |
|
||||||
lvl = int(line[lvl_idx_start : lvl_idx_end]) |
|
||||||
if lvl >= -50: |
|
||||||
network_strength = NetworkStrength.great |
|
||||||
elif lvl >= -60: |
|
||||||
network_strength = NetworkStrength.good |
|
||||||
elif lvl >= -70: |
|
||||||
network_strength = NetworkStrength.moderate |
|
||||||
else: |
|
||||||
network_strength = NetworkStrength.poor |
|
||||||
return network_strength |
|
||||||
else: |
|
||||||
# check cell strength |
|
||||||
out = subprocess.check_output('dumpsys telephony.registry', shell=True).decode('utf-8') |
|
||||||
for line in out.split('\n'): |
|
||||||
if "mSignalStrength" in line: |
|
||||||
arr = line.split(' ') |
|
||||||
ns = 0 |
|
||||||
if ("gsm" in arr[14]): |
|
||||||
rsrp = int(arr[9]) |
|
||||||
rssnr = int(arr[11]) |
|
||||||
ns = get_lte_level(rsrp, rssnr) |
|
||||||
if ns == NetworkStrength.unknown: |
|
||||||
tdscmadbm = int(arr[13]) |
|
||||||
ns = get_tdscdma_level(tdscmadbm) |
|
||||||
if ns == NetworkStrength.unknown: |
|
||||||
asu = int(arr[1]) |
|
||||||
ns = get_gsm_level(asu) |
|
||||||
else: |
|
||||||
cdmadbm = int(arr[3]) |
|
||||||
cdmaecio = int(arr[4]) |
|
||||||
evdodbm = int(arr[5]) |
|
||||||
evdosnr = int(arr[7]) |
|
||||||
lvl_cdma = get_cdma_level(cdmadbm, cdmaecio) |
|
||||||
lvl_edmo = get_evdo_level(evdodbm, evdosnr) |
|
||||||
if lvl_edmo == NetworkStrength.unknown: |
|
||||||
ns = lvl_cdma |
|
||||||
elif lvl_cdma == NetworkStrength.unknown: |
|
||||||
ns = lvl_edmo |
|
||||||
else: |
|
||||||
ns = min(lvl_cdma, lvl_edmo) |
|
||||||
network_strength = max(network_strength, ns) |
|
||||||
|
|
||||||
return network_strength |
|
||||||
|
|
||||||
def get_battery_capacity(self): |
|
||||||
return self.read_param_file("/sys/class/power_supply/battery/capacity", int, 100) |
|
||||||
|
|
||||||
def get_battery_status(self): |
|
||||||
# This does not correspond with actual charging or not. |
|
||||||
# If a USB cable is plugged in, it responds with 'Charging', even when charging is disabled |
|
||||||
return self.read_param_file("/sys/class/power_supply/battery/status", lambda x: x.strip(), '') |
|
||||||
|
|
||||||
def get_battery_current(self): |
|
||||||
return self.read_param_file("/sys/class/power_supply/battery/current_now", int) |
|
||||||
|
|
||||||
def get_battery_voltage(self): |
|
||||||
return self.read_param_file("/sys/class/power_supply/battery/voltage_now", int) |
|
||||||
|
|
||||||
def get_battery_charging(self): |
|
||||||
# This does correspond with actually charging |
|
||||||
return self.read_param_file("/sys/class/power_supply/battery/charge_type", lambda x: x.strip() != "N/A", True) |
|
||||||
|
|
||||||
def set_battery_charging(self, on): |
|
||||||
with open('/sys/class/power_supply/battery/charging_enabled', 'w') as f: |
|
||||||
f.write(f"{1 if on else 0}\n") |
|
||||||
|
|
||||||
def get_usb_present(self): |
|
||||||
return self.read_param_file("/sys/class/power_supply/usb/present", lambda x: bool(int(x)), False) |
|
||||||
|
|
||||||
def get_current_power_draw(self): |
|
||||||
if self.get_battery_status() == 'Discharging': |
|
||||||
# If the battery is discharging, we can use this measurement |
|
||||||
# On C2: this is low by about 10-15%, probably mostly due to UNO draw not being factored in |
|
||||||
return ((self.get_battery_voltage() / 1000000) * (self.get_battery_current() / 1000000)) |
|
||||||
else: |
|
||||||
# We don't have a good direct way to measure this if it's not "discharging" |
|
||||||
return None |
|
||||||
|
|
||||||
def shutdown(self): |
|
||||||
os.system('LD_LIBRARY_PATH="" svc power shutdown') |
|
||||||
|
|
||||||
def get_thermal_config(self): |
|
||||||
# the thermal sensors on the 820 don't have meaningful names |
|
||||||
return ThermalConfig(cpu=((5, 7, 10, 12), 10), gpu=((16,), 10), mem=(2, 10), |
|
||||||
bat=("battery", 1000), ambient=("pa_therm0", 1), pmic=(("pm8994_tz",), 1000)) |
|
||||||
|
|
||||||
def set_screen_brightness(self, percentage): |
|
||||||
with open("/sys/class/leds/lcd-backlight/brightness", "w") as f: |
|
||||||
f.write(str(int(percentage * 2.55))) |
|
||||||
|
|
||||||
def get_screen_brightness(self): |
|
||||||
try: |
|
||||||
with open("/sys/class/leds/lcd-backlight/brightness") as f: |
|
||||||
return int(float(f.read()) / 2.55) |
|
||||||
except Exception: |
|
||||||
return 0 |
|
||||||
|
|
||||||
def set_power_save(self, powersave_enabled): |
|
||||||
pass |
|
||||||
|
|
||||||
def get_gpu_usage_percent(self): |
|
||||||
try: |
|
||||||
used, total = open('/sys/devices/soc/b00000.qcom,kgsl-3d0/kgsl/kgsl-3d0/gpubusy').read().strip().split() |
|
||||||
perc = 100.0 * int(used) / int(total) |
|
||||||
return min(max(perc, 0), 100) |
|
||||||
except Exception: |
|
||||||
return 0 |
|
||||||
|
|
||||||
def get_modem_temperatures(self): |
|
||||||
# Not sure if we can get this on the LeEco |
|
||||||
return [] |
|
||||||
|
|
||||||
def get_nvme_temperatures(self): |
|
||||||
return [] |
|
||||||
|
|
||||||
def initialize_hardware(self): |
|
||||||
pass |
|
||||||
|
|
||||||
def get_networks(self): |
|
||||||
return None |
|
@ -1,7 +0,0 @@ |
|||||||
{ |
|
||||||
"ota_url": "https://commadist.azureedge.net/neosupdate/ota-signed-50da8800caa5cbc224acbaa21f3a83d21802a31d89cccfc62a898903a8eb19e7.zip", |
|
||||||
"ota_hash": "50da8800caa5cbc224acbaa21f3a83d21802a31d89cccfc62a898903a8eb19e7", |
|
||||||
"recovery_url": "https://commadist.azureedge.net/neosupdate/recovery-fe76739438d28ea6111853a737b616afdf340ba75c01c0ee99e6a28c19ecc29f.img", |
|
||||||
"recovery_len": 15222060, |
|
||||||
"recovery_hash": "fe76739438d28ea6111853a737b616afdf340ba75c01c0ee99e6a28c19ecc29f" |
|
||||||
} |
|
@ -1,130 +0,0 @@ |
|||||||
#!/usr/bin/env python3 |
|
||||||
import argparse |
|
||||||
import hashlib |
|
||||||
import json |
|
||||||
import logging |
|
||||||
import os |
|
||||||
import requests |
|
||||||
|
|
||||||
NEOSUPDATE_DIR = "/data/neoupdate" |
|
||||||
|
|
||||||
RECOVERY_DEV = "/dev/block/bootdevice/by-name/recovery" |
|
||||||
RECOVERY_COMMAND = "/cache/recovery/command" |
|
||||||
|
|
||||||
|
|
||||||
def get_fn(url: str): |
|
||||||
return os.path.join(NEOSUPDATE_DIR, os.path.basename(url)) |
|
||||||
|
|
||||||
|
|
||||||
def download_file(url: str, fn: str, sha256: str, display_name: str, cloudlog=logging) -> None: |
|
||||||
# check if already downloaded |
|
||||||
if check_hash(fn, sha256): |
|
||||||
cloudlog.info(f"{display_name} already cached") |
|
||||||
return |
|
||||||
|
|
||||||
try: |
|
||||||
with open(fn, "ab+") as f: |
|
||||||
headers = {"Range": f"bytes={f.tell()}-"} |
|
||||||
r = requests.get(url, stream=True, allow_redirects=True, headers=headers) |
|
||||||
r.raise_for_status() |
|
||||||
|
|
||||||
total = int(r.headers['Content-Length']) |
|
||||||
if 'Content-Range' in r.headers: |
|
||||||
total = int(r.headers['Content-Range'].split('/')[-1]) |
|
||||||
|
|
||||||
for chunk in r.iter_content(chunk_size=1024 * 1024): |
|
||||||
f.write(chunk) |
|
||||||
print(f"Downloading {display_name}: {f.tell() / total * 100}", flush=True) |
|
||||||
except Exception: |
|
||||||
cloudlog.error("download error") |
|
||||||
if os.path.isfile(fn): |
|
||||||
os.unlink(fn) |
|
||||||
raise |
|
||||||
|
|
||||||
if not check_hash(fn, sha256): |
|
||||||
if os.path.isfile(fn): |
|
||||||
os.unlink(fn) |
|
||||||
raise Exception("downloaded update failed hash check") |
|
||||||
|
|
||||||
|
|
||||||
def check_hash(fn: str, sha256: str, length: int = -1) -> bool: |
|
||||||
if not os.path.exists(fn): |
|
||||||
return False |
|
||||||
|
|
||||||
h = hashlib.sha256() |
|
||||||
with open(fn, "rb") as f: |
|
||||||
while f.tell() != length: |
|
||||||
r = min(max(0, length - f.tell()), 1024 * 1024) if length > 0 else 1024 * 1024 |
|
||||||
dat = f.read(r) |
|
||||||
if not dat: |
|
||||||
break |
|
||||||
h.update(dat) |
|
||||||
return h.hexdigest().lower() == sha256.lower() |
|
||||||
|
|
||||||
|
|
||||||
def flash_update(update_fn: str, out_path: str) -> None: |
|
||||||
with open(update_fn, "rb") as update, open(out_path, "w+b") as out: |
|
||||||
while True: |
|
||||||
dat = update.read(8192) |
|
||||||
if len(dat) == 0: |
|
||||||
break |
|
||||||
out.write(dat) |
|
||||||
|
|
||||||
|
|
||||||
def download_neos_update(manifest_path: str, cloudlog=logging) -> None: |
|
||||||
with open(manifest_path) as f: |
|
||||||
m = json.load(f) |
|
||||||
|
|
||||||
os.makedirs(NEOSUPDATE_DIR, exist_ok=True) |
|
||||||
|
|
||||||
# handle recovery updates |
|
||||||
if not check_hash(RECOVERY_DEV, m['recovery_hash'], m['recovery_len']): |
|
||||||
cloudlog.info("recovery needs update") |
|
||||||
recovery_fn = os.path.join(NEOSUPDATE_DIR, os.path.basename(m['recovery_url'])) |
|
||||||
download_file(m['recovery_url'], recovery_fn, m['recovery_hash'], "recovery", cloudlog) |
|
||||||
|
|
||||||
flash_update(recovery_fn, RECOVERY_DEV) |
|
||||||
assert check_hash(RECOVERY_DEV, m['recovery_hash'], m['recovery_len']), "recovery flash corrupted" |
|
||||||
cloudlog.info("recovery successfully flashed") |
|
||||||
|
|
||||||
# download OTA update |
|
||||||
download_file(m['ota_url'], get_fn(m['ota_url']), m['ota_hash'], "system", cloudlog) |
|
||||||
|
|
||||||
|
|
||||||
def verify_update_ready(manifest_path: str) -> bool: |
|
||||||
with open(manifest_path) as f: |
|
||||||
m = json.load(f) |
|
||||||
|
|
||||||
ota_downloaded = check_hash(get_fn(m['ota_url']), m['ota_hash']) |
|
||||||
recovery_flashed = check_hash(RECOVERY_DEV, m['recovery_hash'], m['recovery_len']) |
|
||||||
return ota_downloaded and recovery_flashed |
|
||||||
|
|
||||||
|
|
||||||
def perform_ota_update(manifest_path: str) -> None: |
|
||||||
with open(manifest_path) as f: |
|
||||||
m = json.load(f) |
|
||||||
|
|
||||||
# reboot into recovery |
|
||||||
ota_fn = get_fn(m['ota_url']) |
|
||||||
with open(RECOVERY_COMMAND, "w") as rf: |
|
||||||
rf.write(f"--update_package={ota_fn}\n") |
|
||||||
os.system("service call power 16 i32 0 s16 recovery i32 1") |
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__": |
|
||||||
parser = argparse.ArgumentParser(description="NEOS update utility", |
|
||||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter) |
|
||||||
parser.add_argument("--swap", action="store_true", help="Peform update after downloading") |
|
||||||
parser.add_argument("--swap-if-ready", action="store_true", help="Perform update if already downloaded") |
|
||||||
parser.add_argument("manifest", help="Manifest json") |
|
||||||
args = parser.parse_args() |
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO) |
|
||||||
|
|
||||||
if args.swap_if_ready: |
|
||||||
if verify_update_ready(args.manifest): |
|
||||||
perform_ota_update(args.manifest) |
|
||||||
else: |
|
||||||
download_neos_update(args.manifest, logging) |
|
||||||
if args.swap: |
|
||||||
perform_ota_update(args.manifest) |
|
@ -1,34 +0,0 @@ |
|||||||
#!/usr/bin/env python3 |
|
||||||
import os |
|
||||||
import time |
|
||||||
import datetime |
|
||||||
|
|
||||||
from common.params import Params |
|
||||||
from selfdrive.hardware.eon.hardware import getprop |
|
||||||
from selfdrive.swaglog import cloudlog |
|
||||||
|
|
||||||
def main(): |
|
||||||
prev = b"" |
|
||||||
params = Params() |
|
||||||
while True: |
|
||||||
with open("/dev/__properties__", 'rb') as f: |
|
||||||
cur = f.read() |
|
||||||
|
|
||||||
if cur != prev: |
|
||||||
prev = cur |
|
||||||
|
|
||||||
# 0 for shutdown, 1 for reboot |
|
||||||
prop = getprop("sys.shutdown.requested") |
|
||||||
if prop is not None and len(prop) > 0: |
|
||||||
os.system("pkill -9 loggerd") |
|
||||||
params.put("LastSystemShutdown", f"'{prop}' {datetime.datetime.now()}") |
|
||||||
os.sync() |
|
||||||
|
|
||||||
time.sleep(120) |
|
||||||
cloudlog.error('shutdown false positive') |
|
||||||
break |
|
||||||
|
|
||||||
time.sleep(0.1) |
|
||||||
|
|
||||||
if __name__ == "__main__": |
|
||||||
main() |
|
@ -1,145 +0,0 @@ |
|||||||
#!/usr/bin/env python3 |
|
||||||
import hashlib |
|
||||||
import http.server |
|
||||||
import json |
|
||||||
import os |
|
||||||
import unittest |
|
||||||
import random |
|
||||||
import requests |
|
||||||
import shutil |
|
||||||
import socketserver |
|
||||||
import tempfile |
|
||||||
import multiprocessing |
|
||||||
from pathlib import Path |
|
||||||
|
|
||||||
from selfdrive.hardware.eon.neos import RECOVERY_DEV, NEOSUPDATE_DIR, get_fn, download_file, \ |
|
||||||
verify_update_ready, download_neos_update |
|
||||||
|
|
||||||
EON_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__))) |
|
||||||
MANIFEST = os.path.join(EON_DIR, "neos.json") |
|
||||||
|
|
||||||
PORT = 8000 |
|
||||||
|
|
||||||
def server_thread(port): |
|
||||||
socketserver.TCPServer.allow_reuse_address = True |
|
||||||
httpd = socketserver.TCPServer(("", port), http.server.SimpleHTTPRequestHandler) |
|
||||||
httpd.serve_forever() |
|
||||||
|
|
||||||
|
|
||||||
class TestNeosUpdater(unittest.TestCase): |
|
||||||
|
|
||||||
@classmethod |
|
||||||
def setUpClass(cls): |
|
||||||
# generate a fake manifest |
|
||||||
cls.manifest = {} |
|
||||||
for i in ('ota', 'recovery'): |
|
||||||
with tempfile.NamedTemporaryFile(delete=False, dir=os.getcwd()) as f: |
|
||||||
dat = os.urandom(random.randint(1000, 100000)) |
|
||||||
f.write(dat) |
|
||||||
cls.manifest[f"{i}_url"] = f"http://localhost:{PORT}/" + os.path.relpath(f.name) |
|
||||||
cls.manifest[F"{i}_hash"] = hashlib.sha256(dat).hexdigest() |
|
||||||
if i == "recovery": |
|
||||||
cls.manifest["recovery_len"] = len(dat) |
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(delete=False, mode='w') as f: |
|
||||||
f.write(json.dumps(cls.manifest)) |
|
||||||
cls.fake_manifest = f.name |
|
||||||
|
|
||||||
@classmethod |
|
||||||
def tearDownClass(cls): |
|
||||||
os.unlink(cls.fake_manifest) |
|
||||||
os.unlink(os.path.basename(cls.manifest['ota_url'])) |
|
||||||
os.unlink(os.path.basename(cls.manifest['recovery_url'])) |
|
||||||
|
|
||||||
def setUp(self): |
|
||||||
# server for update files |
|
||||||
self.server = multiprocessing.Process(target=server_thread, args=(PORT, )) |
|
||||||
self.server.start() |
|
||||||
|
|
||||||
# clean up |
|
||||||
if os.path.exists(NEOSUPDATE_DIR): |
|
||||||
shutil.rmtree(NEOSUPDATE_DIR) |
|
||||||
|
|
||||||
def tearDown(self): |
|
||||||
self.server.kill() |
|
||||||
self.server.join(1) |
|
||||||
|
|
||||||
def _corrupt_recovery(self): |
|
||||||
with open(RECOVERY_DEV, "wb") as f: |
|
||||||
f.write(b'\x00'*100) |
|
||||||
|
|
||||||
def test_manifest(self): |
|
||||||
with open(MANIFEST) as f: |
|
||||||
m = json.load(f) |
|
||||||
|
|
||||||
assert m['ota_hash'] in m['ota_url'] |
|
||||||
assert m['recovery_hash'] in m['recovery_url'] |
|
||||||
assert m['recovery_len'] > 0 |
|
||||||
|
|
||||||
for url in (m['ota_url'], m['recovery_url']): |
|
||||||
r = requests.head(m['recovery_url']) |
|
||||||
r.raise_for_status() |
|
||||||
self.assertEqual(r.headers['Content-Type'], "application/octet-stream") |
|
||||||
if url == m['recovery_url']: |
|
||||||
self.assertEqual(int(r.headers['Content-Length']), m['recovery_len']) |
|
||||||
|
|
||||||
def test_download_hash_check(self): |
|
||||||
os.makedirs(NEOSUPDATE_DIR, exist_ok=True) |
|
||||||
Path(get_fn(self.manifest['ota_url'])).touch() |
|
||||||
with self.assertRaisesRegex(Exception, "failed hash check"): |
|
||||||
download_file(self.manifest['ota_url'], get_fn(self.manifest['ota_url']), |
|
||||||
self.manifest['ota_hash']+'a', "system") |
|
||||||
|
|
||||||
# should've unlinked after the failed hash check, should succeed now |
|
||||||
download_file(self.manifest['ota_url'], get_fn(self.manifest['ota_url']), |
|
||||||
self.manifest['ota_hash'], "system") |
|
||||||
|
|
||||||
# TODO: needs an http server that supports Content-Range |
|
||||||
#def test_download_resume(self): |
|
||||||
# os.makedirs(NEOSUPDATE_DIR, exist_ok=True) |
|
||||||
# with open(os.path.basename(self.manifest['ota_url']), "rb") as src, \ |
|
||||||
# open(get_fn(self.manifest['ota_url']), "wb") as dest: |
|
||||||
# l = dest.write(src.read(8192)) |
|
||||||
# assert l == 8192 |
|
||||||
# download_file(self.manifest['ota_url'], get_fn(self.manifest['ota_url']), |
|
||||||
# self.manifest['ota_hash'], "system") |
|
||||||
|
|
||||||
def test_download_no_internet(self): |
|
||||||
self.server.kill() |
|
||||||
os.makedirs(NEOSUPDATE_DIR, exist_ok=True) |
|
||||||
# fail, no internet |
|
||||||
with self.assertRaises(requests.exceptions.ConnectionError): |
|
||||||
download_file(self.manifest['ota_url'], get_fn(self.manifest['ota_url']), |
|
||||||
self.manifest['ota_hash'], "system") |
|
||||||
|
|
||||||
# already cached, ensure we don't hit the server |
|
||||||
shutil.copyfile(os.path.basename(self.manifest['ota_url']), get_fn(self.manifest['ota_url'])) |
|
||||||
download_file(self.manifest['ota_url'], get_fn(self.manifest['ota_url']), |
|
||||||
self.manifest['ota_hash'], "system") |
|
||||||
|
|
||||||
|
|
||||||
def test_download_update(self): |
|
||||||
download_neos_update(self.fake_manifest) |
|
||||||
self.assertTrue(verify_update_ready(self.fake_manifest)) |
|
||||||
|
|
||||||
def test_verify_update(self): |
|
||||||
# good state |
|
||||||
download_neos_update(self.fake_manifest) |
|
||||||
self.assertTrue(verify_update_ready(self.fake_manifest)) |
|
||||||
|
|
||||||
# corrupt recovery |
|
||||||
self._corrupt_recovery() |
|
||||||
self.assertFalse(verify_update_ready(self.fake_manifest)) |
|
||||||
|
|
||||||
# back to good state |
|
||||||
download_neos_update(self.fake_manifest) |
|
||||||
self.assertTrue(verify_update_ready(self.fake_manifest)) |
|
||||||
|
|
||||||
# corrupt ota |
|
||||||
self._corrupt_recovery() |
|
||||||
with open(os.path.join(NEOSUPDATE_DIR, os.path.basename(self.manifest['ota_url'])), "ab") as f: |
|
||||||
f.write(b'\x00') |
|
||||||
self.assertFalse(verify_update_ready(self.fake_manifest)) |
|
||||||
|
|
||||||
if __name__ == "__main__": |
|
||||||
unittest.main() |
|
@ -1,4 +0,0 @@ |
|||||||
#!/usr/bin/bash |
|
||||||
|
|
||||||
ROOT=$PWD/../../.. |
|
||||||
$ROOT/installer/updater/updater "file://$ROOT/installer/updater/update.json" |
|
Binary file not shown.
@ -1,6 +1,3 @@ |
|||||||
Import('env', 'cereal', 'messaging', 'common', 'arch') |
Import('env', 'cereal', 'messaging', 'common') |
||||||
|
|
||||||
if arch == "aarch64": |
env.Program('logcatd', 'logcatd_systemd.cc', LIBS=[cereal, messaging, common, 'zmq', 'capnp', 'kj', 'systemd', 'json11']) |
||||||
env.Program('logcatd', 'logcatd_android.cc', LIBS=[cereal, messaging, common, 'cutils', 'zmq', 'capnp', 'kj']) |
|
||||||
else: |
|
||||||
env.Program('logcatd', 'logcatd_systemd.cc', LIBS=[cereal, messaging, common, 'zmq', 'capnp', 'kj', 'systemd', 'json11']) |
|
||||||
|
@ -1,51 +0,0 @@ |
|||||||
#include <android/log.h> |
|
||||||
#include <log/logger.h> |
|
||||||
#include <log/logprint.h> |
|
||||||
#include <sys/resource.h> |
|
||||||
|
|
||||||
#include <csignal> |
|
||||||
|
|
||||||
#include "cereal/messaging/messaging.h" |
|
||||||
|
|
||||||
#undef LOG_ID_KERNEL |
|
||||||
#define LOG_ID_KERNEL 5 |
|
||||||
|
|
||||||
int main() { |
|
||||||
std::signal(SIGINT, exit); |
|
||||||
std::signal(SIGTERM, exit); |
|
||||||
setpriority(PRIO_PROCESS, 0, -15); |
|
||||||
|
|
||||||
// setup android logging
|
|
||||||
logger_list *logger_list = android_logger_list_alloc(ANDROID_LOG_RDONLY, 0, 0); |
|
||||||
assert(logger_list); |
|
||||||
for (auto log_id : {LOG_ID_MAIN, LOG_ID_RADIO, LOG_ID_SYSTEM, LOG_ID_CRASH, (log_id_t)LOG_ID_KERNEL}) { |
|
||||||
logger *logger = android_logger_open(logger_list, log_id); |
|
||||||
assert(logger); |
|
||||||
} |
|
||||||
|
|
||||||
PubMaster pm({"androidLog"}); |
|
||||||
|
|
||||||
while (true) { |
|
||||||
log_msg log_msg; |
|
||||||
int err = android_logger_list_read(logger_list, &log_msg); |
|
||||||
if (err <= 0) break; |
|
||||||
|
|
||||||
AndroidLogEntry entry; |
|
||||||
err = android_log_processLogBuffer(&log_msg.entry_v1, &entry); |
|
||||||
if (err < 0) continue; |
|
||||||
|
|
||||||
MessageBuilder msg; |
|
||||||
auto androidEntry = msg.initEvent().initAndroidLog(); |
|
||||||
androidEntry.setId(log_msg.id()); |
|
||||||
androidEntry.setTs(entry.tv_sec * NS_PER_SEC + entry.tv_nsec); |
|
||||||
androidEntry.setPriority(entry.priority); |
|
||||||
androidEntry.setPid(entry.pid); |
|
||||||
androidEntry.setTid(entry.tid); |
|
||||||
androidEntry.setTag(entry.tag); |
|
||||||
androidEntry.setMessage(entry.message); |
|
||||||
pm.send("androidLog", msg); |
|
||||||
} |
|
||||||
|
|
||||||
android_logger_list_free(logger_list); |
|
||||||
return 0; |
|
||||||
} |
|
@ -1,45 +0,0 @@ |
|||||||
#!/usr/bin/env python3 |
|
||||||
import os |
|
||||||
import random |
|
||||||
import string |
|
||||||
import time |
|
||||||
import unittest |
|
||||||
import uuid |
|
||||||
|
|
||||||
import cereal.messaging as messaging |
|
||||||
from selfdrive.test.helpers import with_processes |
|
||||||
|
|
||||||
class TestLogcatdAndroid(unittest.TestCase): |
|
||||||
|
|
||||||
@with_processes(['logcatd']) |
|
||||||
def test_log(self): |
|
||||||
sock = messaging.sub_sock("androidLog", conflate=False) |
|
||||||
|
|
||||||
# make sure sockets are ready |
|
||||||
time.sleep(1) |
|
||||||
messaging.drain_sock(sock) |
|
||||||
|
|
||||||
sent_msgs = {} |
|
||||||
for _ in range(random.randint(2, 10)): |
|
||||||
# write some log messages |
|
||||||
for __ in range(random.randint(5, 50)): |
|
||||||
tag = uuid.uuid4().hex |
|
||||||
msg = ''.join(random.choice(string.ascii_letters) for _ in range(random.randrange(2, 50))) |
|
||||||
sent_msgs[tag] = {'recv_cnt': 0, 'msg': msg} |
|
||||||
os.system(f"log -t '{tag}' '{msg}'") |
|
||||||
|
|
||||||
time.sleep(1) |
|
||||||
msgs = messaging.drain_sock(sock) |
|
||||||
for m in msgs: |
|
||||||
self.assertTrue(m.valid) |
|
||||||
self.assertLess(time.monotonic() - (m.logMonoTime / 1e9), 30) |
|
||||||
tag = m.androidLog.tag |
|
||||||
if tag in sent_msgs: |
|
||||||
sent_msgs[tag]['recv_cnt'] += 1 |
|
||||||
self.assertEqual(m.androidLog.message.strip(), sent_msgs[tag]['msg']) |
|
||||||
|
|
||||||
for v in sent_msgs.values(): |
|
||||||
self.assertEqual(v['recv_cnt'], 1) |
|
||||||
|
|
||||||
if __name__ == "__main__": |
|
||||||
unittest.main() |
|
@ -1,228 +0,0 @@ |
|||||||
#include <cutils/log.h> |
|
||||||
#include <hardware/sensors.h> |
|
||||||
#include <sys/cdefs.h> |
|
||||||
#include <sys/resource.h> |
|
||||||
#include <sys/time.h> |
|
||||||
#include <sys/types.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <utils/Timers.h> |
|
||||||
|
|
||||||
#include <cassert> |
|
||||||
#include <cstdint> |
|
||||||
#include <cstdio> |
|
||||||
#include <cstdlib> |
|
||||||
#include <cstring> |
|
||||||
#include <map> |
|
||||||
#include <set> |
|
||||||
|
|
||||||
#include "cereal/messaging/messaging.h" |
|
||||||
#include "selfdrive/common/swaglog.h" |
|
||||||
#include "selfdrive/common/timing.h" |
|
||||||
#include "selfdrive/common/util.h" |
|
||||||
|
|
||||||
// ACCELEROMETER_UNCALIBRATED is only in Android O
|
|
||||||
// https://developer.android.com/reference/android/hardware/Sensor.html#STRING_TYPE_ACCELEROMETER_UNCALIBRATED
|
|
||||||
|
|
||||||
#define SENSOR_ACCELEROMETER 1 |
|
||||||
#define SENSOR_MAGNETOMETER 2 |
|
||||||
#define SENSOR_GYRO 4 |
|
||||||
#define SENSOR_MAGNETOMETER_UNCALIBRATED 3 |
|
||||||
#define SENSOR_GYRO_UNCALIBRATED 5 |
|
||||||
#define SENSOR_PROXIMITY 6 |
|
||||||
#define SENSOR_LIGHT 7 |
|
||||||
|
|
||||||
ExitHandler do_exit; |
|
||||||
volatile sig_atomic_t re_init_sensors = 0; |
|
||||||
|
|
||||||
namespace { |
|
||||||
|
|
||||||
void sigpipe_handler(int sig) { |
|
||||||
LOGE("SIGPIPE received"); |
|
||||||
re_init_sensors = true; |
|
||||||
} |
|
||||||
|
|
||||||
void sensor_loop() { |
|
||||||
LOG("*** sensor loop"); |
|
||||||
|
|
||||||
uint64_t frame = 0; |
|
||||||
bool low_power_mode = false; |
|
||||||
|
|
||||||
while (!do_exit) { |
|
||||||
SubMaster sm({"deviceState"}); |
|
||||||
PubMaster pm({"sensorEvents"}); |
|
||||||
|
|
||||||
struct sensors_poll_device_t* device; |
|
||||||
struct sensors_module_t* module; |
|
||||||
|
|
||||||
hw_get_module(SENSORS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); |
|
||||||
sensors_open(&module->common, &device); |
|
||||||
|
|
||||||
// required
|
|
||||||
struct sensor_t const* list; |
|
||||||
int count = module->get_sensors_list(module, &list); |
|
||||||
LOG("%d sensors found", count); |
|
||||||
|
|
||||||
if (getenv("SENSOR_TEST")) { |
|
||||||
exit(count); |
|
||||||
} |
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) { |
|
||||||
LOGD("sensor %4d: %4d %60s %d-%ld us", i, list[i].handle, list[i].name, list[i].minDelay, list[i].maxDelay); |
|
||||||
} |
|
||||||
|
|
||||||
std::set<int> sensor_types = { |
|
||||||
SENSOR_TYPE_ACCELEROMETER, |
|
||||||
SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED, |
|
||||||
SENSOR_TYPE_MAGNETIC_FIELD, |
|
||||||
SENSOR_TYPE_GYROSCOPE_UNCALIBRATED, |
|
||||||
SENSOR_TYPE_GYROSCOPE, |
|
||||||
SENSOR_TYPE_PROXIMITY, |
|
||||||
SENSOR_TYPE_LIGHT, |
|
||||||
}; |
|
||||||
|
|
||||||
std::map<int, int64_t> sensors = { |
|
||||||
{SENSOR_GYRO_UNCALIBRATED, ms2ns(10)}, |
|
||||||
{SENSOR_MAGNETOMETER_UNCALIBRATED, ms2ns(100)}, |
|
||||||
{SENSOR_ACCELEROMETER, ms2ns(10)}, |
|
||||||
{SENSOR_GYRO, ms2ns(10)}, |
|
||||||
{SENSOR_MAGNETOMETER, ms2ns(100)}, |
|
||||||
{SENSOR_PROXIMITY, ms2ns(100)}, |
|
||||||
{SENSOR_LIGHT, ms2ns(100)} |
|
||||||
}; |
|
||||||
|
|
||||||
// sensors needed while offroad
|
|
||||||
std::set<int> offroad_sensors = { |
|
||||||
SENSOR_LIGHT, |
|
||||||
SENSOR_ACCELEROMETER, |
|
||||||
SENSOR_GYRO_UNCALIBRATED, |
|
||||||
}; |
|
||||||
|
|
||||||
// init all the sensors
|
|
||||||
for (auto &s : sensors) { |
|
||||||
device->activate(device, s.first, 0); |
|
||||||
device->activate(device, s.first, 1); |
|
||||||
device->setDelay(device, s.first, s.second); |
|
||||||
} |
|
||||||
|
|
||||||
// TODO: why is this 16?
|
|
||||||
static const size_t numEvents = 16; |
|
||||||
sensors_event_t buffer[numEvents]; |
|
||||||
|
|
||||||
while (!do_exit) { |
|
||||||
int n = device->poll(device, buffer, numEvents); |
|
||||||
if (n == 0) continue; |
|
||||||
if (n < 0) { |
|
||||||
LOG("sensor_loop poll failed: %d", n); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
int log_events = 0; |
|
||||||
for (int i=0; i < n; i++) { |
|
||||||
if (sensor_types.find(buffer[i].type) != sensor_types.end()) { |
|
||||||
log_events++; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
MessageBuilder msg; |
|
||||||
auto sensor_events = msg.initEvent().initSensorEvents(log_events); |
|
||||||
|
|
||||||
int log_i = 0; |
|
||||||
for (int i = 0; i < n; i++) { |
|
||||||
|
|
||||||
const sensors_event_t& data = buffer[i]; |
|
||||||
|
|
||||||
if (sensor_types.find(data.type) == sensor_types.end()) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
auto log_event = sensor_events[log_i]; |
|
||||||
log_event.setSource(cereal::SensorEventData::SensorSource::ANDROID); |
|
||||||
log_event.setVersion(data.version); |
|
||||||
log_event.setSensor(data.sensor); |
|
||||||
log_event.setType(data.type); |
|
||||||
log_event.setTimestamp(data.timestamp); |
|
||||||
|
|
||||||
switch (data.type) { |
|
||||||
case SENSOR_TYPE_ACCELEROMETER: { |
|
||||||
auto svec = log_event.initAcceleration(); |
|
||||||
svec.setV(data.acceleration.v); |
|
||||||
svec.setStatus(data.acceleration.status); |
|
||||||
break; |
|
||||||
} |
|
||||||
case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED: { |
|
||||||
auto svec = log_event.initMagneticUncalibrated(); |
|
||||||
// assuming the uncalib and bias floats are contiguous in memory
|
|
||||||
kj::ArrayPtr<const float> vs(&data.uncalibrated_magnetic.uncalib[0], 6); |
|
||||||
svec.setV(vs); |
|
||||||
break; |
|
||||||
} |
|
||||||
case SENSOR_TYPE_MAGNETIC_FIELD: { |
|
||||||
auto svec = log_event.initMagnetic(); |
|
||||||
svec.setV(data.magnetic.v); |
|
||||||
svec.setStatus(data.magnetic.status); |
|
||||||
break; |
|
||||||
} |
|
||||||
case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: { |
|
||||||
auto svec = log_event.initGyroUncalibrated(); |
|
||||||
// assuming the uncalib and bias floats are contiguous in memory
|
|
||||||
kj::ArrayPtr<const float> vs(&data.uncalibrated_gyro.uncalib[0], 6); |
|
||||||
svec.setV(vs); |
|
||||||
break; |
|
||||||
} |
|
||||||
case SENSOR_TYPE_GYROSCOPE: { |
|
||||||
auto svec = log_event.initGyro(); |
|
||||||
svec.setV(data.gyro.v); |
|
||||||
svec.setStatus(data.gyro.status); |
|
||||||
break; |
|
||||||
} |
|
||||||
case SENSOR_TYPE_PROXIMITY: { |
|
||||||
log_event.setProximity(data.distance); |
|
||||||
break; |
|
||||||
} |
|
||||||
case SENSOR_TYPE_LIGHT: |
|
||||||
log_event.setLight(data.light); |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
log_i++; |
|
||||||
} |
|
||||||
|
|
||||||
pm.send("sensorEvents", msg); |
|
||||||
|
|
||||||
if (re_init_sensors) { |
|
||||||
LOGE("Resetting sensors"); |
|
||||||
re_init_sensors = false; |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
// Check whether to go into low power mode at 5Hz
|
|
||||||
if (frame % 20 == 0) { |
|
||||||
sm.update(0); |
|
||||||
bool offroad = !sm["deviceState"].getDeviceState().getStarted(); |
|
||||||
if (low_power_mode != offroad) { |
|
||||||
for (auto &s : sensors) { |
|
||||||
device->activate(device, s.first, 0); |
|
||||||
if (!offroad || offroad_sensors.find(s.first) != offroad_sensors.end()) { |
|
||||||
device->activate(device, s.first, 1); |
|
||||||
} |
|
||||||
} |
|
||||||
low_power_mode = offroad; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
frame++; |
|
||||||
} |
|
||||||
sensors_close(device); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
}// Namespace end
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) { |
|
||||||
setpriority(PRIO_PROCESS, 0, -18); |
|
||||||
signal(SIGPIPE, (sighandler_t)sigpipe_handler); |
|
||||||
|
|
||||||
sensor_loop(); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +0,0 @@ |
|||||||
libqpOASES_e.so.3.1 |
|
Binary file not shown.
Binary file not shown.
@ -1,3 +0,0 @@ |
|||||||
git clone https://github.com/CyanogenMod/android_frameworks_native.git && cd android_frameworks_native |
|
||||||
git reset --hard b22bca465e55618a949d9cbdea665a1a3a831241 |
|
||||||
cp -r include ~/one/third_party/android_frameworks_native/ |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue