應用操作

本章您可以了解如何安裝、啟動和關閉應用、授予或撤銷應用的權限、禁用或啟用應用,以及重放任意活動(scheme、Activity)(含未導出的活動)等。

安裝應用

您需要準備好安裝的 apk,支持安卓單個 APK 以及 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

並通過如下方式安裝,通常您需要安裝主包、語言包、以及 native lib 包。

>>> 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

會話參數

使用特定建立會話參數來實現 APK 的覆蓋安裝、降級安裝、自動授權、安裝到特定使用者等。

參數名類型預設值描述
userint0安裝到指定使用者 ID
packagestrNone待安裝的包名,可不填
request_downgradeboolFalse是否允許降級安裝
grant_runtime_permissionsboolFalse是否授予執行階段權限
installer_package_namestrNone自訂安裝器名稱(如 com.android.vending
dont_kill_appboolFalse安裝時不關閉應用
replace_existingboolTrue覆蓋已安裝應用(在 root 模式下始終為 True,即使設定為 False
allow_testboolFalse是否允許安裝測試應用

您可以像下面這樣,在建立會話時使用如上參數。

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。

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。它的可用參數如下,您需要注意,附加資料(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 = d.get_last_activities(count=5)[-1]
d.start_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,那麼它可能對您有用。當然,您的設備或者 APP 需要是可除錯的,它與上面的呼叫區別僅僅是多了一個 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',...]

授予應用權限

此介面用於授予應用對應的系統權限,您應在 APP 未啟動時使用此介面進行權限設定,在 APP 執行請求權限時使用並不會產生自動授予權限的效果。

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)

其中 grant 方法的 mode 參數,還支持 GrantType.GRANT_DENY,意思為明確拒絕該權限。以及 GrantType.GRANT_IGNOREGRANT_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(入口活動)。

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()