##変更点
・Sesameの操作関係をクラス化
インスタンスで2台操作できそうに(未実験
10秒or1分で接続が切れることを利用して、それっぽく例外処理
・スマホとペアリングができていない場合は、createBondでペアリング
・マニフェストでActivityに対して”透明”テーマを適応
・Notificationでヘッドアップ通知で動作状況を把握
・BluetoothAdapterの初期化をdevelopersサイトのKotllin記載に修正
###manufactreDataMacDataStringの見つけ方
このプログラムではPrimarySesameMacDataの値について
BLEScannerのアドバタイズパケットの内容が見れる:RAWの部分の赤線部分。この資料の場合、[00-00-C1-02-02-02-02-02-02]になる。
##成果物
MainActivity.kt
package jp.sakujira.opensesame
//App
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
//NFC
import android.nfc.NfcAdapter
//Bluetooth
import android.content.Context
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
//Notification
import android.os.Build
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
//SetUp-Config
//NFCCardData
val CardID : String = -鍵にしたいCardID-
//Sesame UserData
val UserID : String = -Sesameのメールアドレス-
val Password : String = -公式APPから抜き出したパスワード-
//Primary Sesame
val PrimarySesameAddress : String = -SesameのBLEアドレスEx:FF:00:FF:00:FF:00-
val PrimarySesameMacData : String = -Sesameのアドバタイズパケットの該当部分:0000C1020202020202-
val PrimarySesameLockMinAngle : Int = 10
val PrimarySesameLockMaxAngle : Int = 270
//Secondary Sesame
val SecondarySesameAddress : String = ""
val SecondarySesameMacData : String = ""
val SecondarySesameLockMinAngle : Int = 10
val SecondarySesameLockMaxAngle : Int = 270
class MainActivity : AppCompatActivity() {
//SesameDevice
private lateinit var PrimarySesame : SesameDevice
private lateinit var SecondarySesame : SesameDevice
private val HasSecondary : Boolean = (SecondarySesameAddress.length > 0)
//BLE用のアダプター作成
private val BluetoothAdapter: BluetoothAdapter by lazy(LazyThreadSafetyMode.NONE) {
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothManager.adapter
}
//NFC-Certification
private var isNFCLoad : Boolean = false
@ExperimentalUnsignedTypes
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//SesameDeviceの初期化
PrimarySesame = SesameDevice(BluetoothAdapter.getRemoteDevice(PrimarySesameAddress), PrimarySesameMacData, PrimarySesameLockMinAngle, PrimarySesameLockMaxAngle, this)
if(HasSecondary){
SecondarySesame = SesameDevice(BluetoothAdapter.getRemoteDevice(SecondarySesameAddress), SecondarySesameMacData, SecondarySesameLockMinAngle, SecondarySesameLockMaxAngle,this)
}
//このアプリを開く[起因]はNFC情報を読み取り
//A1.[起因]は、AndroidManifest.xmlに規定した<intent-filter>に起因する
if(!isNFCLoad && NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
//カードのID情報を取得
var tagId: String = ""
for (byte in intent.getByteArrayExtra(NfcAdapter.EXTRA_ID)) {
tagId += String.format("%02X", byte) + ":"
}
//A2.読み込んだカードIDが一致すれば、BLE操作を開始
println("Btest:" + tagId)
if (tagId == CardID + ":") {
Notificate("ちょっとまってね!")
isNFCLoad = true
PrimarySesame.ConnectGatt()
if (HasSecondary) {
SecondarySesame.ConnectGatt()
}
}
}
finishAndRemoveTask()
}
//Sesameに対して操作を開始
@ExperimentalUnsignedTypes
fun StartToggle(){
if(isNFCLoad){
isNFCLoad = false
PrimarySesame.PrimaryStart()
}
}
//SesameDeviceからの結果を受信
@ExperimentalUnsignedTypes
fun SetCompleteState(mCommandState : Int, mLockState : Int){
var _LockState = mLockState
if(HasSecondary && mCommandState == 4){
SecondarySesame.SecondayStart(mLockState)
_LockState = 0
}
when(_LockState){
1->Notificate("いってらっしゃい!")
2->Notificate("おかえりなさい!")
}
}
//通知処理
fun Notificate(mContent:String){
// Channelの取得と生成
val channelId = "KeyToggle"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//Android8 Oreo以上の場合
//通知チャンネルを作り
val channel = NotificationChannel(channelId, "Beacon", NotificationManager.IMPORTANCE_HIGH).apply {
this.description = "Sesame施錠・解錠通知"
this.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
this.enableVibration(true)
}
// システムにチャンネルを登録する
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(this,channelId).apply {
setContentTitle("Sesame施錠・解錠通知")
setContentText(mContent)
priority = NotificationCompat.PRIORITY_DEFAULT
}
with(NotificationManagerCompat.from(this)){
notify(1, notification.build())
}
}
}
SesameDevice.kt
package jp.sakujira.opensesame
import android.bluetooth.*
import java.security.MessageDigest
import java.util.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class SesameDevice(val mDevice : BluetoothDevice, val mMadData :String, val mMinLockAngle : Int, val mMaxLockAngle : Int, val SesameService: MainActivity){
//Gattへの操作
private var mGatt: BluetoothGatt? = null
//SesameがもつBLEのサービス検索詞
private val ServiceOperationUuid : UUID = UUID.fromString("00001523-1212-efde-1523-785feabcd123")
private val CharacteristicCommandUuid : UUID = UUID.fromString("00001524-1212-efde-1523-785feabcd123")
private val CharacteristicStatusUuid : UUID = UUID.fromString("00001526-1212-efde-1523-785feabcd123")
private val CharacteristicAngleStatusUuid : UUID = UUID.fromString("00001525-1212-efde-1523-785feabcd123")
//サービス検索結果:各サービスへの接続詞
var CharStatus: BluetoothGattCharacteristic? = null
var CharCmd:BluetoothGattCharacteristic? = null
var CharAngle:BluetoothGattCharacteristic? = null
//状態管理用の変数
var CommandState : Int = 0 //次のコマンドを何を投げるかを管理
var SesameState : Int = 0 //Sesame側のカウンターを管理
var LockState : Int = 0 //開けるか・閉めるか・状態を聞くかを管理
var ConnectState : Int = 0 //Gatt接続状況を管理
//送信データ管理
var mSendData: ArrayList<ByteArray> = ArrayList()
var mSendPointer : Int = 0
//Gattの接続を試みる
@ExperimentalUnsignedTypes
fun ConnectGatt(){
when(ConnectState){
0->{//未接続状態
ConnectState = 1//接続テスト中へ変更
mGatt = mDevice.connectGatt(SesameService, false, mGattcallback)
}
2->{//接続状態
ConnectedGatt()
}
}
}
//Gattの接続の成功
@ExperimentalUnsignedTypes
fun ConnectedGatt(){
SesameService.StartToggle()
}
//Gattの接続終了処理
fun DisConnectGatt(){
CharStatus = null
CharCmd = null
CharAngle = null
ConnectState = 0
SesameState = 0
LockState = 0
ConnectState = 0
}
//ここから処理順番管理
@ExperimentalUnsignedTypes
//For PrimaryDevice
fun PrimaryStart(){
StateCommand()
}
//For SecondayDevice
@ExperimentalUnsignedTypes
fun SecondayStart(mLockState : Int){
LockState = mLockState
ConnectState = 10
StateCommand()
}
//次処理実行
@ExperimentalUnsignedTypes
fun NextState(){
CommandState += 1
//変更点:別スレッドで次のコマンドを実行
GlobalScope.launch{
StateCommand()
}
}
//処理順番管理
@ExperimentalUnsignedTypes
fun StateCommand(){
println("Btest:Start-" + CommandState)
when (CommandState){
//For PrimaryDevice
0->GetSesameStatus() //B0.Sesameの状態取得:Sesameカウントを取得
1->SendStartData() //B1.LockState:0を送信しAngleを検知させる
2->GetSesameAngle() //B2.角度を取得し、施錠・解錠を取得
3->SendStartData() //B3.施錠・解錠コマンドを送信
4->ControlComplete() //B4.ループを終えてアプリを閉じる
//For SecondaryDevice
10->GetSesameStatus() //B10.Sesameの状態取得:Sesameカウントを取得
11->SendStartData() //B11.施錠・解錠コマンドを送信
12->ControlComplete() //B12.ループを終えてアプリを閉じる
}
println("Btest:End-" + CommandState)
}
//ここまで処理順番管理
//ここから通信開始関数
//Sesameからステータスを取得-開始
fun GetSesameStatus(){
println("Btest:GetStatus")
mGatt!!.readCharacteristic(CharStatus)
}
//Sesameに対してデータを送信-開始
@ExperimentalUnsignedTypes
fun SendStartData(){
println("Btest:SendData:")
//各パラメータから送信データを作成
val PayLoad = CreateSign(LockState,"", Password, mMadData, UserID, SesameState)
//データをmtu:20byteごとに分割
mSendData = SplitByteArray(PayLoad)
mSendPointer = 0
//送信データをセット
println("Btest:SendData:Pointer"+ mSendPointer)
println("Btest:SendData:" + ByteArrayToString(mSendData[mSendPointer]))
CharCmd!!.setValue(mSendData[mSendPointer])
mGatt!!.writeCharacteristic(CharCmd)
}
//Sesameから角度情報取得-開始
fun GetSesameAngle(){
println("Btest:GetRange")
mGatt!!.readCharacteristic(CharAngle)
}
//Sesameへの操作処理終了処理
@ExperimentalUnsignedTypes
fun ControlComplete(){
println("Btest:EndConnect!")
//BLEのペアリングを保存
if(mDevice.bondState == BluetoothDevice.BOND_NONE) {
println("Btest:BOND-NONE")
if(mDevice.createBond()){
println("Btest:BOND-Create")
}
}
//Serviceに状態を報告
SesameService.SetCompleteState(CommandState,LockState)
mGatt?.disconnect()
}
//ここまで通信開始関数
//ここから通信応答処理
@ExperimentalUnsignedTypes
private val mGattcallback: BluetoothGattCallback = object : BluetoothGattCallback() {
//B0.Sesameへの接続確認
override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
super.onConnectionStateChange(gatt, status, newState)
//B0.接続確立を確認して
if (newState == BluetoothProfile.STATE_CONNECTED) {
//B0.サービスの検索を開始
println("Btest:GattSa-Search")
gatt?.discoverServices()
}else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
DisConnectGatt()
}
}
//B0.サービスの検索完了:結果を分析
override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
super.onServicesDiscovered(gatt, status)
//B0.サービスの検索が成功を確認して
if(status == BluetoothGatt.GATT_SUCCESS) {
println("Btest:GattSa-OK!")
//B0.サービスの一覧表を取得
val GattSList: List<BluetoothGattService> = gatt?.services as List<BluetoothGattService>
//B0.サービスの一覧表を走査
for (GaService: BluetoothGattService in GattSList) {
println("Btest:>" + GaService.uuid.toString())
//B0.事前にserviceOperationUuidと一致したものがあったら、
if (GaService.uuid.equals(ServiceOperationUuid)) {
//B0.サービスが持っている機能・情報の一覧を取得
val GattCList: List<BluetoothGattCharacteristic> = GaService.characteristics
//B0.機能・情報の一覧の走査
for (GaCharacteristic: BluetoothGattCharacteristic in GattCList) {
println("Btest:>>" + GaCharacteristic.uuid.toString())
//B0.Sesameの状態情報取得の接続詞を取得
if (GaCharacteristic.uuid.equals(CharacteristicStatusUuid)){
CharStatus = GaCharacteristic
}
//B0.Sesameのコマンド情報取得の接続詞を取得
if (GaCharacteristic.uuid.equals(CharacteristicCommandUuid)){
CharCmd = GaCharacteristic
}
//B0.Sesameの角度情報取得の接続詞を取得
if (GaCharacteristic.uuid.equals(CharacteristicAngleStatusUuid)){
CharAngle = GaCharacteristic
}
}
}
}
//B0.走査した結果が全てあるかどうかをチェックし、次の状態に移行
if(!(CharStatus == null || CharCmd == null || CharAngle == null)){
ConnectState = 2
ConnectedGatt()
}
}
}
//接続詞を使っての読み込み依頼した結果を分析
override fun onCharacteristicRead(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int) {
super.onCharacteristicChanged(gatt, characteristic)
println("Btest:" + characteristic!!.uuid.toString())
when (characteristic.uuid) {
//B2.依頼内容が「AngleStatus」であれば
CharacteristicAngleStatusUuid -> {
val data: ByteArray = characteristic.value //データを取得
println("Btest:" + ByteArrayToString(data)) //データをFF:FF形式で表示
val angleRaw = ByteArrayToInt(data.slice(2..3).toByteArray()) //データを切り出して、Byte→Intへ
val angle = Math.floor(angleRaw * 360 / 1024.0) //角度を計算
//B2.LockStateは、操作コマンドと兼ねているため 1:解錠(操作:施錠) 2:施錠(操作:解錠)と逆になる
LockState = 1;
if (angle < mMinLockAngle || angle > mMaxLockAngle) {
LockState = 2;
}
println("Btest:Byte:" + ByteArrayToString(data) + ", Angle:" + angle + ", LockStatus:" + (LockState==2))
NextState()//次の状態に移行
}
//B0.依頼内容が「Status」であれば
CharacteristicStatusUuid -> {
val data: ByteArray = characteristic.value //データの取得
val Sn: Int = ByteArrayToInt(data.slice(6..9).toByteArray()) + 1 //Sesameカウンターを取得
val Err: Int = ByteArrayToInt(data.slice(14..14).toByteArray()) + 1 //エラーコードを取得
//エラーコードリスト
val errMsg = arrayOf(
"Timeout",
"Unsupported",
"Success",
"Operating",
"ErrorDeviceMac",
"ErrorUserId",
"ErrorNumber",
"ErrorSignature",
"ErrorLevel",
"ErrorPermission",
"ErrorLength",
"ErrorUnknownCmd",
"ErrorBusy",
"ErrorEncryption",
"ErrorFormat",
"ErrorBattery",
"ErrorNotSend"
)
println("Btest:Byte:" + ByteArrayToString(data) + ", Sn:" + Sn + ", Err:" + errMsg[Err])
SesameState = Sn //B0.Sesameカウンタを記録
NextState()//次の状態に移行
}
}
}
//B1.B3.送信データの受領確認後、次パケットを送信
override fun onCharacteristicWrite( gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int) {
super.onCharacteristicWrite(gatt, characteristic, status)
if(status == BluetoothGatt.GATT_SUCCESS){
//送信完了したので、ポインタを一つ進める
mSendPointer += 1
//全部送信し終えたら
if(mSendData.size <= mSendPointer){
println("Btest:SendData:Pointer-End")
//送信データを綺麗にしてから
mSendData = ArrayList()
mSendPointer = 0
//次の処理へ
NextState()
}else {
println("Btest:SendData:Pointer"+ mSendPointer)
println("Btest:SendData:" + ByteArrayToString(mSendData[mSendPointer]))
//ここも別スレッドで残りを送信!
GlobalScope.launch {
CharCmd?.setValue(mSendData[mSendPointer])
gatt?.writeCharacteristic(CharCmd)
}
}
}
}
}
//ここまで通信応答処理
//ここからデータ処理関数
//Sesameに対してデータを送信(BLEの20バイト制約のため、分割して送信)
@ExperimentalUnsignedTypes
fun SplitByteArray(pPayload : ByteArray): ArrayList<ByteArray>{
val Data : ArrayList<ByteArray> = ArrayList()
//送信は20バイトごとに送信
//分割する際には、[先頭:01][中間:02][最後:04]と付ける必要がある
//なので、一回の送信は19バイトごと
for(i in 0..pPayload.size step 19){
val wSz = Math.min(pPayload.size-i,19) //送るデータが最後かどうか?
var wCc : Int = 2 //初期値は[中間:02]とする
var wBuf : ByteArray = ByteArray(wSz+1) //送信用データ場所を作成
//先頭・最後を判定
if(wSz < 19){
wCc = 4
}else if(i == 0){
wCc = 1
}
//バイト列に分割詞をつける
wBuf[0] = wCc.toByte()
//送信データからバイト列を切り出し
wBuf = ByteArrayCopy(wBuf, 1, pPayload, i,wSz)
println("Btest:CutData:" + ByteArrayToString(wBuf))
Data.add(wBuf)
}
return Data
}
//認証用バイトデータを作成(普段Byteを使わないから、符号あり・なしに振り回された、、、)
fun CreateSign(pCode:Int, pPayload: String, pPassword : String, pMacData : String, pUserid : String, pNonce: Int) : ByteArray{
//バイト配列の場所を作成
var wBufnonPw : ByteArray = ByteArray(59 - 32 + pPayload.toByteArray().size)
//manufactreDataのデータをコピー
val ByteMacData : ByteArray = HexStringToByteArray(pMacData)
println("Btest:Mac:" + ByteArrayToString(ByteMacData.sliceArray(3..ByteMacData.size-1)))
wBufnonPw = ByteArrayCopy(wBufnonPw, 0, ByteMacData.sliceArray(3..ByteMacData.size-1),0,6)
//md5のデータをコピー
val md5 = MessageDigest.getInstance("MD5").digest(pUserid.toByteArray())
println("Btest:md5:" + ByteArrayToString(md5))
wBufnonPw = ByteArrayCopy(wBufnonPw,6,md5,0,16)
//Status(Nonce)をコピー
println("Btest:Nonce:" + ByteArrayToString(InttoByteArrayUnsign(pNonce)))
wBufnonPw = ByteArrayCopy(wBufnonPw,22,InttoByteArrayUnsign(pNonce),0,4)
//Codeをコピー
println("Btest:Code:" + ByteArrayToString(InttoByteArrayUnsign(pCode)))
wBufnonPw = ByteArrayCopy(wBufnonPw,26,InttoByteArrayUnsign(pCode),0,1)
//Payloadをコピー
wBufnonPw = ByteArrayCopy(wBufnonPw, 27, pPayload.toByteArray(),0, pPayload.toByteArray().size)
//パラメータの結果を確認
println("Btest:PrameterOK!:" + ByteArrayToString(wBufnonPw))
//「生成したパラメータ」を「パスワード」を使って暗号化
//「パスワード」を使用する暗号機を作成
val key = SecretKeySpec(HexStringToByteArray(pPassword), "HmacSHA256")
val mac = Mac.getInstance("HmacSHA256")
mac.init(key)
val wBufKey = mac.doFinal(wBufnonPw)
println("Btest:wBufkey:" + ByteArrayToString(wBufKey))
//全部を連結
var wBuf : ByteArray = ByteArray(pPayload.toByteArray().size + 59)
wBuf = ByteArrayCopy(wBuf,0, wBufKey,0, 32)
wBuf = ByteArrayCopy(wBuf,32, wBufnonPw,0, wBufnonPw.size)
println("Btest:ALL:" + ByteArrayToString(wBuf))
return wBuf
}
//Intを符号なしのバイト列に変換
fun InttoByteArrayUnsign(num : Int): ByteArray{
val wHexString : String = num.toString(16).padStart(12,'0')//文字埋めを12桁にしているのはByteArrayCopyで参照値外がないようにするため
val wResult = HexStringToByteArray(wHexString)
return wResult.reversedArray() //1101→03F3となるが、送信データ上ではF303と逆にする必要がある
}
//HEX文字列をバイト配列にキャスト
fun HexStringToByteArray(pHexString: String): ByteArray {
val wBArray = ByteArray(pHexString.length / 2)
for (index in 0 until wBArray.count()) {
val pointer = index * 2
wBArray[index] = pHexString.substring(pointer, pointer + 2).toInt(16).toByte()
}
return wBArray
}
//Byte配列を指定位置にコピー
fun ByteArrayCopy(pTarget: ByteArray, pPosition: Int, pCopy: ByteArray, pStart: Int, pLength : Int):ByteArray{
for(i in 0 until pLength){
pTarget[pPosition + i] = pCopy[pStart + i]
}
return pTarget
}
//Byte配列を文字列化
fun ByteArrayToString(pBytes: ByteArray): String{
var wRsult : String = ""
for (b in pBytes) {
wRsult += String.format("%02X", b) + ":"
}
return wRsult
}
//Byte配列を数値化
@ExperimentalUnsignedTypes
fun ByteArrayToInt(pBytes: ByteArray): Int {
var wResult: Int = 0
for (b in pBytes.reversed()){
wResult = wResult shl 8
wResult += b.toUByte().toInt()
}
return wResult
}
//ここまでデータ処理関数
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="jp.sakujira.opensesame">
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:theme="@android:style/Theme.Translucent"><!--実行時にActivityが見えないように-->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
</manifest>
kotlinx.coroutinesを使うので、buid.gradle(app)に以下を追加
dependencies {
・・・・
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:X.X.X'
}
##参考資料
####アドバタイズパケットの分析とBeacon検討
aBeacon ~iBeacon を Android で受信する~
https://gaprot.jp/2014/03/27/abeacon/
Android Beacon Library
https://altbeacon.github.io/android-beacon-library/index.html
[Android][iBeacon] Android Beacon Library パラっと解説 その4 [距離観測]
https://dev.classmethod.jp/articles/android-beacon-library-introduction-4/
####Kotlinのサービス・レシーバーの書き方について
BLEMonitor
https://github.com/SergeiSafrigin/BLEMonitor/blob/master/src/kcg/ble/BleMonitorService.java
AndroidでServiceと通信する(Kotlinサンプル)
https://qiita.com/satken2/items/49dd76d848ceb208e937
コジオニルク-プロセスが同一の場合の例
https://kojion.com/posts/649
####Notificationの書き方
OreoでNotificationを表示させる方法
https://qiita.com/naoi/items/367fc23e55292c50d459
Androidで(ヘッドアップ)通知を表示するサンプル
https://qiita.com/gpsnmeajp/items/33392aac8d00b55bce75
####Activityを見せなくする方法
Theme.Translucentを継承せずにActivityの背景を透過にする
https://qiita.com/kgmyshin/items/a259f31b06ebab637044
####考察:このアプリのセキュリティについて
単純にこのアプリは
Sesame公式APP:ロック解除→アプリ起動→Sesame認識→ロック解除
を
このアプリ:ロック解除→NFCかざす→ロック解除
としているので、公式アプリと同じ位の危険性と考えています。
自宅や職場のwifiでロック解除するアプリを使っていたとしても、同じくらい、多少待ち時間が少ない分危険性は高くなるかなと考えています。
仮に、NFCカード側を持っていかれても、Sesameに関する情報はなにもないので問題なし。
また、NFCカード、スマホ、スマホのロック解除が揃っているなら、公式アプリで解除するでしょうし、そもそも鍵を開けるためにはBLEの通信範囲にいないといけないので、「こんな事をしている」ことを知っている人に絞られるかなっと思ったり。
まぁ、Sesameを着けているいる時点である程度は家の鍵にバックドアの可能性が増えていると思っているので、まぁいいかなっと自分自身は考えています。
(まぁ、アプリの情報抜かれたらと考えるのであれば、そもそもこのアプリ自体が抜いているので、、、、)
####編集後記
この話についてはココで区切り。
あとやるとしたら、施錠解錠の動画の撮影くらいかな~
最終版の書き方にして、かざす→施錠・解錠の時間が短くなったので、バックグランドサービスでSesameをScanLEで定期的に探そうかと思っていましたが、しなくていいかなっと感じています。
(その調査の結果、アドバタイズパケットからMacDataの位置が理解できたのですが、、、、
楽しかったけど、疲れた、、、。