Open6

macOSでアクセシビリティアクセス権限を利用するアプリケーション開発の知見

ピン留めされたアイテム
usagimaruusagimaru

概要

“システム設定 > プライバシーとセキュリティ > アクセシビリティ” で「コンピュータの制御を許可」が必要なアプリケーションについて。

アクセシビリティアプリケーションにMacへのアクセスを許可する

以後、この“コンピュータの制御”のことを「アクセシビリティアクセス」と呼ぶ。

ユーザによるアクセシビリティアクセスの許可


アクセシビリティアクセスのシステムアラート

アクセシビリティアクセスを必要とするアプリケーションでは、イメージのようなシステムアラートを提示してユーザに許可を求めなければならない。アクセシビリティアクセスのシステムアラートは関係するAPIを使って呼び出すことができる。

アクセシビリティアクセスの許可は必ずユーザが直接行う必要があり、例えばプログラムからアクセシビリティアクセスを自動承認させるようなことはできない。

アクセシビリティアクセスの権限が必要になるケース

マウスカーソルの座標やクリック、キーストロークを常時監視したいケースがもっとも多いのでは。

マウスイベントやキーイベントを監視するには、NSEventのイベント監視メソッドaddGlobalMonitorForEvents(matching:handler:)を使って実現できる。ただし、このメソッドは実行するアプリケーションに対してアクセシビリティアクセスの権限がないと機能しない。常駐系アプリケーションの場合、アプリケーションがバックグラウンドにいる場合にもメッセージが常時飛んでくるようにすることが不可欠であるため、このメソッドとアクセシビリティアクセスの権限が機能実現の前提になる。

ちなみにアプリケーションがフォアグラウンドにいる場合は、類似メソッドのaddLocalMonitorForEvents(matching:handler:) の方を使う。こちらはアクセシビリティアクセスの権限なしでも利用できる。バックグラウンドを考慮しないのであればこちらだけでもイベント監視を行える。

これらのメソッドはマウスやキーボードのイベントを常時監視できるので、キーロガーなどの悪意あるソフトウェアを作るための技術にも応用できてしまう。プライバシー保護・セキュリティの観点から、特にバックグラウンドでの利用は不可視にせず、アクセシビリティアクセスという形でユーザによる直接の承認が必要となっているのだと考えられる。

usagimaruusagimaru

Accessibility API

基本的には2つのメソッド(関数?)を使う。

AXIsProcessTrusted()
AXIsProcessTrustedWithOptions()

AXIsProcessTrusted()は引数を持たず、appが「アクセシビリティアクセスの権限を有するか」についてBoolを返す。要するにシステム設定の例の一覧で有効扱い(スイッチがオン)になっているかどうかを判定する。

AXIsProcessTrustedWithOptions()はCFDictionary形式でオプション引数をとり、trueを渡すと「アクセシビリティアクセス」のシステムアラートを表示する。falseを渡すとアラートを表示せず、AXIsProcessTrusted()と同じ効果になる。辞書形式のoptionsとあるが実質このオプションしか使わない。


アクセシビリティアクセスのシステムアラート

let showsSystemAlert = true
let options = [kAXTrustedCheckOptionPrompt.takeRetainedValue(): showsSystemAlert] as CFDictionary
let isTrusted = AXIsProcessTrustedWithOptions(options)

利用する場合のimportするフレームワークはCocoaで良い。
古い情報ではApplication Servicesフレームワークのヘッダーを読み込むとあるが、macOS 13.0環境では不要。

import Cocoa
usagimaruusagimaru

アクセシビリティアクセスのデバッグを便利にする

tccutilコマンド

アクセシビリティアクセスの権限をリセットして、再び例のシステムアラートを表示させたい場合、システム設定のアクセシビリティアクセスの一覧に登録されているappを消すなどする操作が必要になる。手作業だと効率が悪いため、シェルスクリプトやコマンドラインなどを利用して自動化する方法がある。


このアクセシビリティアクセスの一覧から当該appを自動削除して、再びシステムアラートを表示させたい。

tccutilコマンドをTerminalで実行すると、指定したアプリケーションのアクセシビリティアクセスをリセットできる。アプリケーション指定にはBundle Identifierを使う。

BUNDLE_IDENTIFIERのアクセシビリティアクセスをリセット
tccutil reset Accessibility BUNDLE_IDENTIFIER
すべてのアクセシビリティアクセスをリセット
tccutil reset Accessibility
BUNDLE_IDENTIFIERの全プライバシー設定をリセット
tccutil reset All BUNDLE_IDENTIFIER

Xcodeビルド時にリセットをかける

XcodeでTargetを選択し、Build Phasesの末端にRun Scriptを追加、以下のスクリプトをそのまま貼り付けておけば、Xcodeビルド時にリセットを自動実行できる。すなわち、開発アプリケーションがXcode経由で起動するたびにアクセシビリティアクセスの設定がリセットされた状態になる。

Run Script
tccutil reset Accessibility $PRODUCT_BUNDLE_IDENTIFIER

システム設定の“アクセシビリティアクセス”を直接開けるようにする

この方法の応用で、URLを使って設定に直接ジャンプできる体制を作っておけばテストが非常に捗る。Shortcuts.appでシェルスクリプトをメニューバーから実行できるようにしておけば2クリック[1]でジャンプできる。

アクセシビリティアクセスに直接ジャンプするシェルスクリプト
open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"


スクリプトをショートカット化した例

TCC.db

アクセシビリティアクセスは以下のパスにあるTCC[2]ファイルに収録されている。直接操作することはないだろうが参考までに。

~/Library/Application Support/com.apple.TCC/TCC.db
/Library/Application Support/com.apple.TCC/TCC.db

このファイルはデータベースファイルなので、sqlite3で覗くことができる[3]。直接のアクセスはパーミッションエラーになるため、TCC.dbファイルをデスクトップ等にコピーしてから操作する。

sqlite3  /path/to/TCC.db 'SELECT * from access' | grep kTCCServiceSystemPolicyAllFiles

参考資料

Accessibility Permissions for Development in macOS and XCode
How-to: Reset Privacy Permissions in macOS
[Mac Admin]TCC.dbを操作する【その5】TCC.dbの中身をみてみましょう
In Catalina running bash script via an AppleScript, results are not correct
セキュリティとプライバシーの設定値をリセットする(macOS 10.15 Catalina)

脚注
  1. ショートカットメニューを展開するクリックと、メニューからショートカットを選んで実行するクリックの2回。 ↩︎

  2. TCC = Transparency Consent and Control. ↩︎

  3. サービス名を探る目的ならば、stringsコマンドで文字列をダンプする方法もあるらしい。 ↩︎

usagimaruusagimaru

AXIsProcessTrusted()がどうやってもtrueにならず、常にfalseを返す

システム設定のアクセシビリティアクセスの一覧に該当appが登録され、有効になっているにもかかわらず、AXIsProcessTrusted()が常にfalseを返すことがある。なぜそうなるのかはわからないが、割と頻繁に起きる上にどうやったら解決するのかも今ひとつわからないし、しれっと直ったりもするので、とりあえず試した方法や調べて出てきた資料を記しておく。


アクセシビリティアクセスの一覧

アクセシビリティアクセスの一覧から当該appを一旦削除する

GUIに削除ボタンが用意されているので、まずは登録自体を消しておく。

tccutilでリセットをかける

デバッグを便利にするのシェルスクリプトを使って、登録自体を一旦削除する。GUIからではなくコマンドラインから行う。

DrivedData(Xcodeのキャッシュ)を破棄する

古典的な方法である。

swift - How to prompt for accessibility features in a macOS app (from the AppDelegate)?

Xcodeとプロジェクトを一旦閉じる

Xcodeを再起動することで解決することもある。プロジェクトウインドウが復元されたりするとうまくいかないかもしれないので、プロジェクトウインドウも一旦閉じる。

DrivedDataの削除と合わせてみても良い。

Macを再起動

他の方法を試してどうにもならん場合、それらをもう一度試してからMac再起動が効くかも。

AXIsProcessTrustedWithOptions doesn't return true even when the app is ticked in accessibility

不確かな情報:アプリケーション実行中にシステム設定からアクセシビリティアクセスを操作するとおかしくなる

アプリケーション実行中にシステム設定からアクセシビリティアクセスを操作するとおかしくなることがあるらしい。関連してSandbox環境の話や署名の話もあるが原因は不確かである。

システム設定からアクセシビリティアクセスを変更した場合は、あとでtccutilでリセットをかけると良いのかもしれない。

Call to AXIsProcessTrustedWithOptions does not work after change setting manually

不確かな情報:アプリケーションの署名方法を変える

Developer IDで署名すると解決するなどの情報あり。本当か?

Call to AXIsProcessTrustedWithOptions does not work after change setting manually

不確かな情報:開発中にはXcodeにもアクセシビリティアクセスの許可を

しかし手元の環境(macOS 13.0 Ventura)では効果がなく、結局Xcodeへの許可がなくてもtrueが返ってきた。

objective c - Relaunch OS X app to execute CGEventTapCreate() when AXIsProcessTrusted() is true

When I run the app in Xcode and add trust Xcode in Privacy_Accessibility it all work fine.

【Cocoa】Macプログラマのスレ【Objective-C】 | 781 仕様書無しさん 2015/11/14(土) 21:42:55.64

XcodeでRun(Debugger)する場合には、許可を与えるのはXcodeにね
成果物を直接起動する場合には成果物に許可を

usagimaruusagimaru

システム設定の“アクセシビリティアクセス”のペインを直接開く

わざわざ奥深いアクセシビリティアクセスの設定まで行くのは少々面倒である。デベロッパーがデバッグのために操作を繰り返すことも当然ながら、ユーザにとっても不便に感じる場面はあるのではと考えられる[1]

システム設定(旧:システム環境設定)の各ペインには固有のURLが割り振られている。ドキュメントには載っていないみたいだが、有志が解析して見つけたURLが明らかになっている。これを使えばアクセシビリティアクセスまで直接ジャンプする機能を実装できる。

アクセシビリティアクセスのURL
x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility

openコマンドでアクセシビリティアクセスにジャンプできる。

アクセシビリティアクセスに直接ジャンプするシェルスクリプト
open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"

SwiftコードではNSWorkspaceのopen(_:)を使えば良い。

システム設定の各ペインのURL一覧

有志が解析して見つけた、システム設定の各ペインのURLがまとまった資料がある。

macOS 10.15 System Preference Panes
Ventura System Settings
Create System Preferences URL to Privacy Files and Folders in 10.15 Catalina

脚注
  1. システムアラート経由で直接ジャンプする手段もあるが、システムアラートが存在しない場面でも設定に飛ぶ手段を提供したい場合は、この方法が役立つ。 ↩︎

usagimaruusagimaru

AXIsProcessTrustedWithOptions()でシステムアラートが表示されない

手元で確認した限りでは、Sandboxが有効だとシステムアラートが表示されない模様。Sandboxを無効化すると表示されるようになる。ただし、Sandboxを無効化してしまうとMac App Storeで配布できなくなる副作用がある。

Sandbox有効の問題は「アクセシビリティアクセスのシステムアラートが表示されない」というだけで、ユーザが手動でアクセシビリティアクセスに当該appを登録してくれれば、フラグ自体はtrueが返ってくる。

…ということは、システムアラートに頼るのではなく、独自設計のアラートダイアログでアクセシビリティアクセスの設定にジャンプできるようにすれば、Sandbox環境でも基本的には同じ機能とユーザ体験を実現できるのではないか[1]

関連資料

Call to AXIsProcessTrustedWithOptions does not work after change setting manually
objective c - Global events, the Mac App Store, and the sandbox
スクリーンキャスト用のキーストローク表示アプリ | tech - 氾濫原

脚注
  1. 今のところ試してはいない。 ↩︎