Операции с приложениями¶
В этой главе вы узнаете, как устанавливать, запускать и закрывать приложения, предоставлять или отзывать разрешения, отключать или включать приложения, а также воспроизводить любые Activity (scheme, Activity), включая неэкспортированные Activity, и т. д.
Установка приложения¶
Вам нужно подготовить APK для установки; поддерживаются одиночные APK Android, а также Split APK (например, XAPK).
Одиночный APK¶
Для одиночного APK-файла используйте следующий порядок установки
>>> session = d.create_install_session()
>>> session.write("/path/to/file.apk")
session: 2033102247
name: "base.apk"
writtenBytes: 5130176
totalBytes: 5130176
>>> session.commit()
session: 2033102247
package: "com.example.apk"
legacyStatus: 1
statusMessage: "INSTALL_SUCCEEDED: Session installed"
finished: true
committed: true
progress: 1
success: true
>>>
Split APK (например, XAPK)¶
Для приложений с несколькими APK вам, возможно, потребуется сначала распаковать общий архив. После распаковки вы обычно увидите набор файлов, похожий на следующий.
➜ apk_temp/ $ ls -l
total 350720
-rw-rw-r-- 1 root root 86M 1 26 1970 com.some.package.apk
-rw-rw-r-- 1 root root 772K 1 26 1970 config.ar.apk
-rw-rw-r-- 1 root root 68M 1 26 1970 config.arm64_v8a.apk
-rw-rw-r-- 1 root root 676K 1 26 1970 config.de.apk
-rw-rw-r-- 1 root root 478K 1 26 1970 config.en.apk
Устанавливайте их следующим образом. Обычно вам нужно установить основной пакет, языковые пакеты и пакеты нативных библиотек.
>>> session = d.create_install_session()
>>> session.write("com.example.app.apk", name="com.example.app.apk")
session: 1137194135
name: "com.example.app.apk"
writtenBytes: 89661970
totalBytes: 89661970
>>> session.write("config.de.apk", name="config.de.apk")
session: 1137194135
name: "config.de.apk"
writtenBytes: 692633
totalBytes: 90354603
>>> session.write("config.arm64_v8a.apk", name="config.arm64_v8a.apk")
session: 1137194135
name: "config.arm64_v8a.apk"
writtenBytes: 70978318
totalBytes: 161332921
>>> session.write("config.xxhdpi.apk", name="config.xxhdpi.apk")
session: 1137194135
name: "config.xxhdpi.apk"
writtenBytes: 6266292
totalBytes: 167599213
>>> session.commit()
session: 1137194135
package: "com.example.app"
legacyStatus: 1
statusMessage: "INSTALL_SUCCEEDED: Session installed"
finished: true
committed: true
progress: 0.900000036
success: true
>>>
Если во время установки произойдёт какая-либо ошибка, будет сразу вызвано исключение. Например, если в Split APK не был записан основной пакет.
>>> session = d.create_install_session()
>>> session.write("config.de.apk", name="config.de.apk")
session: 1658145271
name: "config.de.apk"
writtenBytes: 692633
totalBytes: 692633
>>> session.commit()
Traceback (most recent call last):
File "<console>", line 1, in <module>
....
raise self.remote_exception(exception)
lamda.exceptions.InstallPackageFailed: INSTALL_FAILED_INVALID_APK: Full install must include a base package
Параметры сессии¶
С помощью определённых параметров создания сессии можно выполнять установку поверх существующей, установку с понижением версии, автоматическую выдачу разрешений, установку для конкретного пользователя и т. д.
Вы можете использовать перечисленные выше параметры при создании сессии, как показано ниже.
session = d.create_install_session(request_downgrade=True)
Прочие случаи¶
Некоторые системы могут перехватывать установку или добавлять всплывающие окна проверки приложений, например проверку Google Play и т. п. В таком случае вы можете автоматизировать завершение установки — это возможно. Метод commit предоставляет фоновый режим установки. В примере ниже можно не ждать завершения и вручную запрашивать статус установки.
>>> session = d.create_install_session()
>>> session.write("/path/to/file.apk")
session: 1079135981
name: "base.apk"
writtenBytes: 5130176
totalBytes: 5130176
>>> session.commit(wait=False)
session: 1079135981
status: -2147483648
legacyStatus: -2147483648
statusMessage: "Commit sent"
committed: true
progress: 0.8
>>> session.status()
session: 1079135981
package: "com.example.app"
status: -2147483648
legacyStatus: -2147483648
statusMessage: "Commit sent"
committed: true
progress: 0.900000036
>>> session.status()
session: 1079135981
package: "com.example.app"
legacyStatus: 1
statusMessage: "INSTALL_SUCCEEDED: Session installed"
finished: true
committed: true
progress: 1
success: true
Таким образом, после начала установки вы можете с помощью другого кода обрабатывать всплывающие окна, возникающие в процессе установки. Конечно, использование Watcher для автоматических нажатий также является рабочим вариантом.
Список установленных приложений¶
Получение информации обо всех установленных на устройстве приложениях.
d.enumerate_installed_apps()
>>> d.enumerate_installed_apps()
[packageName: "com.android.uwb.resources"
label: "System UWB Resources"
uid: 10110
enabled: true
system: true
versionName: "T-initial"
, packageName: "com.android.adservices.api"
label: "Android System"
uid: 10105
enabled: true
system: true
versionName: "14"
...
Список запущенных приложений¶
Получение информации о приложениях, запущенных в данный момент в системе.
d.enumerate_running_processes()
>>> d.enumerate_running_processes()
[packages: "com.android.launcher3"
processName: "com.android.launcher3"
uid: 10084
pid: 2360
label: "Quickstep"
, packages: "com.google.android.gms"
processName: "com.google.android.gms.persistent"
uid: 10123
pid: 2765
label: "Google Play services"
, packages: "com.instagram.android"
processName: "com.instagram.android"
uid: 10150
pid: 5529
label: "Instagram"
...
>>> result = d.enumerate_running_processes()
>>> print(result[0].processName)
com.android.launcher3
Получение приложения по имени¶
Используйте общепринятое имя приложения для получения экземпляра приложения (если неизвестен Package ID).
app = d.get_application_by_name("微信")
Получение приложения по имени пакета¶
Используйте Package ID приложения для получения экземпляра приложения.
app = d.application("com.tencent.mm")
Получение приложения на переднем плане¶
Получение экземпляра приложения, которое в данный момент работает на переднем плане.
app = d.current_application()
Получение клонированного приложения¶
Получение экземпляра клонированного приложения (обычно клонированные приложения различаются по параметру user, а uid равен 999).
app = d.application("com.my.app", user=999)
Запуск приложения¶
Запускает это приложение.
app.start()
Закрытие приложения¶
Принудительно закрывает это приложение.
app.stop()
Проверка работы на переднем плане¶
Проверяет, работает ли приложение в данный момент на переднем плане.
app.is_foreground()
Получение информации о приложении¶
Получение информации о приложении, например версии и т. д.
app.info()
>>> app.info()
packageName: "com.android.settings"
uid: 1000
enabled: true
processName: "com.android.settings"
sourceDir: "/system/product/priv-app/Settings/Settings.apk"
dataDir: "/data/user_de/0/com.android.settings"
firstInstallTime: 1230739200000
lastUpdateTime: 1230768000000
versionCode: 1276
versionName: "10"
>>> result = app.info()
>>> print(result.processName)
'com.android.settings'
Проверка установки¶
Проверяет, установлено ли это приложение на устройстве.
app.is_installed()
Удаление приложения¶
Удаляет это приложение с устройства.
app.uninstall()
Запуск Activity приложения¶
Вы можете воспроизводить системные Activity и вызывать любые Activity любого приложения. Доступные параметры приведены ниже. Обратите внимание, что дополнительные данные (extras) поддерживают только типы boolean, int, short, long, double, float и string. Определение параметра flags см. в документации developer.android.com/reference/android/content/Intent.
from lamda.const import *
d.start_activity(action="*", category="*", component="*", extras={"boolean": False, "int": 1, "string": "abc", "float": 1.123}, flags=FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_CLEAR_TASK, data="*", debug=False)
Теперь возьмём в качестве примера результат, возвращаемый интерфейсом получения последних Activity. Вы можете напрямую воспроизвести последнюю системную Activity с помощью следующего кода.
activity = d.get_last_activities(count=5)[-1]
d.start_activity(**activity)
Если вы хотите запустить Activity в клонированном приложении, следующий код воспроизведёт Activity в клонированном приложении.
d.start_activity(**activity, user=999)
Ниже приведены несколько примеров для справки. Следующий вызов позвонит на сервисный номер 10000.
d.start_activity(action="android.intent.action.CALL", data="tel:10000")
Следующий вызов запустит приложение Settings; это почти эквивалентно прямому запуску приложения.
d.start_activity(action="android.intent.action.MAIN", category="android.intent.category.LAUNCHER", component="com.android.settings/.Settings")
Следующий вызов откроет приложение настроек в режиме отладки. Если вы когда-либо видели сообщение Waiting for debugger, этот вызов может быть вам полезен. Разумеется, ваше устройство или приложение должны быть отлаживаемыми. От предыдущего вызова он отличается только наличием параметра debug.
d.start_activity(action="android.intent.action.MAIN", category="android.intent.category.LAUNCHER", component="com.android.settings/.Settings", debug=True)
Следующий вызов сразу перейдёт на страницу настройки сертификатов.
d.start_activity(action="com.android.settings.TRUSTED_CREDENTIALS")
Список разрешений приложения¶
Этот интерфейс позволяет вывести список всех разрешений, объявленных приложением.
app.permissions()
>>> app.permissions()
['android.permission.REQUEST_NETWORK_SCORES', 'android.permission.WRITE_MEDIA_STORAGE', 'android.permission.WRITE_EXTERNAL_STORAGE', 'android.permission.READ_EXTERNAL_STORAGE', 'android.permission.WRITE_SETTINGS',...]
Предоставление разрешения приложению¶
Этот интерфейс используется для предоставления приложению соответствующих системных разрешений. Его следует вызывать, когда приложение ещё не запущено. Если вызвать его во время работы приложения при запросе разрешения, автоматическое предоставление не сработает.
from lamda.const import *
app.grant(PERMISSION_READ_PHONE_STATE, mode=GrantType.GRANT_ALLOW)
Это эквивалентно передаче полной строки с именем разрешения:
app.grant("android.permission.READ_PHONE_STATE", mode=GrantType.GRANT_ALLOW)
Параметр mode метода grant также поддерживает GrantType.GRANT_DENY, что означает явный отказ в разрешении, а также GrantType.GRANT_IGNORE. Значение GRANT_IGNORE особенное: оно означает, что разрешение приложению выдано, но фактически приложение не может им нормально пользоваться. Например, если приложение запрашивает камеру, при отказе с этим параметром камера в приложении может показывать чёрный экран.
Отзыв разрешения у приложения¶
Вызов этого интерфейса отзывает разрешение, выданное приложению. Его также следует вызывать до запуска приложения.
from lamda.const import *
app.revoke(PERMISSION_READ_PHONE_STATE)
Проверка предоставления разрешения¶
Этот интерфейс используется для проверки того, было ли определённое разрешение нормально предоставлено приложению.
from lamda.const import *
app.is_permission_granted(PERMISSION_READ_PHONE_STATE)
Очистка кэша приложения¶
Этот интерфейс используется для очистки кэша приложения; обычно это не влияет на работу приложения.
app.clear_cache()
Очистка данных приложения¶
Этот интерфейс используется для очистки данных приложения. Обратите внимание, что эта операция удалит все данные приложения, включая учётные записи и другую информацию.
app.reset()
Получение точки входа Activity¶
С помощью этого интерфейса можно узнать стартовую Activity приложения (Activity точки входа).
app.query_launch_activity()
>>> app.query_launch_activity()
{'action': 'android.intent.action.MAIN', 'component': 'com.android.settings/com.android.settings.Settings', 'categories': ['android.intent.category.LAUNCHER']}
Включение приложения¶
Этот интерфейс используется для включения отключённого приложения. После включения вы снова сможете нормально им пользоваться.
app.enable()
Отключение приложения¶
Этот интерфейс используется для отключения приложения. Отключённое приложение не будет отображаться в списке приложений и не может использоваться, пока не будет включено снова. Эта функция позволяет временно или полностью заморозить приложение: оно не может запускаться самостоятельно. Если на устройстве установлено много приложений, вы можете отключить временно ненужные, чтобы снизить потребление системных ресурсов.
app.disable()