Slide 1

Slide 1 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kioskアプリと端末の作り方 Feb, 08, 2018 DroidKaigi 2018 Tomoya Miwa Core System Development Dept. Automotive Business Unit. DeNA Co., Ltd.

Slide 2

Slide 2 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 自己紹介 ● 三輪 智也(みわ ともや) @tomoya0x00 ● Android & 組み込みエンジニア ● 株式会社ディー・エヌ・エー(11/16入社) ○ オートモーティブ事業本部 基幹システム開発部 ● これまでの開発経験 ○ 車載機器(カーナビなど) ○ BLEデバイスファームウェア、BLEアプリ(iOS/Android) ○ KioskなAndroidアプリ 2

Slide 3

Slide 3 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. サンプルソース https://goo.gl/ryz8mX 3

Slide 4

Slide 4 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. アウトライン ●Kiosk端末とは? ●Kiosk端末の実現方法 ●活用事例紹介:タクベル ●Kiosk端末開発のノウハウ ●まとめ 4

Slide 5

Slide 5 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kiosk端末とは?

Slide 6

Slide 6 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kiosk端末とは? 6 Interactive kiosk. (2017, December 8). In Wikipedia, The Free Encyclopedia. Retrieved 07:39, February 7, 2018, from https://en.wikipedia.org/w/index.php?title=Interactive_kiosk&oldid=814355848 “An interactive kiosk is a computer terminal featuring specialized hardware and software that provides access to information and applications for communication, commerce, entertainment, or education.”

Slide 7

Slide 7 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kiosk端末とは? つまり、特定用途に特化した情報端末 e.g.回転寿司、居酒屋、カラオケのタッチパネル 7

Slide 8

Slide 8 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kiosk端末の実現方法

Slide 9

Slide 9 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. こんなことをやりたい ● 通常操作ではKioskアプリ以外の画面に遷移できない ● 端末を再起動しても強制的にKioskアプリが起動 ● 特定の手順でKioskアプリ以外の画面に遷移可能 9

Slide 10

Slide 10 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. こんなことをやりたい ● 通常操作ではKioskアプリ以外の画面に遷移できない ● 端末を再起動しても強制的にKioskアプリが起動 ● 特定の手順でKioskアプリ以外の画面に遷移可能 10

Slide 11

Slide 11 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Screen Pinning

Slide 12

Slide 12 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Screen Pinningとは ● Android 5.0から追加された機能 ○ Activity#startLockTaskで開始 ● 特定アプリから他画面への遷移を制限 ● Pinning開始にユーザー合意が必要 ● 「戻る」と「最近」の長押しで解除可能 12

Slide 13

Slide 13 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 簡単に解除できるんじゃ 意味が無い

Slide 14

Slide 14 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. そこでDevice Owner

Slide 15

Slide 15 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Device Ownerとは 15 “A device owner is a specialized type of device administrator that has the additional ability to create and remove secondary users and to configure global settings on the device. “ Android 5.0 APIs by the Android Open Source Project is licensed under the Creative Commons Attribution 2.5 License. Retrieved Feburary 07, 2018, from https://developer.android.com/about/versions/android- 5.0.html

Slide 16

Slide 16 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Device Ownerとは ● Android端末に様々な制約を課すことが出来る特別な管理者 ○ Android 5.0で追加された ● Device Ownerに指定できるのは、 DeviceAdminReceiverを継承したクラスを実装したアプリ 16

Slide 17

Slide 17 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Device Ownerとは ● Device Ownerは特定のアプリにLock task modeを許可できる ● Lock task mode ○ ユーザー合意無しに開始出来るScreen Pinning ○ 通常のユーザー操作では解除できない ○ Activity#startLockTaskで開始 ■ Screen Pinningと同じ 17

Slide 18

Slide 18 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Screen PinningとLock task modeの違い 18 Set up Single-Purpose Devices by the Android Open Source Project is licensed under the Creative Commons Attribution 2.5 License. Retrieved Feburary 07, 2018, from https://developer.android.com/work/cosu.html

Slide 19

Slide 19 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Device Ownerの注意点

Slide 20

Slide 20 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Device Ownerの注意点 Device Owner指定には、基本的に端末初期化が必要 20 “You must provision the device owner mode of operation during the initial setup of a new device or after a factory reset. Device owner mode can’t be provisioned on a device at any other time.” EMM developer's guide by Google is licensed under the Creative Commons Attribution 3.0 License. Retrieved Feburary 07, 2018, from https://developers.google.com/android/work/prov- devices#device_must_be_new_or_factory_reset

Slide 21

Slide 21 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Device Ownerの注意点 21 ● WiFiやアカウント設定後だと、DeviceOwner指定に失敗する ○ ただし、挙動は機種によって異なる ○ Nexus 5はWiFi設定後でもOKだけど、MediaPad M2はNG ■ 挙動の違いに気付かなくて、かなりハマった ■ モバイルネットワークの設定は問題無しだったり

Slide 22

Slide 22 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. こんなことをやりたい ● 通常操作ではKioskアプリ以外の画面に遷移できない ● 端末を再起動しても強制的にKioskアプリが起動 ● 特定の手順でKioskアプリ以外の画面に遷移可能 22

Slide 23

Slide 23 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 強制的にホームアプリ化

Slide 24

Slide 24 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. DevicePolicyManager #addPersistentPreferredActivity

Slide 25

Slide 25 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 強制的にホームアプリ化するには? 特定のActivityをホームアプリとして登録可能 25 DevicePolicyManager by the Android Open Source Project is licensed under the Creative Commons Attribution 2.5 License. Retrieved Feburary 07, 2018, from https://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#addPersistentPrefe rredActivity(android.content.ComponentName%2C%20android.content.IntentFilter%2C%20android.content. ComponentName) DevicePolicyManager#addPersistentPreferredActivity “Called by a profile owner or device owner to add a default intent handler activity for intents that match a certain intent filter. This activity will remain the default intent handler even if the set of potential event handlers for the intent filter changes and if the intent preferences are reset.”

Slide 26

Slide 26 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. こんなことをやりたい ● 通常操作ではKioskアプリ以外の画面に遷移できない ● 端末を再起動しても強制的にKioskアプリが起動 ● 特定の手順でKioskアプリ以外の画面に遷移可能 26

Slide 27

Slide 27 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Lock task modeの解除

Slide 28

Slide 28 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Activity#stopLockTask

Slide 29

Slide 29 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Lock task modeを解除するには? 特定のパスワード入力後にLock task mode解除が実現可能 29 Activity by the Android Open Source Project is licensed under the Creative Commons Attribution 2.5 License. Retrieved Feburary 07, 2018, from https://developer.android.com/reference/android/app/Activity.html#stopLockTask() Activity#stopLockTask “Allow the user to switch away from the current task. Called to end the mode started by startLockTask(). This can only be called by activities that have successfully called startLockTask previously. This will allow the user to exit this app and move onto other activities.”

Slide 30

Slide 30 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. ホームアプリ化の解除

Slide 31

Slide 31 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. DevicePolicyManager #clearPackagePersistentPreferredActivities

Slide 32

Slide 32 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. ホームアプリ化を解除するには? パッケージ名がわかればホームアプリ化解除可能 32 DevicePolicyManager by the Android Open Source Project is licensed under the Creative Commons Attribution 2.5 License. Retrieved Feburary 07, 2018, from https://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#clearPackagePersis tentPreferredActivities(android.content.ComponentName%2C%20java.lang.String) DevicePolicyManager#clearPackagePersistentPreferredActivities “Called by a profile owner or device owner to remove all persistent intent handler preferences associated with the given package that were set by addPersistentPreferredActivity(ComponentName, IntentFilter, ComponentName).”

Slide 33

Slide 33 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. こんなことをやりたい ● 通常操作ではKioskアプリ以外の画面に遷移できない ● 端末を再起動しても強制的にKioskアプリが起動 ● 特定の手順でKioskアプリ以外の画面に遷移可能 33

Slide 34

Slide 34 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 実際にKiosk端末化してみる

Slide 35

Slide 35 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kiosk端末化の流れ 1. Kioskアプリの準備 2. 端末の初期化 3. Kioskアプリのインストール 4. Device Ownerの指定 35

Slide 36

Slide 36 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kiosk端末化の流れ 1. Kioskアプリの準備 2. 端末の初期化 3. Kioskアプリのインストール 4. Device Ownerの指定 36

Slide 37

Slide 37 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kioskアプリの準備 1. DeviceAdminReceiver継承クラスの実装 2. KioskUtils作成 3. AndroidManifestにReceiver登録 4. AndroidManifestにIntentFilterカテゴリ追加 5. MainActivityからKiosk開始/終了 37

Slide 38

Slide 38 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. DeviceAdminReceiver継承クラスの実装 class AdminReceiver: DeviceAdminReceiver() { // Device Owner指定時に実行される override fun onEnabled( context: Context, intent: Intent ) { super.onEnabled(context, intent) // Lock task modeを許可する KioskUtils(context).setLockTaskPackage() } }

Slide 39

Slide 39 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. KioskUtils作成 class KioskUtils(private val context: Context) { private val deviceAdmin = ComponentName(context, AdminReceiver::class.java) private val dpm = context.getSystemService( Context.DEVICE_POLICY_SERVICE ) as DevicePolicyManager fun setLockTaskPackage() {...} fun setHomeActivity(activity: Activity) {...} fun resetHomeActivity() {...} fun hasDeviceOwnerPermission(): Boolean {...} fun start(activity: Activity) {...} fun stop(activity: Activity) {...} }

Slide 40

Slide 40 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. KioskUtils作成 class KioskUtils(private val context: Context) { private val deviceAdmin = ComponentName(context, AdminReceiver::class.java) private val dpm = context.getSystemService( Context.DEVICE_POLICY_SERVICE ) as DevicePolicyManager fun setLockTaskPackage() {...} fun setHomeActivity(activity: Activity) {...} fun resetHomeActivity() {...} fun hasDeviceOwnerPermission(): Boolean {...} fun start(activity: Activity) {...} fun stop(activity: Activity) {...} }

Slide 41

Slide 41 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. KioskUtils作成 // Lock task modeを許可する fun setLockTaskPackage() = dpm.setLockTaskPackages( deviceAdmin, arrayOf(context.packageName)) // DeviceOwner権限を持っているか否か fun hasDeviceOwnerPermission(): Boolean = dpm.isAdminActive(deviceAdmin) && dpm.isDeviceOwnerApp(context.packageName)

Slide 42

Slide 42 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. KioskUtils作成 class KioskUtils(private val context: Context) { private val deviceAdmin = ComponentName(context, AdminReceiver::class.java) private val dpm = context.getSystemService( Context.DEVICE_POLICY_SERVICE ) as DevicePolicyManager fun setLockTaskPackage() {...} fun setHomeActivity(activity: Activity) {...} fun resetHomeActivity() {...} fun hasDeviceOwnerPermission(): Boolean {...} fun start(activity: Activity) {...} fun stop(activity: Activity) {...} }

Slide 43

Slide 43 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. KioskUtils作成 // ホームアプリ化 fun setHomeActivity(activity: Activity) { val intentFilter = IntentFilter(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_DEFAULT) addCategory(Intent.CATEGORY_HOME) } val home = ComponentName(context, activity::class.java) dpm.addPersistentPreferredActivity( deviceAdmin, intentFilter, home) } // ホームアプリ化解除 fun resetHomeActivity() = dpm.clearPackagePersistentPreferredActivities( deviceAdmin, context.packageName)

Slide 44

Slide 44 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. KioskUtils作成 class KioskUtils(private val context: Context) { private val deviceAdmin = ComponentName(context, AdminReceiver::class.java) private val dpm = context.getSystemService( Context.DEVICE_POLICY_SERVICE ) as DevicePolicyManager fun setLockTaskPackage() {...} fun setHomeActivity(activity: Activity) {...} fun resetHomeActivity() {...} fun hasDeviceOwnerPermission(): Boolean {...} fun start(activity: Activity) {...} fun stop(activity: Activity) {...} }

Slide 45

Slide 45 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. KioskUtils作成 fun start(activity: Activity) { activity.startLockTask() if (hasDeviceOwnerPermission()) { setHomeActivity(activity) } } fun stop(activity: Activity) { activity.stopLockTask() if (hasDeviceOwnerPermission()) { resetHomeActivity() } }

Slide 46

Slide 46 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. AndroidManifestにReceiver登録 参考: https://developer.android.com/guide/topics/admin/device-admin.html#developing

Slide 47

Slide 47 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. AndroidManifestにReceiver登録

Slide 48

Slide 48 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. AndroidManifestにReceiver登録

Slide 49

Slide 49 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. AndroidManifestにIntentFilterカテゴリ追加

Slide 50

Slide 50 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. MainActivityからKiosk開始/終了 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) kioskUtils = KioskUtils(this) binding.kioskOnButton.setOnClickListener { kioskUtils.start(this) } binding.kioskOffButton.setOnClickListener { kioskUtils.stop(this) } }

Slide 51

Slide 51 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kiosk端末化の流れ 1. Kioskアプリの準備 2. 端末の初期化 3. Kioskアプリのインストール 4. Device Ownerの指定 51

Slide 52

Slide 52 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 端末の初期化 ● ファクトリーリセットする ● 初期設定は極力スキップすること ○ WiFiやアカウント設定すると、 Device Owner指定不可となる事がある 52

Slide 53

Slide 53 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kiosk端末化の流れ 1. Kioskアプリの準備 2. 端末の初期化 3. Kioskアプリのインストール 4. Device Ownerの指定 53

Slide 54

Slide 54 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kioskアプリのインストール ● NFC ○ あらかじめKioskアプリをサーバーにアップロードし、 KioskアプリのURLやWiFi情報などをNFCで転送する ● adb install ○ NFC非対応端末の場合、adb install 54

Slide 55

Slide 55 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kiosk端末化の流れ 1. Kioskアプリの準備 2. 端末の初期化 3. Kioskアプリのインストール 4. Device Ownerの指定 55

Slide 56

Slide 56 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Device Ownerの指定 ● NFC ○ 詳しくは調べていない ■ お仕事でKiosk化した端末はNFC非対応だったので・・・ ○ 気になる方は googlesamples をご参照 ■ https://github.com/googlesamples/android-NfcProvisioning ● adb shell dpm set-device-owner ○ 今回はこちらの方法で指定 56

Slide 57

Slide 57 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. adb shellでDeviceOwnerを指定 # adb shell dpm set-device-owner jp.gr.java_conf.miwax.kioskexample/.AdminReceiver Success: Device owner set to package jp.gr.java_conf.miwax.kioskexample Active admin set to component {jp.gr.java_conf.miwax.kioskexample/jp.gr.java_conf.miwa x.kioskexample.AdminReceiver} インストールしたアプリをDeviceOwnerとして指定

Slide 58

Slide 58 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kiosk端末化の流れ 1. Kioskアプリの準備 2. 端末の初期化 3. Kioskアプリのインストール 4. Device Ownerの指定 58

Slide 59

Slide 59 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kiosk端末のデモ

Slide 60

Slide 60 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 活用事例紹介:タクベル

Slide 61

Slide 61 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 活用事例紹介:タクベル ● タクベルとは? ● タクベルの乗務員アプリ ● 活用しているDeviceOwnerの機能紹介 (Lock task modeとホームアプリ化以外) ○ サイレントインストール(自動でAPKをインストール) ○ ワイプ(盗難・紛失時のデータ消去) ○ USBデバッグOFF ○ スクリーンショットOFF 61

Slide 62

Slide 62 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. タクベルとは? 62 “賢いタクシーアプリ「タクベル」は、交通情報を活用したよ り効率的な運行・配車が可能なサービスです。” ※正式リリースは2018年春頃を予定しています。 https://dena-taxi.jp/

Slide 63

Slide 63 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. タクベルの乗務員アプリ Android上で動作するKioskアプリ(開発中) ※下記スクショは先行モニターキャンペーン時のものでKiosk化されていない 63

Slide 64

Slide 64 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 活用している DeviceOwnerの機能紹介

Slide 65

Slide 65 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. ※入社前から実現されてた なので、自分は1行もコード書いてないです・・・すごーい!

Slide 66

Slide 66 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. サイレントインストール (自動でAPKをインストール)

Slide 67

Slide 67 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. PackageInstaller

Slide 68

Slide 68 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. サイレントインストールするには? ユーザー操作無しでAPKのインストールができる 68 Android 6.0 APIs by the Android Open Source Project is licensed under the Creative Commons Attribution 2.5 License. Retrieved Feburary 07, 2018, from https://developer.android.com/about/versions/marshmallow/android-6.0.html PackageInstaller※Android6.0からサイレントインストール可能 “Silent install and uninstall of apps by Device Owner: A Device Owner can now silently install and uninstall applications using the PackageInstaller APIs, independent of Google Play for Work. You can now provision devices through a Device Owner that fetches and installs apps without user interaction. This feature is useful for enabling one-touch provisioning of kiosks or other such devices without activating a Google account.”

Slide 69

Slide 69 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. サイレントインストールするには? val packageInstaller = context.packageManager.packageInstaller val params = PackageInstaller .SessionParams(SessionParams.MODE_FULL_INSTALL).apply { setInstallLocation(PackageInfo.INSTALL_LOCATION_AUTO) } val sessionId = packageInstaller.createSession(params) packageInstaller.openSession(sessionId).use { session -> session.openWrite("hoge", 0, file.length()).use { output -> FileInputStream(file).use { input -> input.copyTo(output) session.fsync(output) } } val dummySender = PendingIntent.getBroadcast(context, sessionId, Intent("dummy"), 0).intentSender session.commit(dummySender) }

Slide 70

Slide 70 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. サイレントインストールするには? val packageInstaller = context.packageManager.packageInstaller val params = PackageInstaller .SessionParams(SessionParams.MODE_FULL_INSTALL).apply { setInstallLocation(PackageInfo.INSTALL_LOCATION_AUTO) } val sessionId = packageInstaller.createSession(params) packageInstaller.openSession(sessionId).use { session -> session.openWrite("hoge", 0, file.length()).use { output -> FileInputStream(file).use { input -> input.copyTo(output) session.fsync(output) } } val dummySender = PendingIntent.getBroadcast(context, sessionId, Intent("dummy"), 0).intentSender session.commit(dummySender) }

Slide 71

Slide 71 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. サイレントインストールするには? val packageInstaller = context.packageManager.packageInstaller val params = PackageInstaller .SessionParams(SessionParams.MODE_FULL_INSTALL).apply { setInstallLocation(PackageInfo.INSTALL_LOCATION_AUTO) } val sessionId = packageInstaller.createSession(params) packageInstaller.openSession(sessionId).use { session -> session.openWrite("hoge", 0, file.length()).use { output -> FileInputStream(file).use { input -> input.copyTo(output) session.fsync(output) } } val dummySender = PendingIntent.getBroadcast(context, sessionId, Intent("dummy"), 0).intentSender session.commit(dummySender) }

Slide 72

Slide 72 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. サイレントインストールするには? val packageInstaller = context.packageManager.packageInstaller val params = PackageInstaller .SessionParams(SessionParams.MODE_FULL_INSTALL).apply { setInstallLocation(PackageInfo.INSTALL_LOCATION_AUTO) } val sessionId = packageInstaller.createSession(params) packageInstaller.openSession(sessionId).use { session -> session.openWrite("hoge", 0, file.length()).use { output -> FileInputStream(file).use { input -> input.copyTo(output) session.fsync(output) } } val dummySender = PendingIntent.getBroadcast(context, sessionId, Intent("dummy"), 0).intentSender session.commit(dummySender) }

Slide 73

Slide 73 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. サイレントインストールのデモ

Slide 74

Slide 74 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. ワイプ (盗難・紛失時のデータ消去)

Slide 75

Slide 75 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. DevicePolicyManager #wipeData

Slide 76

Slide 76 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. ワイプするには? 強制的に全データ消去できる 76 DevicePolicyManager by the Android Open Source Project is licensed under the Creative Commons Attribution 2.5 License. Retrieved Feburary 07, 2018, from https://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#wipeData(int) DevicePolicyManager#wipeData “Ask that all user data be wiped. If called as a secondary user, the user will be removed and other users will remain unaffected. Calling from the primary user will cause the device to reboot, erasing all device data - including all the secondary users and their data - while booting up.”

Slide 77

Slide 77 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. ワイプするには? val deviceAdmin = ComponentName(context, AdminReceiver::class.java) val dpm = context.getSystemService( Context.DEVICE_POLICY_SERVICE ) as DevicePolicyManager // ワイプ dpm.wipeData(0)

Slide 78

Slide 78 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. USBデバッグOFF

Slide 79

Slide 79 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. DevicePolicyManager #setGlobalSetting

Slide 80

Slide 80 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. USBデバッグOFFするには? 強制的にADBやUSBマスストレージのON/OFFができる 80 DevicePolicyManager by the Android Open Source Project is licensed under the Creative Commons Attribution 2.5 License. Retrieved Feburary 07, 2018, from https://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#setGlobalSetting(andr oid.content.ComponentName%2C%20java.lang.String%2C%20java.lang.String) DevicePolicyManager#setGlobalSetting “Called by device owners to update Settings.Global settings. Validation that the value of the setting is in the correct form for the setting type should be performed by the caller.”

Slide 81

Slide 81 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. USBデバッグOFFするには? val deviceAdmin = ComponentName(context, AdminReceiver::class.java) val dpm = context.getSystemService( Context.DEVICE_POLICY_SERVICE ) as DevicePolicyManager // USBデバッグOFF dpm.setGlobalSetting(deviceAdmin, Settings.Global.ADB_ENABLED, "0")

Slide 82

Slide 82 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. スクリーンショットOFF

Slide 83

Slide 83 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. DevicePolicyManager #setScreenCaptureDisabled

Slide 84

Slide 84 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. スクリーンショットOFFするには? 強制的にスクリーンショットON/OFFができる 84 DevicePolicyManager by the Android Open Source Project is licensed under the Creative Commons Attribution 2.5 License. Retrieved Feburary 07, 2018, from https://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#setScreenCaptureDisa bled(android.content.ComponentName%2C%20boolean) “Called by a device/profile owner to set whether the screen capture is disabled.” DevicePolicyManager#setScreenCaptureDisabled

Slide 85

Slide 85 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. スクリーンショットOFFするには? val deviceAdmin = ComponentName(context, AdminReceiver::class.java) val dpm = context.getSystemService( Context.DEVICE_POLICY_SERVICE ) as DevicePolicyManager // スクリーンショットOFF dpm.setScreenCaptureDisabled(deviceAdmin, true)

Slide 86

Slide 86 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kiosk端末開発のノウハウ

Slide 87

Slide 87 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kiosk端末開発のノウハウ ● バッテリー残量と電波強度が表示されない ● ホームアプリ化すると再起動後にロック画面が表示される ● Kioskアプリが落ちると他画面に遷移できてしまう ● 端末起動直後のアップデート確認処理が毎回失敗する ● ホームアプリ化が解除されない ● デバッグメニューが欲しい 87

Slide 88

Slide 88 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. バッテリー残量と電波強度が表示されない ● Lock task modeはデフォルトでステータスバー非表示 ○ バッテリー残量と電波強度がわからなくなる ● どちらもプログラマブルに取得可能 ○ Intent.ACTION_BATTERY_CHANGED ○ TelephonyManager ● 取得した情報をKioskアプリ側で表示すればOK 88

Slide 89

Slide 89 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. ホームアプリ化すると再起動後にロック画面が表示される ● Lock task modeだけだとロック画面表示されない ● しかし、ホームアプリ化して端末再起動すると ロック画面が表示されてしまう ● Androidの設定でロック画面を「なし」に変更が楽 89

Slide 90

Slide 90 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kioskアプリが落ちると他画面に遷移できてしまう 90

Slide 91

Slide 91 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kioskアプリが落ちると他画面に遷移できてしまう 絶対に落ちないアプリを作ればOK 91

Slide 92

Slide 92 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. Kioskアプリが落ちると他画面に遷移できてしまう 絶対に落ちないアプリを作ればOK 92 ・・・というのは難しいので、落ちたらアプリを再起動させる

Slide 93

Slide 93 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 落ちたらアプリを再起動させる private fun setDefaultUncaughtExceptionHandler() { val pendingIntent = ... val origin = Thread.getDefaultUncaughtExceptionHandler() Thread.setDefaultUncaughtExceptionHandler( object : Thread.UncaughtExceptionHandler { override fun uncaughtException(thread: Thread, throwable: Throwable) { val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 100, pendingIntent) } finally { origin.uncaughtException(thread, throwable) } } }) }

Slide 94

Slide 94 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 落ちたらアプリを再起動させる private fun setDefaultUncaughtExceptionHandler() { val pendingIntent = ... val origin = Thread.getDefaultUncaughtExceptionHandler() Thread.setDefaultUncaughtExceptionHandler( object : Thread.UncaughtExceptionHandler { override fun uncaughtException(thread: Thread, throwable: Throwable) { val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 100, pendingIntent) } finally { origin.uncaughtException(thread, throwable) } } }) }

Slide 95

Slide 95 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 落ちたらアプリを再起動させる val pendingIntent = PendingIntent.getActivity( application.baseContext, REQUEST_START, Intent(intent), PendingIntent.FLAG_CANCEL_CURRENT )

Slide 96

Slide 96 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 落ちたらアプリを再起動のデモ

Slide 97

Slide 97 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. 端末起動直後のアップデート確認処理が毎回失敗する ● 端末起動直後はインターネット接続確立していない事がある ● 特にモバイルネットワークは接続確立に時間がかかる ○ 端末起動≒Kioskアプリの起動から、 約1分程度かかる事もある ● 通信リトライ回数と間隔を多めに取るなどで対策! 97 必ず、モバイルネットワーク&端末起動直後で 実機テストしよう!!!

Slide 98

Slide 98 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. ホームアプリ化が解除されない ● DeviceOwner権限を削除しても、ホームアプリ化されたまま ○ DevicePolicyManager#clearDeviceOwnerAppで削除可 ● 設定画面でホームアプリ変更しても無視される ○ しかも、設定画面からアンインストールもできない 98 DeviceOwner権限削除前にホームアプリ化解除 or セキュリティ->端末管理アプリからチェック外してadb uninstall

Slide 99

Slide 99 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. デバッグメニューが欲しい ● 巷で流行っているNotificationに常駐させる案は使えない ○ ステータスバーが非表示となるので ● デバッグメニューを表示するボタンを、 ホームアプリ化したActivityの邪魔にならない箇所に追加する ○ BuildConfig.DEBUGで有効/無効を切り替える 99

Slide 100

Slide 100 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. まとめ

Slide 101

Slide 101 text

Copyright © DeNA Co.,Ltd. All Rights Reserved. まとめ ● Device Owner活用でKiosk端末が実現できる ○ 色々な制約を課すことができる ○ APKの自動アップデートも可能 ○ Device Owner指定には条件があるので注意 ● Kiosk端末開発固有の注意・工夫が必要 ○ 端末起動後のネットワーク接続時間 ○ デバッグメニューの実現方法 10 1