2021年における個人に依存しないGoogle Apps Script(GAS)運用の話
これはなに
2021年における個人に依存しないGoogle Apps Script(GAS)運用の話
会社やチームなどでGASを使う場合に、可能な限りGASの管理を個人に依存しない運用方法を雑に書いておきます
GASのコード管理をどうするか?
書いたGASはすべてGitで管理する
GitHubなどのGitのホスティングサービスを利用して、書いたGASを共有する(必要な場合 誰でも変更できるようにする)
GASをどうやって書くか?
ローカルの好きなエディタで書く
ローカルで書いたGASをどうやってデプロイするかは、この後出てくるclaspを使う
GASのサイト上で編集すると、Gitとの差分がめちゃくちゃになるのでローカルで書くのがおすすめ
デバッグ・ちょっとした動作確認など 書いたコードが消えても問題ない場合は、GASのサイト上で編集して動かすのは許容
GASのデプロイをどうするか?
claspを使う
claspは説明は以下のリンクなどを参照
他人が書いたGASの編集・デプロイをどうするか?
書いたGASはGitHubなどで管理することで権限があれば誰でも閲覧・編集ができるようになる
なので、git cloneして、ローカルでGASを編集して、claspを使ってデプロイすればOK
トリガーどうするのか?
GASのトリガーは個人依存しやすいため、個人アカウントでのトリガー作成を極力避ける方がベストです
共有のGoogleアカウントを作成し、トリガー作成・変更は必ずそのアカウントから行うようにする
こうすることで、トリガーが個人アカウントに依存することを避けます
memo
CIを活用すればデプロイも個人依存しなくなりそう
Android11から導入されたAuto-reset permissionsを調べてみてわかったこと
はじめに
本記事はAndroid Advent Calendar 2020 14日目の記事です
本記事では、Android11から導入されたAuto-reset permissionsを調べてみてわかったことを書いていきます
Auto-reset permissionsとは?
長期間アプリを使わなかった場合、ユーザーがアプリに付与した機密情報に関わるPermission(権限)が自動的にリセットされる仕組みです Protection LevelがDangerousに分類されるPermissionが自動リセットの対象です つまり、すべてのPermissionが自動リセットの対象ではないということです
公式ドキュメントの説明はこのあたりか、ブログを読むといいかと思います
アプリに対してAuto-reset permissionsが適用されるかどうかは、ユーザの設定次第になります 設定アプリの各アプリケーション Permission画面下部に、長期間アプリを使わなかった場合にPermissionを自動的にリセットするかどうかの設定があります
targetSDKVersionが30以上のアプリはこの設定値が初期状態で有効、targetSDKVersionが30未満のアプリこの設定値が初期状態で無効になります 公式ドキュメントでは、targetSdkVersionが30以上のアプリにAuto-reset permissionsが適用されるっぽい記載になっていますが、targetSDKVersionが30未満のアプリにも適用されることがあります
最終的には、ユーザの設定次第なので、targetSdkVersionが30以上のアプリでもユーザが設定値をOFFにすれば、Auto-reset permissionsは適用されなくなりますし、targetSDKVersionが30未満のアプリでもユーザが設定値をONにすれば、Auto-reset permissionsは適用されます
Auto-reset permissionsが実行され、Permissionが自動リセットされると通知が来ます 通知タップで遷移する画面でアプリをアンインストールできるのは便利ですね(自動リセットされる = 長期間使用していないアプリなのでアンインストールも検討してねってことだろう)
Auto-reset permissionsでPermissionが自動リセットされると通知が来る | 通知タップで遷移する画面 |
---|---|
ここからは、Auto-reset permissionsの内部動作等について調べてわかったことなどを書いていきます
調べた理由
- アプリをAndroid 11対応する上で、Auto-reset permissionsがどれだけ影響度がある機能なのかを理解したかったため
- Android Advent Calendarのネタにちょうど良さそうだと思ったため
- Android Code Searchを使ってCode Readingしてみたかったため
わかったこと要約
- アプリ実装への影響度そこまで大きくない
- 思ったよりAuto-reset permissionsが実行される頻度が少ない
Android Code Searchを使って内部実装を調べる
Android Code Searchを使ってAuto-reset permissionsの内部実装を調べました
Auto-reset permissionsに関する実装はだいたいAutoRevokePermissionsクラスにまとまってます
長期間アプリを使わなかった場合の「長期間」ってどれくらいの期間なのか
疑問に思ったのが、長期間アプリを使わなかった場合の「長期間」ってどれくらいの期間なの?ってところです
期間に関するしきい値の定数がありました デフォルトでは90日間らしいです
private val DEFAULT_UNUSED_THRESHOLD_MS = if (AUTO_REVOKE_ENABLED) DAYS.toMillis(90) else Long.MAX_VALUE
期間のしきい値を変更する
デフォルトってことは、つまり期間のしきい値を変更する方法が存在します 期間のしきい値にどの値を使うかを決めているコードを抜粋したのが以下です
fun getUnusedThresholdMs(context: Context) = when { DEBUG_OVERRIDE_THRESHOLDS -> SECONDS.toMillis(1) TeamfoodSettings.get(context) != null -> TeamfoodSettings.get(context)!!.unusedThresholdMs else -> DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS, PROPERTY_AUTO_REVOKE_UNUSED_THRESHOLD_MILLIS, DEFAULT_UNUSED_THRESHOLD_MS) }
private data class TeamfoodSettings( val enabledForPreRApps: Boolean, val unusedThresholdMs: Long, val checkFrequencyMs: Long ) { companion object { private var cached: TeamfoodSettings? = null fun get(context: Context): TeamfoodSettings? { if (cached != null) return cached return Settings.Global.getString(context.contentResolver, "auto_revoke_parameters" /* Settings.Global.AUTO_REVOKE_PARAMETERS */)?.let { str -> if (DEBUG) { DumpableLog.i(LOG_TAG, "Parsing teamfood setting value: $str") } str.split(",") .mapNotNull { val keyValue = it.split("=") keyValue.getOrNull(0)?.let { key -> key to keyValue.getOrNull(1) } } .toMap() .let { pairs -> TeamfoodSettings( enabledForPreRApps = pairs["enabledForPreRApps"] == "true", unusedThresholdMs = pairs["unusedThresholdMs"]?.toLongOrNull() ?: DEFAULT_UNUSED_THRESHOLD_MS, checkFrequencyMs = pairs["checkFrequencyMs"]?.toLongOrNull() ?: DEFAULT_CHECK_FREQUENCY_MS) } }.also { cached = it if (DEBUG) { Log.i(LOG_TAG, "Parsed teamfood setting value: $it") } } } }
Settings.Global.AUTO_REVOKE_PARAMETERSから設定値を読み込んでいます(Settings.Global.AUTO_REVOKE_PARAMETERS はhide APIなので、通常のアプリケーションからは定数を参照できません)
Settings.Global.AUTO_REVOKE_PARAMETERSの設定値にunusedThresholdMsが存在すれば、未使用期間のしきい値にそれを使うような実装になっています
つまり、adb shellから以下のコマンドを実行することで、未使用期間のしきい値を変更できます
// 未使用期間のしきい値を1分に変更 adb shell settings put global auto_revoke_parameters enabledForPreRApps=false,unusedThresholdMs=60000,checkFrequencyMs=60000 // 設定できているかを確認 adb shell settings get global auto_revoke_parameters
adbではなくコードからSettings.Global.AUTO_REVOKE_PARAMETERSを設定しようと試みましたが、これはさすがにセキュリティ的に無理でした
// java.lang.SecurityException: Permission denial: writing to settings requires:android.permission.WRITE_SECURE_SETTINGS Settings.Global.putString(contentResolver, "auto_revoke_parameters", "enabledForPreRApps=false,unusedThresholdMs=60000,checkFrequencyMs=60000")
設定は無理でしたが、設定値の読み込みはコードからできました
Settings.Global.getString(contentResolver,"auto_revoke_parameters")
Settings.Globalを設定する以外に、DeviceConfigを設定する方法もあります adb shellから以下のコマンドを実行することで、未使用期間のしきい値を変更できます
// 未使用期間のしきい値を1分に変更 adb shell device_config put permissions auto_revoke_unused_threshold_millis2 60000 // 設定できているかを確認 adb shell device_config get permissions auto_revoke_unused_threshold_millis2
どのくらいの周期でAuto-reset permissionsが実行されるのか
Auto-reset permissionsが実行される周期に関する定数がありました デフォルトでは15日間隔らしいです(広くね...?🤔)
private val DEFAULT_CHECK_FREQUENCY_MS = DAYS.toMillis(15)
内部ではJobSchedulerを使って定期実行しています BOOT_COMPLETEDでAutoRevokeOnBootReceiverを起動させて、そこでJobSchedulerを登録して、AutoRevokeServiceが定期的に実行されるようにしてます
Auto-reset permissionsの実行周期を変更する
こちらも期間のしきい値と同様、Settings.GlobalかDeviceConfigを設定することで変更できます
private fun getCheckFrequencyMs(context: Context) = when { TeamfoodSettings.get(context) != null -> TeamfoodSettings.get(context)!!.checkFrequencyMs else -> DeviceConfig.getLong( DeviceConfig.NAMESPACE_PERMISSIONS, PROPERTY_AUTO_REVOKE_CHECK_FREQUENCY_MILLIS, DEFAULT_CHECK_FREQUENCY_MS) }
変更する際の注意点として、JobSchedulerの関係上 実行周期を15分以下にできません
Settings.Global
adb shellから以下のコマンドを実行することで、実行周期を変更できます
// 実行周期を15分に変更 adb shell settings put global auto_revoke_parameters enabledForPreRApps=false,unusedThresholdMs=60000,checkFrequencyMs=900000 // 設定できているかを確認 adb shell settings get global auto_revoke_parameters
DeviceConfig
adb shellから以下のコマンドを実行することで、実行周期を変更できます
// 実行周期を15分に変更 adb shell device_config put permissions auto_revoke_check_frequency_millis 900000 // 設定できているかを確認 adb shell device_config get permissions auto_revoke_check_frequency_millis
Auto-reset permissionsをすぐに実行したい!!
定期実行にJobSchedulerを使ってるので、adb shellから以下のコマンドを実行することで、Auto-reset permissionsをすぐに実行させることができます
adb shell cmd jobscheduler run -u 0 -f com.google.android.permissioncontroller 2
Auto-reset permissionsの動作確認をしたい時に便利です
ACTIVITY_RECOGNITION Permissionは自動リセットの対象外
Protection levelがDangerousに分類されるPermissionでも、android.Manifest.permission.ACTIVITY_RECOGNITIONのPermissionだけは自動リセットの対象外みたいです
private val EXEMPT_PERMISSIONS = listOf( android.Manifest.permission.ACTIVITY_RECOGNITION)
アプリの未使用期間はどのように調べているのか
UsageStatsManagerを利用してゴニョゴニョ調べているようです 詳細はコードを読んでみてください
Auto-reset permissionsの動作ログを見る
以下のlogcatで見れます
adb logcat -v time -s AutoRevokePermissions
dumpsysから過去書き込んだログは見れます
adb shell dumpsys permissionmgr
Auto-reset permissionsの内部ステータスを見たい
dumpsysから色々見れます
adb shell dumpsys permissionmgr
自身のアプリのAuto-reset permissionsの設定値が知りたい
PackageManager#isAutoRevokeWhitelistedメソッドで知ることができます
applicationContext.packageManager.isAutoRevokeWhitelisted
自身のアプリのAuto-reset permissions設定画面に遷移させたい
Intent.ACTION_AUTO_REVOKE_PERMISSIONSで遷移できます
val i = Intent(Intent.ACTION_AUTO_REVOKE_PERMISSIONS).apply { data = Uri.fromParts("package", packageName, null) } startActivity(i)
おわりに
調べてみると公式ドキュメントには記載されていないことがいくつかわかったので面白かったです Permissionの実装を普通に行っていれば、Auto-reset permissionsは何も面倒なことではないのですが、詳細な動作が知りたくて調べてみました 久々にAndroidの一機能の内部実装を読んでみたのですが、Android Code Searchがとにかく便利だった!
今回Auto-reset permissionsを調べる上で、ちょっと書いたコードをGitHubで公開してます
Dart・Flutterを学ぶ日々 その13
MoorのMigrations
結構簡単だった
package_info
設定画面とかによくあるアプリのバージョンとか表示するためには、package_info経由で情報と取ってきて表示するのが良さそう
url_launcher
外部ブラウザでリンクを開くならこれ使う
FlutterFire
FirebaseのFlutter Pluginがまとまってるページ
Dart・Flutterを学ぶ日々 その12
kIsWeb、kDebugMode
アプリケーションがWebで動いているかどうかの判定に使える定数
kDebugMode
アプリケーションがデバッグモードで動いてるかどうかの判定に使える定数
kReleaseModeもある
private function
_
をつけるのね
Unlike Java, Dart doesn’t have the keywords public, protected, and private. If an identifier starts with an underscore (_), it’s private to its library.
shared_preferences
ローカルで値を保存・取得したい場合のplugin
SimpleDialog
ダイアログでメニュー選択式のやつ実装する時に使える
Flutterの基本的なレイアウトの話
めっちゃ助かる
ListViewをColumnに格納する方法【Flutter】
同じく困ったので助かった