release v3.0.0

This commit is contained in:
hyugogirubato
2025-06-09 18:12:49 +02:00
parent 2f74b4c5b7
commit 82d6a903dd
28 changed files with 5384 additions and 1620 deletions
+171
View File
@@ -0,0 +1,171 @@
from pathlib import Path
# https://cs.android.com/android/platform/superproject/+/android14-qpr3-release:bionic/libc/libc.map.txt
NATIVE_C_API = (
'__assert', '__assert2', '__atomic_cmpxchg', '__atomic_dec', '__atomic_inc', '__atomic_swap', '__b64_ntop',
'__b64_pton', '__cmsg_nxthdr', '__connect', '__ctype_get_mb_cur_max', '__cxa_atexit', '__cxa_finalize',
'__cxa_thread_atexit_impl', '__dn_comp', '__dn_count_labels', '__dn_skipname', '__epoll_pwait', '__errno', '__exit',
'__fadvise64', '__fbufsize', '__fcntl64', '__FD_CLR_chk', '__FD_ISSET_chk', '__FD_SET_chk', '__fgets_chk', '__flbf',
'__fp_nquery', '__fp_query', '__fpclassify', '__fpclassifyd', '__fpclassifyf', '__fpclassifyl', '__fpending',
'__fpurge', '__freadable', '__fsetlocking', '__fstatfs64', '__fwritable', '__get_h_errno', '__getcpu', '__getcwd',
'__getpid', '__getpriority', '__gnu_basename', '__gnu_strerror_r', '__hostalias', '__ioctl', '__isfinite',
'__isfinitef', '__isfinitel', '__isinf', '__isinff', '__isinfl', '__isnan', '__isnanf', '__isnanl', '__isnormal',
'__isnormalf', '__isnormall', '__isthreaded', '__libc_current_sigrtmax', '__libc_current_sigrtmin', '__libc_init',
'__llseek', '__loc_aton', '__loc_ntoa', '__memchr_chk', '__memcpy_chk', '__memmove_chk', '__memrchr_chk',
'__memset_chk', '__mmap2', '__ns_format_ttl', '__ns_get16', '__ns_get32', '__ns_initparse', '__ns_makecanon',
'__ns_msg_getflag', '__ns_name_compress', '__ns_name_ntol', '__ns_name_ntop', '__ns_name_pack', '__ns_name_pton',
'__ns_name_rollback', '__ns_name_skip', '__ns_name_uncompress', '__ns_name_unpack', '__ns_parserr', '__ns_put16',
'__ns_put32', '__ns_samename', '__ns_skiprr', '__ns_sprintrr', '__ns_sprintrrf', '__open_2', '__openat',
'__openat_2', '__p_cdname', '__p_cdnname', '__p_class', '__p_class_syms', '__p_fqname', '__p_fqnname', '__p_option',
'__p_query', '__p_rcode', '__p_secstodate', '__p_time', '__p_type', '__p_type_syms', '__poll_chk', '__ppoll',
'__ppoll_chk', '__ppoll64_chk', '__pread64_chk', '__pread_chk', '__progname', '__pselect6', '__pthread_cleanup_pop',
'__pthread_cleanup_push', '__ptrace', '__putlong', '__putshort', '__read_chk', '__readlink_chk', '__readlinkat_chk',
'__reboot', '__recvfrom_chk', '__register_atfork', '__res_close', '__res_dnok', '__res_hnok', '__res_hostalias',
'__res_isourserver', '__res_mailok', '__res_nameinquery', '__res_nclose', '__res_ninit', '__res_nmkquery',
'__res_nquery', '__res_nquerydomain', '__res_nsearch', '__res_nsend', '__res_ownok', '__res_queriesmatch',
'__res_querydomain', '__res_send', '__res_send_setqhook', '__res_send_setrhook', '__rt_sigaction',
'__rt_sigpending', '__rt_sigprocmask', '__rt_sigsuspend', '__rt_sigtimedwait', '__sched_cpualloc',
'__sched_cpucount', '__sched_cpufree', '__sched_getaffinity', '__set_thread_area', '__set_tid_address', '__set_tls',
'__sF', '__sigaction', '__snprintf_chk', '__socket', '__sprintf_chk', '__stack_chk_fail', '__stack_chk_guard',
'__statfs64', '__stpcpy_chk', '__stpncpy_chk', '__stpncpy_chk2', '__strcat_chk', '__strchr_chk', '__strcpy_chk',
'__strlcat_chk', '__strlcpy_chk', '__strlen_chk', '__strncat_chk', '__strncpy_chk', '__strncpy_chk2',
'__strrchr_chk', '__sym_ntop', '__sym_ntos', '__sym_ston', '__system_property_area_serial',
'__system_property_find', '__system_property_find_nth', '__system_property_foreach', '__system_property_get',
'__system_property_read', '__system_property_serial', '__system_property_set', '__timer_create', '__timer_delete',
'__timer_getoverrun', '__timer_gettime', '__timer_settime', '__umask_chk', '__vsnprintf_chk', '__vsprintf_chk',
'__waitid', '_ctype_', '_Exit', '_exit', '_flushlbf', '_getlong', '_getshort', '_longjmp',
'_resolv_delete_cache_for_net', '_resolv_flush_cache_for_net', '_resolv_set_nameservers_for_net', '_setjmp',
'_tolower', '_tolower_tab_', '_toupper', '_toupper_tab_', 'abort', 'abs', 'accept', 'accept4', 'access', 'acct',
'alarm', 'alphasort', 'alphasort64', 'android_set_abort_message', 'arc4random', 'arc4random_buf',
'arc4random_uniform', 'asctime', 'asctime64', 'asctime64_r', 'asctime_r', 'asprintf', 'at_quick_exit', 'atof',
'atoi', 'atol', 'atoll', 'basename', 'basename_r', 'bind', 'bindresvport', 'brk', 'bsearch', 'btowc', 'c16rtomb',
'c32rtomb', 'cacheflush', 'calloc', 'capget', 'capset', 'cfgetispeed', 'cfgetospeed', 'cfmakeraw', 'cfsetispeed',
'cfsetospeed', 'cfsetspeed', 'chdir', 'chmod', 'chown', 'chroot', 'clearenv', 'clearerr', 'clearerr_unlocked',
'clock', 'clock_getcpuclockid', 'clock_getres', 'clock_gettime', 'clock_nanosleep', 'clock_settime', 'clone',
'close', 'closedir', 'closelog', 'connect', 'creat', 'creat64', 'ctime', 'ctime64', 'ctime64_r', 'ctime_r',
'daemon', 'daylight', 'delete_module', 'difftime', 'dirfd', 'dirname', 'dirname_r', 'div', 'dn_expand', 'dprintf',
'drand48', 'dup', 'dup2', 'dup3', 'duplocale', 'endmntent', 'endservent', 'endutent', 'environ', 'epoll_create',
'epoll_create1', 'epoll_ctl', 'epoll_pwait', 'epoll_wait', 'erand48', 'err', 'error', 'error_at_line',
'error_message_count', 'error_one_per_line', 'error_print_progname', 'errx', 'ether_aton', 'ether_aton_r',
'ether_ntoa', 'ether_ntoa_r', 'eventfd', 'eventfd_read', 'eventfd_write', 'execl', 'execle', 'execlp', 'execv',
'execve', 'execvp', 'execvpe', 'exit', 'faccessat', 'fallocate', 'fallocate64', 'fchdir', 'fchmod', 'fchmodat',
'fchown', 'fchownat', 'fclose', 'fcntl', 'fdatasync', 'fdopen', 'fdopendir', 'fdprintf', 'feof', 'feof_unlocked',
'ferror', 'ferror_unlocked', 'fflush', 'ffs', 'fgetc', 'fgetln', 'fgetpos', 'fgets', 'fgetwc', 'fgetws',
'fgetxattr', 'fileno', 'flistxattr', 'flock', 'flockfile', 'fmemopen', 'fnmatch', 'fopen', 'fork', 'forkpty',
'fpathconf', 'fprintf', 'fpurge', 'fputc', 'fputs', 'fputwc', 'fputws', 'fread', 'free', 'freeaddrinfo',
'freelocale', 'fremovexattr', 'freopen', 'fscanf', 'fseek', 'fseeko', 'fsetpos', 'fsetxattr', 'fstat', 'fstat64',
'fstatat', 'fstatat64', 'fstatfs', 'fstatfs64', 'fstatvfs', 'fstatvfs64', 'fsync', 'ftell', 'ftello', 'ftok',
'ftruncate', 'ftruncate64', 'ftrylockfile', 'fts_children', 'fts_close', 'fts_open', 'fts_read', 'fts_set', 'ftw',
'ftw64', 'funlockfile', 'funopen', 'futimens', 'fwide', 'fwprintf', 'fwrite', 'fwscanf', 'gai_strerror',
'get_avphys_pages', 'get_nprocs', 'get_nprocs_conf', 'get_phys_pages', 'getaddrinfo', 'getauxval', 'getc',
'getc_unlocked', 'getchar', 'getchar_unlocked', 'getcwd', 'getdelim', 'getegid', 'getenv', 'geteuid', 'getgid',
'getgrgid', 'getgrnam', 'getgrouplist', 'getgroups', 'gethostbyaddr', 'gethostbyaddr_r', 'gethostbyname',
'gethostbyname2', 'gethostbyname2_r', 'gethostbyname_r', 'gethostent', 'gethostname', 'getitimer', 'getline',
'getlogin', 'getmntent', 'getmntent_r', 'getnameinfo', 'getnetbyaddr', 'getnetbyname', 'getopt', 'getopt_long',
'getopt_long_only', 'getpagesize', 'getpeername', 'getpgid', 'getpgrp', 'getpid', 'getppid', 'getpriority',
'getprogname', 'getprotobyname', 'getprotobynumber', 'getpt', 'getpwnam', 'getpwnam_r', 'getpwuid', 'getpwuid_r',
'getresgid', 'getresuid', 'getrlimit', 'getrlimit64', 'getrusage', 'gets', 'getservbyname', 'getservbyport',
'getservent', 'getsid', 'getsockname', 'getsockopt', 'gettid', 'gettimeofday', 'getuid', 'getutent', 'getwc',
'getwchar', 'getxattr', 'gmtime', 'gmtime64', 'gmtime64_r', 'gmtime_r', 'grantpt', 'herror', 'hstrerror', 'htonl',
'htons', 'if_indextoname', 'if_nametoindex', 'imaxabs', 'imaxdiv', 'inet_addr', 'inet_aton', 'inet_lnaof',
'inet_makeaddr', 'inet_netof', 'inet_network', 'inet_nsap_addr', 'inet_nsap_ntoa', 'inet_ntoa', 'inet_ntop',
'inet_pton', 'init_module', 'initgroups', 'initstate', 'inotify_add_watch', 'inotify_init', 'inotify_init1',
'inotify_rm_watch', 'insque', 'ioctl', 'isalnum', 'isalnum_l', 'isalpha', 'isalpha_l', 'isascii', 'isatty',
'isblank', 'isblank_l', 'iscntrl', 'iscntrl_l', 'isdigit', 'isdigit_l', 'isfinite', 'isfinitef', 'isfinitel',
'isgraph', 'isgraph_l', 'isinf', 'isinff', 'isinfl', 'islower', 'islower_l', 'isnan', 'isnanf', 'isnanl',
'isnormal', 'isnormalf', 'isnormall', 'isprint', 'isprint_l', 'ispunct', 'ispunct_l', 'isspace', 'isspace_l',
'isupper', 'isupper_l', 'iswalnum', 'iswalnum_l', 'iswalpha', 'iswalpha_l', 'iswblank', 'iswblank_l', 'iswcntrl',
'iswcntrl_l', 'iswctype', 'iswctype_l', 'iswdigit', 'iswdigit_l', 'iswgraph', 'iswgraph_l', 'iswlower',
'iswlower_l', 'iswprint', 'iswprint_l', 'iswpunct', 'iswpunct_l', 'iswspace', 'iswspace_l', 'iswupper',
'iswupper_l', 'iswxdigit', 'iswxdigit_l', 'isxdigit', 'isxdigit_l', 'jrand48', 'kill', 'killpg', 'klogctl', 'labs',
'lchown', 'lcong48', 'ldexp', 'ldiv', 'lfind', 'lgetxattr', 'link', 'linkat', 'listen', 'listxattr', 'llabs',
'lldiv', 'llistxattr', 'localeconv', 'localtime', 'localtime64', 'localtime64_r', 'localtime_r', 'login_tty',
'longjmp', 'lrand48', 'lremovexattr', 'lsearch', 'lseek', 'lseek64', 'lsetxattr', 'lstat', 'lstat64', 'madvise',
'mallinfo', 'malloc', 'malloc_info', 'malloc_usable_size', 'mbrlen', 'mbrtoc16', 'mbrtoc32', 'mbrtowc', 'mbsinit',
'mbsnrtowcs', 'mbsrtowcs', 'mbstowcs', 'mbtowc', 'memalign', 'memccpy', 'memchr', 'memcmp', 'memcpy', 'memmem',
'memmove', 'mempcpy', 'memrchr', 'memset', 'mincore', 'mkdir', 'mkdirat', 'mkdtemp', 'mkfifo', 'mkfifoat', 'mknod',
'mknodat', 'mkostemp', 'mkostemp64', 'mkostemps', 'mkostemps64', 'mkstemp', 'mkstemp64', 'mkstemps', 'mkstemps64',
'mktemp', 'mktime', 'mktime64', 'mlock', 'mlockall', 'mmap', 'mmap64', 'mount', 'mprotect', 'mrand48', 'mremap',
'msync', 'munlock', 'munlockall', 'munmap', 'nanosleep', 'newlocale', 'nftw', 'nftw64', 'nice', 'nrand48',
'ns_format_ttl', 'ns_get16', 'ns_get32', 'ns_initparse', 'ns_makecanon', 'ns_msg_getflag', 'ns_name_compress',
'ns_name_ntol', 'ns_name_ntop', 'ns_name_pack', 'ns_name_pton', 'ns_name_rollback', 'ns_name_skip',
'ns_name_uncompress', 'ns_name_unpack', 'ns_parserr', 'ns_put16', 'ns_put32', 'ns_samename', 'ns_skiprr',
'ns_sprintrr', 'ns_sprintrrf', 'nsdispatch', 'ntohl', 'ntohs', 'open', 'open64', 'open_memstream',
'open_wmemstream', 'openat', 'openat64', 'opendir', 'openlog', 'openpty', 'optarg', 'opterr', 'optind', 'optopt',
'optreset', 'pathconf', 'pause', 'pclose', 'perror', 'personality', 'pipe', 'pipe2', 'poll', 'popen',
'posix_fadvise', 'posix_fadvise64', 'posix_fallocate', 'posix_fallocate64', 'posix_madvise', 'posix_memalign',
'posix_openpt', 'ppoll', 'prctl', 'pread', 'pread64', 'printf', 'prlimit', 'prlimit64', 'process_vm_readv',
'process_vm_writev', 'pselect', 'psiginfo', 'psignal', 'pthread_atfork', 'pthread_attr_destroy',
'pthread_attr_getdetachstate', 'pthread_attr_getguardsize', 'pthread_attr_getschedparam',
'pthread_attr_getschedpolicy', 'pthread_attr_getscope', 'pthread_attr_getstack', 'pthread_attr_getstacksize',
'pthread_attr_init', 'pthread_attr_setdetachstate', 'pthread_attr_setguardsize', 'pthread_attr_setschedparam',
'pthread_attr_setschedpolicy', 'pthread_attr_setscope', 'pthread_attr_setstack', 'pthread_attr_setstacksize',
'pthread_cond_broadcast', 'pthread_cond_destroy', 'pthread_cond_init', 'pthread_cond_signal',
'pthread_cond_timedwait', 'pthread_cond_timedwait_monotonic', 'pthread_cond_timedwait_monotonic_np',
'pthread_cond_timedwait_relative_np', 'pthread_cond_timeout_np', 'pthread_cond_wait', 'pthread_condattr_destroy',
'pthread_condattr_getclock', 'pthread_condattr_getpshared', 'pthread_condattr_init', 'pthread_condattr_setclock',
'pthread_condattr_setpshared', 'pthread_create', 'pthread_detach', 'pthread_equal', 'pthread_exit',
'pthread_getattr_np', 'pthread_getcpuclockid', 'pthread_getschedparam', 'pthread_getspecific', 'pthread_gettid_np',
'pthread_join', 'pthread_key_create', 'pthread_key_delete', 'pthread_kill', 'pthread_mutex_destroy',
'pthread_mutex_init', 'pthread_mutex_lock', 'pthread_mutex_lock_timeout_np', 'pthread_mutex_timedlock',
'pthread_mutex_trylock', 'pthread_mutex_unlock', 'pthread_mutexattr_destroy', 'pthread_mutexattr_getpshared',
'pthread_mutexattr_gettype', 'pthread_mutexattr_init', 'pthread_mutexattr_setpshared', 'pthread_mutexattr_settype',
'pthread_once', 'pthread_rwlock_destroy', 'pthread_rwlock_init', 'pthread_rwlock_rdlock',
'pthread_rwlock_timedrdlock', 'pthread_rwlock_timedwrlock', 'pthread_rwlock_tryrdlock', 'pthread_rwlock_trywrlock',
'pthread_rwlock_unlock', 'pthread_rwlock_wrlock', 'pthread_rwlockattr_destroy', 'pthread_rwlockattr_getkind_np',
'pthread_rwlockattr_getpshared', 'pthread_rwlockattr_init', 'pthread_rwlockattr_setkind_np',
'pthread_rwlockattr_setpshared', 'pthread_self', 'pthread_setname_np', 'pthread_setschedparam',
'pthread_setspecific', 'pthread_sigmask', 'ptrace', 'ptsname', 'ptsname_r', 'putc', 'putc_unlocked', 'putchar',
'putchar_unlocked', 'putenv', 'puts', 'pututline', 'putw', 'putwc', 'putwchar', 'pvalloc', 'pwrite', 'pwrite64',
'qsort', 'quick_exit', 'raise', 'rand', 'rand_r', 'random', 'read', 'readahead', 'readdir', 'readdir64',
'readdir64_r', 'readdir_r', 'readlink', 'readlinkat', 'readv', 'realloc', 'realpath', 'reboot', 'recv', 'recvfrom',
'recvmmsg', 'recvmsg', 'regcomp', 'regerror', 'regexec', 'regfree', 'remove', 'removexattr', 'remque', 'rename',
'renameat', 'res_init', 'res_mkquery', 'res_query', 'res_search', 'rewind', 'rewinddir', 'rmdir', 'sbrk', 'scandir',
'scandir64', 'scanf', 'sched_get_priority_max', 'sched_get_priority_min', 'sched_getaffinity', 'sched_getcpu',
'sched_getparam', 'sched_getscheduler', 'sched_rr_get_interval', 'sched_setaffinity', 'sched_setparam',
'sched_setscheduler', 'sched_yield', 'seed48', 'seekdir', 'select', 'sem_close', 'sem_destroy', 'sem_getvalue',
'sem_init', 'sem_open', 'sem_post', 'sem_timedwait', 'sem_trywait', 'sem_unlink', 'sem_wait', 'send', 'sendfile',
'sendfile64', 'sendmmsg', 'sendmsg', 'sendto', 'setbuf', 'setbuffer', 'setegid', 'setenv', 'seteuid', 'setfsgid',
'setfsuid', 'setgid', 'setgroups', 'sethostname', 'setitimer', 'setjmp', 'setlinebuf', 'setlocale', 'setlogmask',
'setmntent', 'setns', 'setpgid', 'setpgrp', 'setpriority', 'setprogname', 'setregid', 'setresgid', 'setresuid',
'setreuid', 'setrlimit', 'setrlimit64', 'setservent', 'setsid', 'setsockopt', 'setstate', 'settimeofday', 'setuid',
'setutent', 'setvbuf', 'setxattr', 'shutdown', 'sigaction', 'sigaddset', 'sigaltstack', 'sigblock', 'sigdelset',
'sigemptyset', 'sigfillset', 'siginterrupt', 'sigismember', 'siglongjmp', 'signal', 'signalfd', 'sigpending',
'sigprocmask', 'sigqueue', 'sigsetjmp', 'sigsetmask', 'sigsuspend', 'sigtimedwait', 'sigwait', 'sigwaitinfo',
'sleep', 'snprintf', 'socket', 'socketpair', 'splice', 'sprintf', 'srand', 'srand48', 'srandom', 'sscanf', 'stat',
'stat64', 'statfs', 'statfs64', 'statvfs', 'statvfs64', 'stderr', 'stdin', 'stdout', 'stpcpy', 'stpncpy',
'strcasecmp', 'strcasecmp_l', 'strcasestr', 'strcat', 'strchr', 'strcmp', 'strcoll', 'strcoll_l', 'strcpy',
'strcspn', 'strdup', 'strerror', 'strerror_l', 'strerror_r', 'strftime', 'strftime_l', 'strlcat', 'strlcpy',
'strlen', 'strncasecmp', 'strncasecmp_l', 'strncat', 'strncmp', 'strncpy', 'strndup', 'strnlen', 'strpbrk',
'strptime', 'strrchr', 'strsep', 'strsignal', 'strspn', 'strstr', 'strtod', 'strtof', 'strtoimax', 'strtok',
'strtok_r', 'strtol', 'strtold', 'strtold_l', 'strtoll', 'strtoll_l', 'strtoul', 'strtoull', 'strtoull_l',
'strtoumax', 'strxfrm', 'strxfrm_l', 'swapoff', 'swapon', 'swprintf', 'swscanf', 'symlink', 'symlinkat', 'sync',
'sys_siglist', 'sys_signame', 'syscall', 'sysconf', 'sysinfo', 'syslog', 'system', 'tcdrain', 'tcflow', 'tcflush',
'tcgetattr', 'tcgetpgrp', 'tcgetsid', 'tcsendbreak', 'tcsetattr', 'tcsetpgrp', 'tdelete', 'tdestroy', 'tee',
'telldir', 'tempnam', 'tfind', 'tgkill', 'time', 'timegm', 'timegm64', 'timelocal', 'timelocal64', 'timer_create',
'timer_delete', 'timer_getoverrun', 'timer_gettime', 'timer_settime', 'timerfd_create', 'timerfd_gettime',
'timerfd_settime', 'times', 'timezone', 'tmpfile', 'tmpnam', 'toascii', 'tolower', 'tolower_l', 'toupper',
'toupper_l', 'towlower', 'towlower_l', 'towupper', 'towupper_l', 'truncate', 'truncate64', 'tsearch', 'ttyname',
'ttyname_r', 'twalk', 'tzname', 'tzset', 'umask', 'umount', 'umount2', 'uname', 'ungetc', 'ungetwc', 'unlink',
'unlinkat', 'unlockpt', 'unsetenv', 'unshare', 'uselocale', 'usleep', 'utime', 'utimensat', 'utimes', 'utmpname',
'valloc', 'vasprintf', 'vdprintf', 'verr', 'verrx', 'vfdprintf', 'vfork', 'vfprintf', 'vfscanf', 'vfwprintf',
'vfwscanf', 'vmsplice', 'vprintf', 'vscanf', 'vsnprintf', 'vsprintf', 'vsscanf', 'vswprintf', 'vswscanf', 'vsyslog',
'vwarn', 'vwarnx', 'vwprintf', 'vwscanf', 'wait', 'wait4', 'waitid', 'waitpid', 'warn', 'warnx', 'wcpcpy',
'wcpncpy', 'wcrtomb', 'wcscasecmp', 'wcscasecmp_l', 'wcscat', 'wcschr', 'wcscmp', 'wcscoll', 'wcscoll_l', 'wcscpy',
'wcscspn', 'wcsdup', 'wcsftime', 'wcslcat', 'wcslcpy', 'wcslen', 'wcsncasecmp', 'wcsncasecmp_l', 'wcsncat',
'wcsncmp', 'wcsncpy', 'wcsnlen', 'wcsnrtombs', 'wcspbrk', 'wcsrchr', 'wcsrtombs', 'wcsspn', 'wcsstr', 'wcstod',
'wcstof', 'wcstoimax', 'wcstok', 'wcstol', 'wcstold', 'wcstold_l', 'wcstoll', 'wcstoll_l', 'wcstombs', 'wcstoul',
'wcstoull', 'wcstoull_l', 'wcstoumax', 'wcswidth', 'wcsxfrm', 'wcsxfrm_l', 'wctob', 'wctomb', 'wctype', 'wctype_l',
'wcwidth', 'wmemchr', 'wmemcmp', 'wmemcpy', 'wmemmove', 'wmempcpy', 'wmemset', 'wprintf', 'write', 'writev',
'wscanf', 'main'
)
# https://github.com/kaltura/kaltura-device-info-android
DRM_PLAYER = {
'name': 'Kaltura Device Info',
'package': 'com.kaltura.kalturadeviceinfo',
'url': 'https://github.com/kaltura/kaltura-device-info-android/releases/download/t3/kaltura-device-info-release.apk',
'path': Path(__file__).parent.parent / 'docs' / 'server' / 'kaltura.apk'
}
DRM_WEB = 'https://bitmovin.com/demos/drm'
+371
View File
@@ -0,0 +1,371 @@
import logging
import re
import shutil
from subprocess import run
from typing import Optional, List, Tuple
from pathlib import Path
import frida
import requests
from frida.core import Device
# Suppress urllib3 warnings
logging.getLogger('urllib3.connectionpool').setLevel(logging.ERROR)
def shell(prompt: List[str]) -> Tuple[bool, str]:
"""
Executes a shell command and returns its success status along with the output.
Args:
prompt (List[str]): A list representing the command and its arguments to be executed.
Returns:
Tuple[bool, str]: A tuple where the first value is True if the command failed,
and the second value is the decoded standard output (stdout) string.
Example:
shell(["ls", "-l"])
(False, "total 0\n-rw-r--r-- 1 user ...")
Note:
The return status is inverted (True means failure), which should be considered
when checking execution outcomes.
"""
# Convert all arguments to strings in case any are not
prompt = list(map(str, prompt))
# Uncomment for debugging shell command execution
# logging.getLogger('Shell').debug('Executing command: %s', ' '.join(prompt))
try:
# Execute the command and capture the output
# TODO: Redirect standard output and error in PIP
sp = run(prompt, capture_output=True)
# Return True if the command failed (non-zero exit), along with stdout
return sp.returncode != 0, sp.stdout.decode('utf-8').strip()
except KeyboardInterrupt:
return False, ''
class Remote:
"""
Handles ADB-based operations on Android devices using Frida.
This class provides a set of utilities for interacting with Android devices connected via ADB,
including retrieving system properties, managing applications, enumerating processes, and executing shell commands.
"""
def __init__(self, serial: Optional[str] = None, timeout: int = 5):
"""
Initializes and connects to an Android device using ADB and Frida.
Args:
serial (Optional[str]): Specific device serial number. If not provided,
the first connected USB device will be used.
timeout (int): Timeout (in seconds) for connecting to the device.
Raises:
EnvironmentError: If ADB is not found in the system PATH.
Exception: If connection to the Android device fails.
KeyError: If essential properties (e.g., SDK or ABI) cannot be retrieved.
"""
self.logger = logging.getLogger('Remote')
# Ensure ADB is installed and available in the system PATH
if not shutil.which('adb'):
raise EnvironmentError(
'ADB is not recognized as an environment variable. '
'Ensure ADB is installed and refer to the documentation: '
'https://github.com/hyugogirubato/KeyDive/blob/main/docs/PACKAGE.md#adb-android-debug-bridge'
)
# Start the ADB server if it's not already running
sp = shell(['adb', 'start-server'])
if sp[0]:
self.logger.warning('Unable to start ADB server (Error: %s)', sp[1])
# Attempt to connect to the device (or default to the first USB device)
try:
self.socket: Device = frida.get_device(serial, timeout) if serial else frida.get_usb_device(timeout)
self.logger.info('Connected to device: %s (%s)', self.socket.name, self.socket.id)
except Exception as e:
self.logger.critical('Could not connect to any device: %s', e)
raise e
# Construct the shell command prefix using the connected device ID
self.__prefix = ['adb', '-s', self.socket.id, 'shell']
# Retrieve system properties (e.g., SDK version, CPU ABI)
properties = self.enumerate_properties()
try:
self.sdk = properties['ro.build.version.sdk']
self.abi = properties['ro.product.cpu.abi']
except Exception as e:
raise KeyError('Failed to enumerate properties: %s', e)
self.logger.info('SDK API: %s', self.sdk)
self.logger.info('ABI CPU: %s', self.abi)
def enumerate_properties(self) -> dict:
"""
Retrieves Android system properties from the connected device via ADB.
This method parses the output of the `getprop` shell command, casting values
to appropriate Python types (e.g., integers or booleans) when possible. It is
useful for gathering device configuration, build info, and environment metadata.
Returns:
dict: A dictionary of system properties where keys are property names and
values are their corresponding parsed values.
"""
# https://source.android.com/docs/core/architecture/configuration/add-system-properties?#shell-commands
properties = {}
# Execute shell command to fetch all system properties using 'getprop'
sp = shell([*self.__prefix, 'getprop', '|', 'strings'])
if sp[0]:
self.logger.error('Unable to retrieve system properties from the device (error: %s)', sp[1])
return properties
# Parse each line of the output and extract key-value pairs
for line in sp[1].splitlines():
match = re.match(r'\[(.*?)]: \[(.*?)]', line)
if match:
key, value = match.groups()
# Try casting numeric strings to integers
if value.isdigit():
value = int(value)
# Try converting string booleans to Python boolean
elif value.lower() in ('true', 'false'):
value = value.lower() == 'true'
# Store the parsed key-value pair
properties[key] = value
return properties
def enumerate_processes(self, pids: List[int] = None, names: List[str] = None) -> dict:
"""
Retrieves a list of running processes from the connected Android device.
This method uses ADB to run the `ps` command, processes the output, and filters
the result by provided process IDs or names if specified. It gracefully handles
variability in output format across Android versions.
Args:
pids (List[int], optional): A list of process IDs to filter against. Only
processes with these PIDs will be included in the result.
names (List[str], optional): A list of process names to filter against.
Only processes with these names will be included in the result.
Returns:
dict: A dictionary mapping process IDs (int) to process names (str).
Exception:
Logs and returns an empty dictionary if the `ps` command fails to execute.
"""
# https://github.com/frida/frida/issues/1225#issuecomment-604181822
processes = {}
# Attempt to get the list of processes using the 'ps -A' command
prompt = [*self.__prefix, 'ps']
sp = shell([*prompt, '-A'])
lines = sp[1].splitlines()
# Retry with simpler 'ps' if output seems too short (older Android or restricted shell)
if len(lines) < 10:
sp = shell(prompt)
if sp[0]:
self.logger.error('Failed to execute ps command (Error: %s)', sp[1])
return processes
lines = sp[1].splitlines()
# Iterate through process list, skipping the header
for line in lines[1:]:
try:
parts = line.split() # USER,PID,PPID,VSZ,RSS,WCHAN,ADDR,S,NAME
pid = int(parts[1]) # Extract the PID from the 2nd column
name = ' '.join(parts[8:]).strip() # Extract the process name (column 9+)
# Handle cases where process name might be in brackets (e.g., kernel threads)
name = name if name.startswith('[') else Path(name).name
# Apply optional filters
if (pids and pid not in pids) or (names and name not in names):
continue
processes[pid] = name
except Exception as e:
# Suppress and skip lines that cannot be parsed correctly
pass
return processes
def enumerate_applications(self, user: bool = True, system: bool = False) -> dict:
"""
Lists installed Android applications on the connected device.
This method uses ADB's package manager (pm) to list installed apps,
with optional filtering for user-installed or system apps. It returns
a mapping of package names to their corresponding APK file paths.
Args:
user (bool): If True, include user-installed applications (default: True).
system (bool): If True, include system applications (default: False).
Returns:
dict: A dictionary where the keys are package names (str) and the values
are file paths (str) to their APKs.
"""
applications = {}
# Validate input to ensure at least one filter is active
if not user and not system:
return applications # Nothing to return if both are disabled
# Construct the command to retrieve package info
prompt = [*self.__prefix, 'pm', 'list', 'packages', '-f']
if user and not system:
prompt.append('-3') # Filter only user-installed apps
elif not user and system:
prompt.append('-s') # Filter only system apps
# Run the shell command to get the list of packages
sp = shell(prompt)
if sp[0]:
self.logger.error('Unable to list installed apps (Error: %s)', sp[0])
return applications
# Parse command output line-by-line
for line in sp[1].splitlines():
try:
# Example line format: package:/data/app/com.example.app-1/base.apk=com.example.app
path, package = line.strip().split(':', 1)[1].rsplit('=', 1)
applications[package] = path
except Exception as e:
# Skip lines that do not conform to expected structure
pass
return applications
def open_url(self, url: str) -> bool:
"""
Launches the specified URL on the connected Android device using the default browser.
This uses Android's Activity Manager (am) to start an intent with the action
`android.intent.action.VIEW`, which is the standard way to launch URLs.
Args:
url (str): The web address to open on the device (e.g., "https://example.com").
Returns:
bool: True if the command executed successfully and the intent was launched;
False if the shell command failed or was rejected by the system.
"""
# Execute the shell command to open the URL using the Android 'am' (Activity Manager) command.
sp = shell([*self.__prefix, 'am', 'start', '-a', 'android.intent.action.VIEW', '-d', url])
if sp[0]:
self.logger.error('Failed to open URL: %s (Error: %s)', url, sp[1])
# Return True if command succeeded (sp[0] is 0), False otherwise
return not sp[0]
def install_application(self, path: Optional[Path] = None, url: Optional[str] = None) -> bool:
"""
Installs an APK on the connected Android device from either a local file or a remote URL.
Args:
path (Optional[Path]): Path to the APK file stored locally.
url (Optional[str]): Direct URL to download the APK from.
Returns:
bool: True if the application was successfully installed, False otherwise.
"""
# Prepare the shell command for installation
prompt = [*self.__prefix[:-1], 'install']
# If a valid local path is provided, attempt to install the APK from that file
if path and path.is_file():
sp = shell([*prompt, path]) # Run the installation command with the local file path
if not sp[0]:
return True # Installation succeeded
self.logger.error('Could not install the APK from local path: %s (Error: %s)', path, sp[1])
# If a URL is provided, try to download the APK and install it temporarily
status = False
if url:
file = Path('tmp.apk') # Temporary file to store the downloaded APK
try:
# Send a GET request to download the APK
r = requests.request(
method='GET',
url=url,
headers={
'Accept': '*/*',
'User-Agent': 'KeyDive/ADB'
}
)
r.raise_for_status()
# Write the downloaded content to a temporary APK file
file.write_bytes(r.content)
# Attempt to install the downloaded APK
status = self.install_application(path=file)
except Exception as e:
self.logger.error('Failed to download or install APK from URL: %s (Error: %s)', url, e)
finally:
# Clean up the temporary file regardless of success or failure
file.unlink(missing_ok=True)
return status
def start_application(self, package: str) -> bool:
"""
Attempts to start an Android application using its package name by locating
and invoking its main activity via the Activity Manager.
Args:
package (str): The package name of the application to start.
Returns:
bool: True if the application was successfully launched, False otherwise.
"""
# Get package information using dumpsys
sp = shell([*self.__prefix, 'dumpsys', 'package', package])
if sp[0]:
self.logger.error('Unable to retrieve package information (Error: %s)', sp[1])
return False
# Clean up output and filter out empty lines for consistency
lines = sp[1].splitlines()
lines = [l.strip() for l in lines if l.strip()]
# Scan for the MAIN intent line and extract the associated activity
for i, line in enumerate(lines):
if 'android.intent.action.MAIN' in line:
match = re.search(fr'({package}/[^ ]+)', lines[i + 1])
if match:
# Format: com.example.package/.MainActivity
main_activity = match.group()
# Attempt to start the application using the resolved activity
sp = shell([*self.__prefix, 'am', 'start', '-n', main_activity])
if not sp[0]:
return True
self.logger.error('Failed to start app %s (Error: %s)', package, sp[1])
break
# TODO: adb shell monkey -p com.topjohnwu.magisk -c android.intent.category.LAUNCHER 1
self.logger.error('No MAIN activity found for package "%s" or package not installed.', package)
return False
__all__ = ('Remote',)
+114
View File
@@ -0,0 +1,114 @@
import re
from typing import List, Optional, Tuple
class Vendor:
"""
Represents a vendor-specific configuration including process name, minimum SDK version,
OEM versioning information, and the name of a related native library.
This class is used to identify vendor-specific behaviors on Android devices and provides
regex-based helpers for pattern matching against processes and libraries.
"""
def __init__(
self,
process: str,
min_sdk: int,
min_oem: Tuple[int, str],
library: str
):
"""
Initializes a Vendor instance with identifying details.
Args:
process (str): The name of the process to identify for the vendor (can be pattern-based).
min_sdk (int): The minimum required Android SDK version for compatibility.
min_oem (Tuple[int, str]): Tuple defining the minimum OEM version, e.g., (version_code, version_name).
library (str): The shared library name or pattern used by the vendor.
"""
self.process = process
self.min_sdk = min_sdk
self.min_oem = min_oem
self.library = library
def __repr__(self) -> str:
"""
Generates a string representation of the current instance for debugging purposes.
This method is primarily used to help developers understand the state of the object
during logging or interactive debugging by displaying all attribute names and values
in a clean, readable format.
Returns:
str: A string showing the class name and its instance variables with their values.
"""
# Construct a formatted string with the class name and a list of key=value pairs from the instance's attributes
return '{name}({items})'.format(
name=self.__class__.__name__,
items=', '.join([f'{k}={repr(v)}' for k, v in self.__dict__.items()])
)
@staticmethod
def __pattern(value: str) -> str:
"""
Converts a given string into a regex-compatible pattern that allows for matching
flexible naming conventions found in Android service or library identifiers.
This is particularly useful when dealing with variant suffixes (e.g., `-lazy`, version tags),
or optional annotations (e.g., `@<version>` in `.so` or `.widevine` modules).
Args:
value (str): The base string (e.g., filename or service name) to convert into a regex pattern.
Returns:
str: A regex pattern that can be used to match common variants of the input string.
"""
return (value
.replace('-service', r'-service(?:-lazy)?')
.replace('.widevine', r'.widevine(?:@\S+)?')
.replace('.so', r'(?:@\S+)?.so')
.replace('.', r'\.'))
def is_process(self, value: str) -> bool:
"""
Determines whether the provided process name matches the expected vendor-specific process pattern.
This is useful when trying to detect or hook into a known target process,
especially when its name can include runtime-specific suffixes (e.g., versioned .so files or services).
Args:
value (str): The name of the process to evaluate.
Returns:
bool: True if the process name matches the generated pattern; otherwise, False.
"""
# Generate a regex pattern from the expected process name
pattern = self.__pattern(self.process)
# Check if the input value matches the regex pattern
return bool(re.match(pattern, value))
def get_library(self, values: List[dict]) -> Optional[dict]:
"""
Retrieves the first matching library dictionary from a provided list based on a dynamically generated regex pattern.
This method is useful when trying to identify a target library in a list of shared objects (e.g., `.so` files),
especially when the exact name may include version tags or runtime decorations (e.g., `libxyz@1.2.so`).
Args:
values (List[dict]): A list of dictionaries, each expected to contain a 'name' key with the library's filename.
Returns:
Optional[dict]: The first dictionary whose 'name' matches the expected regex pattern; returns None if no match is found.
"""
# Create a regex pattern based on the expected library name, allowing flexibility for suffixes or variants
pattern = self.__pattern(self.library)
# Iterate through the list and return the first dictionary with a matching 'name'
return next((v for v in values if re.match(pattern, v['name'])), None)
__all__ = ('Vendor',)