-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathpushAppResources.py
More file actions
executable file
·170 lines (135 loc) · 5.72 KB
/
pushAppResources.py
File metadata and controls
executable file
·170 lines (135 loc) · 5.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#
# SPDX-FileCopyrightText: Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
#
# SPDX-License-Identifier: Apache-2.0
#
"""
Utility script to push resources needed by the app onto device.
Pushes local models, configuration files and test images to an Android device using ADB.
Supports:
- STT model files (flat files)
- Configuration JSON files (flat files)
- LLM model files (organized under a framework subdirectory, e.g., llama.cpp)
Usage:
STT / Config:
python3 pushModel.py <local_dir> <device_dir>
LLM:
python3 pushModel.py <local_dir> <device_dir> --llm_framework <llm_framework>
Arguments:
local_dir Path to the local folder containing files to push
device_dir Target directory on the Android device
llm_framework Optional. Subfolder to push from (e.g., 'llama.cpp').
Applies only for LLM models.
Behavior:
- For LLM: Adds the framework subfolder to local and device paths
- Creates remote directories with `mkdir -p`
- Adds executable permission (`chmod +x`) to the LLM framework directory
- Skips files that are already up to date (by comparing md5 checksums)
- Falls back to using `cat >` if `adb push` fails
"""
from argparse import ArgumentParser
import os
import sys
import subprocess
import hashlib
import logging
def md5sum(filepath):
"""
Calculate the MD5 checksum of a local file.
@param filepath: Path to the local file
@return: Hexadecimal MD5 checksum string
"""
hash_md5 = hashlib.md5()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
hash_md5.update(chunk)
return hash_md5.hexdigest()
def adb_md5sum(device_path):
"""
Retrieve the MD5 checksum of a file on the Android device.
@param device_path: Full path to the file on the device
@return: MD5 checksum string or None if file does not exist
"""
try:
result = subprocess.check_output(['adb', 'shell', f'md5sum "{device_path}"'], stderr=subprocess.DEVNULL)
return result.decode().split()[0]
except subprocess.CalledProcessError:
return None
def adb_delete_file(device_path):
"""
Delete file on the Android device.
@param device_path: Full path to the file on the device
"""
try:
result = subprocess.check_output(['adb', 'shell', f'rm "{device_path}"'], stderr=subprocess.DEVNULL)
logging.info(f"adb delete succeed: {device_path}")
except subprocess.CalledProcessError:
logging.info(f"adb delete failed: {device_path}")
def push_file(local_path, device_path):
"""
Push a local file to the device using `adb push`.
Falls back to `cat >` if ADB fails.
@param local_path: Path to the local file
@param device_path: Destination path on the Android device
"""
try:
logging.info(f"Pushing: {device_path}")
subprocess.run(['adb', 'push', local_path, device_path], check=True)
except subprocess.CalledProcessError:
logging.info(f"adb push failed, retrying with cat > {device_path}")
with open(local_path, 'rb') as f:
subprocess.run(['adb', 'shell', f'cat > "{device_path}"'], input=f.read(), check=True)
def adb_mkdir(path):
logging.info(f"mkdir'ing and chowning {path}")
subprocess.run(['adb', 'shell', f'mkdir -p "{path}"'], check=True)
subprocess.run(['adb', 'shell', f'chmod +xr "{path}"'], check=True)
def sync_dir(local_dir, device_dir):
"""
Recursively sync files and folders from a local directory to a device directory via ADB.
Skips files that are already up to date (based on MD5 hash).
@param local_dir: Path to the local directory
@param device_dir: Destination directory on the Android device
"""
for name in os.listdir(local_dir):
local_path = os.path.join(local_dir, name)
device_path = f"{device_dir}/{name}"
if os.path.isdir(local_path):
# Recursively sync subdirectories
logging.info(f"Entering directory: {local_path}")
adb_mkdir(device_path)
sync_dir(local_path, device_path)
else:
# Sync individual files
local_hash = md5sum(local_path)
remote_hash = adb_md5sum(device_path)
if local_hash != remote_hash:
logging.info(f"Updating file: {device_path}")
adb_delete_file(device_path)
push_file(local_path, device_path)
subprocess.run(['adb', 'shell', f'chmod +xr "{device_path}"'], check=True)
else:
logging.info(f"{device_path} is up to date, skipping.")
def main():
logging.basicConfig(filename="download.log", level=logging.DEBUG)
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))
parser = ArgumentParser(description="Push local model/config files to an Android device using ADB.")
parser.add_argument("local_dir",
help="Path to the local model/config directory")
parser.add_argument("device_dir",
help="Path to the destination directory on the device")
parser.add_argument("--llm_framework",
help="Optional: LLM framework subdirectory (e.g., llama.cpp)",
default=None)
args = parser.parse_args()
local_dir = os.path.abspath(args.local_dir)
device_dir = args.device_dir
is_llm = args.llm_framework is not None
llm_framework = args.llm_framework
subprocess.run(['adb', 'shell', f'mkdir -p "{device_dir}"'], check=True)
if not os.path.isdir(local_dir):
logging.info(f"Directory does not exist: {local_dir}, skipping.")
return
sync_dir(local_dir, device_dir)
logging.info("Done.")
if __name__ == "__main__":
main()