開発note@パッション

開発ノート:NFC-③DriverKit 実装 後編【動作確認編】

開発note@パッション

今回は、前・中編で実装したアプリプログラムとドライバプログラムの動作確認について解説していきます。

・NFCを活用した新しいサービスや製品を企画・開発したい方
・スマートフォンやICカードとの連携機能を実装したいエンジニア
・IoT機器や非接触決済の技術に関心のある開発者

▼関連記事はこちら▼
・開発note#1 NFC-① イントロダクション
・開発note#2 NFC-② 機材・開発環境準備
・開発note#3 NFC-③ DriverKit 実装 前編【アプリプログラム実装編】
・開発note#4 NFC-③ DriverKit 実装 中編【ドライバプログラム実装編】

 

ビルド・動作確認 【 アプリプログラム 】

ドライバのインストールは、システムの /Applications ディレクトリにアプリ⾃体がインストールされている必要が ありますが、デバッグ実⾏では /Applications ディレクトリにインストールされません。
開発者モードではアプリの配置場所を確認しなくなるため、 systemextensionsctl コマンドを使⽤して開発者モードを有効にします。

ターミナルを開き、以下のコマンドを実⾏します。


% systemextensionsctl developer on

機能拡張ブロックのダイアログが表示されるため、ログインユーザーのパスワードを入力して OK ボタンをクリックします。

開発者モードを有効にしたら、プロジェクト設定でアプリとドライバの署名 (Signing & Capabilities – Signing) を適切に設定してビルドします。
ビルドが正常終了したら実行してみましょう。

実行すると、ドライバインストールボタンのみのダイアログが表示されます。
このボタンをクリックするとドライバインストールが開始されるのですが、機能拡張ブロックのダイアログが表示される場合があります。

このダイアログが表示された場合、 システム設定 – プライバシーとセキュリティ – セキュリティ から、システムソフトウェアの読み込みを許可する必要があります。

これでドライバがインストールされました。確認していきましょう。

システムにインストールされているシステム拡張機能の確認は systemextensionsctl コマンドを使用します。
ターミナルを開き、以下のコマンドを実行します。


% systemextensionsctl list

コマンド実行結果より、今回作成したドライバ jp.passion-s.NFCDriverTestApp.NFCDriver が利用可能となっていることがわかります。


% ps auxh | grep <ドライバの Bundle Identifier>
 

以上でドライバのインストールされ、動作していることが確認できました。

 

ビルド・動作確認 【 ドライバプログラム 】

ドライバプログラムの動作確認をしてみましょう。
なお、デバイスが Mac に接続されている場合、アプリを実行する前にデバイスを抜いておきます。
続いてドライバの実行確認のための準備をします。

  1. デバイスを Mac に接続します
  2. 開発note@パッション

  3. 左部メニューで自身のデバイスが選択します
  4. 右上の検索バーに <ドライバの Bundle Identifier>.dext(今回の場合は jp.passion-s.NFCDriverTestApp.NFCDriver.dext)を入力します
  5. 開始ボタンをクリックします

準備ができたら実際にデバイスを接続してみましょう。

デバイスを接続してしばらくすると、ドライバの init メソッド、 Start メソッドに実装している os_log のログが出力されます。
また、デバイスを抜くと、ドライバの Stop メソッド、 free メソッドに実装している os_log のログが出力されます。

もしデバイスを接続してもドライバが起動しない場合、 AMFI (Apple Mobile File Integrity) によってブロックされてしまっている可能性があります。
その場合、 開発目的で 一時的に AMFI を無効化することで、ドライバを起動するようにできます。

【免責・注意】
ここでご紹介する AMFI 無効化は Apple の正式な手順ではありません。
実施は自己責任でお願いいたします。

また、前回ご紹介した SIP 同様、 AMFI が無効のままだと、コンピュータが悪意のあるコードに対して脆弱な状態となります。
このため、 SIP 無効化同様、 AMFI も必要なタスクを実行する時だけ無効化し、できるだけ早く再度有効化してください。

AMFI を無効化するには、ターミナルを開いて以下のコマンドを実行した後、マシンを再起動します。


% sudo nvram boot-args="amfi_get_out_of_my_way=0x1"

動作確認後、 AMFI を有効化するには、ターミナルを開いて以下のコマンドを実行した後、マシンを再起動します。


% sudo nvram -r boot-args

 

Appendix: ドライバインストール状態の確認

現在は常にインストールボタンが表示されますが、実際のユースケースを考慮すると、ドライバがインストールされていない場合のみインストールボタンが表示された方が良いかと思います。
そこで、アプリ起動時にドライバのインストール状態を確認し、未インストールの場合のみボタンを表示するようにしたいと思います。
なお、ここで紹介する方法は macOS 12 Monterey 以降 で利用可能です。

ViewModel クラスの修正(抜粋)


- class ContentViewModel: NSObject {
+ class ContentViewModel: NSObject, ObservableObject {
    
      // MARK: - Properties

      /// ドライバの Bundle Identifier
      private let dextIdentifier: String = "jp.passion-s.NFCDriverTestApp.NFCDriver"
    
+     /// ドライバアクティベート状態
+     @Published private var _isActivated: Bool = false
+ 
+     /// ドライバがインストールされているか
+     public var isInstalled: Bool {
+         return _isActivated
+     }
+ 
+     // MARK: - Methods
+ 
+     /// 初期化
+     override init() {
+         super.init()
+ 
+         if #available(macOS 12, *) {
+             let request = OSSystemExtensionRequest.propertiesRequest(forExtensionWithIdentifier: dextIdentifier,
+                                                                      queue: .global(qos: .userInteractive))
+             request.delegate = self
+             OSSystemExtensionManager.shared.submitRequest(request)
+         }
+     }
+ 
      // MARK: -
  }
  
  extension ContentViewModel: OSSystemExtensionRequestDelegate {
  
+     /// システム拡張プロパティ要求に対する結果のデリゲート処理
+     @available(macOS 12, *)
+     func request(_ request: OSSystemExtensionRequest, foundProperties properties: [OSSystemExtensionProperties]) {
+         os_log("OSSystemExtensionRequest foundProperties")
+ 
+         let sortedProperties = properties.sorted(by: { $0.bundleVersion > $1.bundleVersion })
+         for dext in sortedProperties where dext.isEnabled {
+             _isActivated = true
+             break
+         }
+     }

  }

View クラスの修正(抜粋)


  struct ContentView: View {
-     var viewModel: ContentViewModel = .init()
+     @ObservedObject var viewModel: ContentViewModel = .init()

      var body: some View {
          VStack {
              Text("NFC Driver テストアプリ")
                  .font(.title)
              Divider()
              HStack {
+                 if viewModel.isInstalled {
+                     Text("ドライバはすでにインストール済です")
+                 }
+                 else {
                      Button(
                          action: {
                              self.viewModel.installDriver()
                          }, label: {
                              Text("インストール")
                          }
                      )
+                 }
              }
          }
          .padding()
      }
  }

アクティベーション時同様、 OSSystemExtensionRequest クラスの propertiesRequest メソッドでプロパティ要求を作成して OSSystemExtensionManager に送信します。
すると、デリゲートメソッド request:foundProperties: に、指定された Bundle Identifier のプロパティが通知されます。

今回はシンプルに、該当ドライバがインストールされていればインストール不要として、インストールボタンを表示せずラベルを表示するように実装しました。

 

おわりに

ここまででベースとなる DriverKit 開発が完了となります。
今回用意したプログラムをベースに、目的や用途に合わせて実装していくことで、 Mac 向けドライバを作成することができます。

次回は FT232H との通信について実装していきます。

 


 

TOP