転職したらスマレジだった件

スマレジのエンジニアやまてのテックブログです。マジレス大歓迎です。

Flutter|UDID を端末から取得する手順

スマレジのテックファーム(SES 部門)で Web 系エンジニアとして働いている やまて(@r_yamate) と申します。

2023 年 4 月からは、スマレジの関連アプリの開発業務を担当しています。触ったことのなかった Flutter での開発を担当することになり、日々奮闘中です。めっちゃ楽しい。

はじめに

今回は、 Flutter を用いて AndroidiOS の端末から UDID(Unique Device Identifier)を取得する手順についてまとめます。

なお、一般的にはネイティブアプリ開発で UDID を利用するケースは、あまりないのではないかとは思いますが、使用を検討する機会があり、調べたことをまとめておきたい目的で記事にしています。

目次

環境

私が試したFlutterの環境は以下のとおりです。

UDID とは

まずは UDID とは、どのようなものかについて確認します。

UDID(Unique Device Identifier)の定義

UDID とは、端末ごとに異なる一意の識別子です。製造元や OS によって生成され、ソフトウェアやサービスが端末を識別する際に用いられます。

flutter_udid パッケージ

flutter_udid パッケージを使用して、 UDID を取得し表示します。

pub.dev

UDID の使用の注意

UDID は、プライバシーの観点などから使用には注意が必要です。ユーザー追跡やデバイス特定、広告のパーソナライゼーションなどには、他の一意の識別子を使用するのがベストプラクティスとされます。

そのため、一般的には UDID を利用する場面は、あまりないのではないかとは思います。

参考: 一意の識別子に関するベスト プラクティス

developer.android.com

なお、一般的なデバイス情報の取得に使われる device_info_plus パッケージでは、 UDID の取得はサポートされていません。

pub.dev

step1. 依存パッケージの追加

pubspec.yaml への依存パッケージ追加

プロジェクトの pubspec.yaml ファイルに以下のようにflutter_udidを追加します。

dependencies:
  flutter_udid: ^2.0.1

その後、ターミナルで flutter pub get コマンドを実行して依存パッケージを取得します。

step2. コードの実装

パッケージのインポート

まず、 flutter_udid パッケージをインポートします。

import 'package:flutter_udid/flutter_udid.dart';

UDID 取得の関数を作成

次に、以下のような UDID 取得の関数を作成します。

/// ログインする券売機端末のUDIDを取得する。
///
/// 非同期で実行され、UDIDとして文字列を返す。
Future<String> getUdid() async {
  final udid = await FlutterUdid.udid;
  return udid;
}

実行結果

例えば Android で正常に実行すると、以下のような UDID 文字列が取得できます。

'8af8770a27cfd182'

パッケージのコードリーディング

FlutterUdid.udid がどのように動作しているか、パッケージのコードを読んで確認します。

Dart とネイティブコード(AndroidiOS)の間でデータを送受信するための MethodChannel が作成されているため、Dart と Kotlin (Android 側)のデータの送受信のコードを見てみます。

Dart のコード

確認:lib/flutter_udid.dart

import 'dart:async';
import 'package:crypto/crypto.dart';
import 'package:flutter/services.dart';
import 'dart:convert';

/// API to retrieve a unique device ID of the device the Flutter
/// app is currently running on.
class FlutterUdid {
  static const MethodChannel _channel = const MethodChannel('flutter_udid');

  /// Returns the UDID in the platform-specific format.
  /// iOS: 7946DA4E-8429-423C-B405-B3FC77914E3E,
  /// Android: 8af8770a27cfd182
  static Future<String> get udid async {
    final String udid = await _channel.invokeMethod('getUDID');
    return udid;
  }

  /// Returns the UDID in a consistent format for all platforms.
  /// Example: 984725b6c4f55963cc52fca0f943f9a8060b1c71900d542c79669b6dc718a64b
  static Future<String> get consistentUdid async {
    final String udid = await _channel.invokeMethod('getUDID');
    var bytes = utf8.encode(udid);
    var digest = sha256.convert(bytes);
    return digest.toString();
  }
}
  • FlutterUdid クラス: UDID を取得するためのメインクラス。
  • _channel: MethodChannel を作成している。 Dart とネイティブコード(AndroidiOS)の間でデータを送受信するためのチャンネル。
  • udid メソッド: getUDIDという名前のメソッドをネイティブ側に呼び出して、 UDID 取得。
  • consistentUdid メソッド: getUDIDで取得した UDID を SHA-256 でハッシュ化。

参考:MethodChannel の参考記事

ryamate.hatenablog.com

Kotlin のコード

ネイティブコードは Android 用の Kotlin のコードを確認してみます。

確認:android/src/main/kotlin/de/gigadroid/flutter_udid/FlutterUdidPlugin.kt

package de.gigadroid.flutter_udid

import androidx.annotation.NonNull
import android.annotation.SuppressLint
import android.provider.Settings
import android.content.Context

import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.PluginRegistry.Registrar
import io.flutter.plugin.common.PluginRegistry
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.BinaryMessenger

class FlutterUdidPlugin() : MethodCallHandler, FlutterPlugin {

  private lateinit var channel : MethodChannel
  private var applicationContext: Context? = null

  companion object {
    @JvmStatic
    fun registerWith(registrar: Registrar): Unit {
      val instance = FlutterUdidPlugin()
      instance.onAttachedToEngine(registrar.context(), registrar.messenger())
    }
  }

  override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
    onAttachedToEngine(flutterPluginBinding.getApplicationContext(), flutterPluginBinding.getBinaryMessenger());
  }

  private fun onAttachedToEngine(applicationContext : Context, messenger: BinaryMessenger) {
    this.applicationContext = applicationContext;
    channel = MethodChannel(messenger, "flutter_udid")
    channel.setMethodCallHandler(this)
  }

  override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    if(call.method == "getUDID"){
      val udid = getUDID()
      if(udid == null || udid == ""){
        result.error("UNAVAILABLE", "UDID not available.", null)
      }else{
        result.success(udid)
      }
    }else{
      result.notImplemented()
    }
  }

  override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
    applicationContext = null;
    channel.setMethodCallHandler(null)
  }

  private fun getUDID() : String{
    return Settings.Secure.getString(applicationContext?.contentResolver, Settings.Secure.ANDROID_ID)
  }
}
  • FlutterUdidPlugin クラス: Flutter プラグインとしてのメインクラス。
  • registerWith メソッド: 旧式の Flutter プラグイン API に対応するためのメソッド。
  • onAttachedToEngine: Flutter エンジンにアタッチされたときに呼ばれるメソッド。ここでMethodChannel を設定している。
  • onMethodCall: Dart 側から呼ばれるメソッドをハンドリングする。 getUDID メソッドが呼ばれた場合、getUDID()を呼び出して結果を返す。
  • getUDIDメソッド: 実際に AndroidSettings.Secure.ANDROID_IDを使って UDID 取得。

おわりに

今回は、 UDID を Android などの端末から取得する手順についてまとめました。まとめてはみたものの、 UDID を利用するケースはあまりないのではないかとは思います。

ありがとうございました。



ちょっとずつ開けてる。ポケモン初代を小学生の頃にやり込んだ人間としては 151 が最強。