アプリケーション操作

この章では、アプリケーションのインストール、起動、終了、権限の付与と取り消し、アプリの無効化と有効化、そして任意のアクティビティ(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

次のようにしてインストールします。通常、メインのパッケージ、言語パック、ネイティブライブラリパックをインストールする必要があります。

>>> 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 モードでは常に TrueFalse に設定しても無効)
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

名前でアプリを取得

アプリの一般名を使用してアプリインスタンスを取得します(パッケージ ID が不明な場合)。

app = d.get_application_by_name("微信")

パッケージ名でアプリを取得

アプリのパッケージ ID を使用してアプリインスタンスを取得します。

app = d.application("com.tencent.mm")

フォアグラウンドアプリの取得

現在フォアグラウンドで実行中のアプリインスタンスを取得します。

app = d.current_application()

マルチユーザーアプリの取得

マルチユーザー(デュアルアプリ)のインスタンスを取得します(通常、マルチユーザーアプリはユーザー ID で区別され、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()

アクティビティの起動

システムレベルのアクティビティをリプレイし、任意のアプリの任意のアクティビティを呼び出すことができます。以下のパラメータが使用可能です。なお、追加データ(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)

get_last_activities インターフェースの戻り値を例にとると、以下のコードで最後のシステムアクティビティを直接リプレイできます。

activity = d.get_last_activities(count=5)[-1]
d.start_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)

grant メソッドの mode パラメータは 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()

起動アクティビティの取得

このインターフェースで、アプリの起動アクティビティ(エントリアクティビティ)を照会できます。

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