Skip to content
This repository has been archived by the owner on Apr 12, 2023. It is now read-only.

Fix issue that background task doesn't work in Android. #27

Closed

Conversation

keiji
Copy link
Collaborator

@keiji keiji commented Feb 26, 2021

I confirmed that worker tasks do not start if constraints RequiresDeviceIdle are set true.

Purpose

Fix issue #25

Does this introduce a breaking change?

[ ] Yes
[x] No

Pull Request Type

What kind of change does this Pull Request introduce?

[x] Bugfix
[ ] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Documentation content changes
[ ] Other... Please describe:

How to Test

  1. Launch an application
  2. On console(terminal), type adb command adb shell dumpsys jobscheduler
  3. Confirm job status "Minimum latency" and "Required constraints" correct.
  JOB #u0a91/104: 3d7af17 APP_PACKAGE_NAME.APP_PACKAGE_NAME/androidx.work.impl.background.systemjob.SystemJobService
    u0a91 tag=*job*/APP_PACKAGE_NAME.APP_PACKAGE_NAME/androidx.work.impl.background.systemjob.SystemJobService
    Source: uid=u0a91 user=0 pkg=APP_PACKAGE_NAME.APP_PACKAGE_NAME
    JobInfo:
      Service: APP_PACKAGE_NAME.APP_PACKAGE_NAME/androidx.work.impl.background.systemjob.SystemJobService
      Requires: charging=false batteryNotLow=true deviceIdle=false
      Extras: mParcelledData.dataSize=180
      Network type: NetworkRequest [ NONE id=0, [ Capabilities: INTERNET&NOT_RESTRICTED&TRUSTED&VALIDATED Unwanted:  Uid: 10091] ]
      Minimum latency: +5h59m59s992ms
      Backoff: policy=1 initial=+30s0ms
      Has early constraint
    Required constraints: BATTERY_NOT_LOW TIMING_DELAY CONNECTIVITY [0x90000002]
    Satisfied constraints: BATTERY_NOT_LOW CONNECTIVITY DEVICE_NOT_DOZING BACKGROUND_NOT_RESTRICTED [0x12400002]
    Unsatisfied constraints: TIMING_DELAY [0x80000000]
    Uid: active
    Tracking: BATTERY CONNECTIVITY TIME
    Network: 100
    Standby bucket: ACTIVE
    Enqueue time: -147ms
    Run time: earliest=+5h59m59s845ms, latest=none
    Last run heartbeat: 18
    Ready: false (job=false user=true !pending=true !active=true !backingup=true comp=true)

Android版で診断キーサーバーとの通信タスクが定期実行されない問題について調査し、原因の一つらしき箇所が見つかったためサジェスチョンとしてのPRとなります。

手元で最小限のコードを作成して実験したところWorkManagerWorkRequestに制約(Constraints)として、要待機状態(requiresDeviceIdle)を設定すると、Workerが実行されない現象が確認されました。

今回requiresDeviceIdleを設定した場合に定期実行されない現象については原因は解明できていませんが、現象に対応するため、requiresDeviceIdleを設定しない(デフォルトでfalse)変更をしています。

Googleの公開しているリファレンス実装「exposure-notification-android」の当該部分にはrequiresDeviceIdleがついていません。EN APIの挙動としては当該制約はなくても問題はないという認識です。

https://github.com/google/exposure-notifications-android/blob/4b7b461282b2ede6fb2a93488c6d628440052c8d/app/src/main/java/com/google/android/apps/exposurenotification/nearby/ProvideDiagnosisKeysWorker.java#L216-L219

この変更によってバックグラウンドタスクの定期実行が正常に動作するか(#25 (comment) と合わせて複合的な要因である可能性もあります)。
あとはアイドル時以外でも実行されることが、アプリとしてのCOCOAの要件を満たすかと言う話になってくると考えます。

検証方法

検証のため、COCOAのコードからWorkManagerに関連した部分を確認して、最小限で動作する検証コードを作成しました。
結果、ぼくの手元の端末ではrequiresDeviceIdletrueに設定した場合にWorkerが実行されない現象が確認されました。

検証用のコードをGistに置いておきます(コードはXamarinのものですが、Androidネイティブで同様のものを作成して実験することもできます)。

https://gist.github.com/keiji/4b41890377ab342a48432b38943a0cd3

WorkManagerは、Androidのバックグラウンドジョブを実行するライブラリで、その実体はJobScheduler(API Level 23以上の場合)です。

JobSchedulerではsetRequiresDeviceIdleの注意事項として「端末そのもののIDLEや省電力(DOZE)モードとは関係がなく、直接使われていない場合にジョブの実行を許可する」とあり、IDLEの定義は明確になっていません。

https://developer.android.com/reference/android/app/job/JobInfo.Builder#setRequiresDeviceIdle(boolean)

WorkManagerのデバッグ方法はAndroidの公式サイトで紹介されています。

https://developer.android.com/topic/libraries/architecture/workmanager/how-to/debugging?hl=ja

そこで、Android(API Level 28)のエミュレーターに検証用のアプリをインストールして、requiresDeviceIdletrueのパターンでコマンド adb shell dumpsys jobscheduler を実行したところ「IDLE制約を満たしていない」ため実行されない(Unsatisfied constraints: IDLE [0x4])という結果が得られました。

 JOB #u0a89/0: 4a47b44 dev.keiji.test4/androidx.work.impl.background.systemjob.SystemJobService
    u0a89 tag=*job*/dev.keiji.test4/androidx.work.impl.background.systemjob.SystemJobService
    Source: uid=u0a89 user=0 pkg=dev.keiji.test4
    JobInfo:
      Service: dev.keiji.test4/androidx.work.impl.background.systemjob.SystemJobService
      Requires: charging=false batteryNotLow=false deviceIdle=true
      Extras: mParcelledData.dataSize=180
      Backoff: policy=1 initial=+30s0ms
      Has early constraint
    Required constraints: TIMING_DELAY IDLE [0x80000004]
    Satisfied constraints: TIMING_DELAY DEVICE_NOT_DOZING BACKGROUND_NOT_RESTRICTED [0x82400000]
    Unsatisfied constraints: IDLE [0x4]
    Uid: active
    Tracking: IDLE
    Standby bucket: ACTIVE
    Enqueue time: -5s683ms
    Run time: earliest=-5s683ms, latest=none
    Last run heartbeat: -130
    Ready: false (job=false user=true !pending=true !active=true !backingup=true comp=true)

どのような状態がIDLEかは明確でないため、エミュレーターの電源ボタンを押して表示を消したり、 adb shell dumpsys deviceidle force-idleを用いてエミュレーターを強制的にIDLE状態に設定したりしましたが、IDLE制約を満たしてWorkerが実行されることはありませんでした。

参考

他にもsetRequiresDeviceIdleについて触れられているIssueが見つかっているので参考までに。

@keiji
Copy link
Collaborator Author

keiji commented Feb 26, 2021

補足です。

テスト自体は実機(Android 10/Essential PH-1)でも行っています。
こちらも電源を抜き、液晶を消して、しばらく放置するなどしましたが、IDLE制約の付いたWorkerの実行は確認できていません。

@tmurakami
Copy link

失礼します。

adb shell dumpsys deviceidle force-idleを用いてエミュレーターを強制的にIDLE状態に設定したりしましたが、IDLE制約を満たしてWorkerが実行されることはありませんでした。

adb shell am idle-maintenanceはいかがでしょうか?

あと、ExposureNotification.android.csは XamarinComponents の当該ソースを取り込んだものですので、直接修正してしまうと #22 で Xamarin.ExposureNotifications のバイナリを適用したタイミングで元に戻ってしまいます。
SetRequiresDeviceIdleを外すのであれば、MainApplication なり MainActivity なり適当なところでConfigureBackgroundWorkRequestを呼び出して制約を設定するほうが良いように思います。

ちなみに私の端末では、深夜0時前に COCOA を起動した状態にして放置しておくと、朝までには診断キーがダウンロードされています。

@kazumihirose
Copy link
Contributor

@tmurakami @keiji ありがとうございます。XamarinComponents 側 ENのメンテナのMatthew に、個人的に連絡とってみてます。

@keiji
Copy link
Collaborator Author

keiji commented Feb 26, 2021

adb shell am idle-maintenanceはいかがでしょうか?

お、いけました。実機だと画面をオフにしてからadb shell am idle-maintenanceでWorkerが起動しますね。ありがとうございます!

ちょっと気になったのは、通常使っていて、この状態(idle-maintenance)になるのはどのようなケースかなと。結構テストしたのですが一度もIDLE状態が確認できなかったりします。ぼくは基本的に「充電中はスリープしない設定」にしていますが、今回のテストではそれをオフにしています。

朝までにですか……IDLE移行まで時間がかかるとして、他のアプリが端末を起こしたら待ち直すことになったりすると、一般ユーザーでもなかなかIDLEまで行かない気がします。

あと、ExposureNotification.android.csは XamarinComponents の当該ソースを取り込んだものですので、直接修正してしまうと #22 で Xamarin.ExposureNotifications のバイナリを適用したタイミングで元に戻ってしまいます。

これは知りませんでした。リポジトリにファイルが含まれているファイルは上書きのような位置づけになるという理解でいたので……あとから生成できる系とか、ライブラリ(?)に含まれるソースコードは、可能な限りコード管理に含めない方がいい気がしますね。

EDIT:

ちょっと気になったのは、通常使っていて、この状態(idle-maintenance)になるのはどのようなケースかなと。

調べました。

idle-maintenance はDOZEモードのメンテナンスウィンドウの期間に設定するコマンドですね(JobSchedulerはDOZEは関係ないって言ってたのに…!)。

DOZEについては懐かしい話題の一つですが、あらためて。
充電しておらず完全静止の状態で1時間40分ほどでDOZE期間に突入。メンテナンスウィンドは1時間後なので、2時間40分後にコマンドで設定したメンテナンスウィンドウが到来。

https://qiita.com/komitake/items/4476e7e4d1b5ed3a8b97
https://programmer.group/source-code-analysis-of-android-doze-mode.html

センサーを使って端末の動きも見るとのことで、ぼくがテスト中に場所を動かしたり持ち歩いてたのを検知していた可能性がありますね。

調べた上で、DOZE中はrequiresDeviceIdleを有効にしていなくても、通常WorkManager(JobScheduler)は動かないという理解なので、やはりrequiresDeviceIdleはなくてもいいような気がします(この点 @kazumihirose さんの確認結果をお待ちしています)。

今回のPRは、requiresDeviceIdleの設定はバックグラウンドタスクの実行を鈍くするかもしれませんが、実行されない原因ではなさそうですね。他の部分(https://github.com/cocoa-mhlw/cocoa/issues/25#issuecomment-786668197)も調べてみようと思います。

@tmurakami
Copy link

@kazumihirose よろしくお願いします!
(FHIはデンマークではなくてノルウェーですよ〜)

@keiji

朝までにですか……

実際には午前2時前後に動いていることが多いです。

ご指摘の通り当該オプションはとても分かりづらいと思います。
これが外れると、Android 5 にも対応できるかも知れませんね。

I have confirmed that the worker task works better if the constraint `RequiresDeviceIdle` is not set to true.
@keiji keiji force-pushed the android/fix_backgroundtask_doesnt_work branch from ec01fd1 to 2a840de Compare February 28, 2021 14:05
@keiji
Copy link
Collaborator Author

keiji commented Feb 28, 2021

@tmurakami ConfigureBackgroundWorkRequestを使う方式に変更しました。ご指摘感謝します。

@kazumihirose メンテナのMatthewからの見解はいかがでしょうか。外部から制約を変更できる手段(ConfigureBackgroundWorkRequest)を提供しているので、おそらくデフォルトの設定を変更してはならないと言った方針はないと理解しています。もし何か情報が得られたなら、また教えていただけるとありがたいです。

@moonmile
Copy link
Contributor

moonmile commented Mar 3, 2021

追試用に @keiji が作った検証コードの Xamarin.Forms 版を置いておきます。

AndroidBackTaskTest/RequiresDeviceIdleTest at main · moonmile/AndroidBackTaskTest

@keiji
Copy link
Collaborator Author

keiji commented Mar 31, 2021

この変更については開発チームのInternal Repositoryに取り込まれたのでcloseします。

レビュアーのみなさま。ほんとうにありがとうございました。

@keiji keiji closed this Mar 31, 2021
@keiji keiji deleted the android/fix_backgroundtask_doesnt_work branch August 19, 2021 03:10
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants