56

JobScheduler Code Reading

Embed Size (px)

Citation preview

ごめんなさい 資料完成しませんでした。。。

https://github.com/operando/Notes/tree/master/Shibuya.apk_4

https://github.com/operando/JobScheduler-Code-Reading

資料内のリンクとか JobSchedulerの電波メモ

About Me

• Shinobu Okano (@operandoOS)

• Mercari, Inc.

• 今週で23歳になりました!ありがとうございます!

• 最近iPhone6s Plus買いました(笑)

まったりAndroid Framework Code Reading #2

メルカリでやります!きてね!

https://mandroidfcr.doorkeeper.jp/events/33925

Did you know Project Volta?

Project Volta

• 簡単に言ってバッテリー消費を削減するプロジェクト

• Android Lで行われたもの

• Battery Historian

• JobScheduler

• AlarmManagerが省電力化(4.4 KitKat)

Google I/O 2014 Introduction to Project Volta

https://www.youtube.com/watch?v=KzSKIpJepUw

定期処理って今までこんな感じ

• AlarmManager + IntentService

• BroadcastReceiver + Service

• Timer,ScheduledExecutorService

• 方法は様々...

条件付きで実行したいってなると...

• とりあえずIntentService動かす

• でも条件(Wi-Fiにつながってるとか)に合致してなくて処理を断念...

• とにかく動かしてみないとわからない...

AlarmManagerって大変だよね...

• 再起動/アプリのアップデートするとAlarmがクリアされる

• BroadcastCastでACTION_BOOT_COMPLETED拾ってAlarmセットしなおし

• COMPLETED拾って、Alarmセットしなおし

• cancelめんどくさい , 同じようなAlarm作るのだるい

• とにかく辛い… 俺は辛い...

ずっと生きてるService嫌だよね...

• 死んでほしいのにずっと生きてるService

• Killしてもすぐ蘇る

• そんなService開発者も作りたくない

• こいつはどうしようもない...

とにかく今まで辛かったよな!!

• その気持ちわかるぞ!

• 辛い!辛い!

俺はこんな辛い気持ちを愚痴りにきたわけではない!

救世主? JobScheduler

Did you know JobScheduler?

Do you use JobScheduler?

About JobScheduler

• Android Lから導入されたAPI

• 様々な条件のJobをスケジュールしてくれるAPI

• 使うことで消費電力を意識した実装ができる

• 開発者が頑張らなくていいAPI

About JobScheduler

• Android Framework Services

• マルチユーザ用にも設計されている(当たり前か)

• Schedulerであって、Alarmではない

• 特定の時間に実行!みたいな感じではない

JobSchedulerの使い方

• Android API21から追加されたJobSchedulerに慣れていこう

• goo.gl/OMBCrl

• Using the JobScheduler API on Android Lollipop

• code.tutsplus.com/tutorials/using-the-jobscheduler-api-on-android-lollipop--cms-23562

FrameworkではJobSchedulerは使われている• Auto Bakcup

• Download

• Dex Opt

Auto Backup - Android M

• ユーザの操作コストを極力かけずにアプリのユーザデータの自動バックアップを作成する

• 今までBackupの実装必要だったけどいらない

• 細かいことは割愛

Auto Backup - 条件

• バックアップは24時間ごとに行われる

• バックアップは充電中、WiFi接続、アイドル状態の3つの条件が満たされた時に行われる

• この条件(24時間,充電中,WiFi接続,アイドル状態)

を制御してるのがJobScheduler

Auto Backup - 条件

http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/backup/java/com/android/server/backup/

FullBackupJob.java

ココらへんにそれっぽい処理が書いてある

Inside JobScheduler

JobService#onStartJob

• MainThreadで実行される

• なので重い処理はAnother Threadを作って処理する

• jobFinishedしないとjobがはけないから気をつけて。新しいjobが実行できなくなる

JobService#onStopJob

• 戻り値でtrueを返せばjob作成時に、指定された再試行の基準に基づいてjobを再スケジューリング

• 戻り値でfalseを返せばjobは止まる

• onStopJobが呼ばれるのは、保留中・実行中のjobが何かにより(登録したjobの更新)cancelされた際に呼ばれるっぽい

• job実行中にcancelAllとかすれば呼ばれる

scheduleの流れ

JobScheduler.schedule(JobInfo)

• JobScheduler.schedule(JobInfo)

• プロセス間通信でJobSchedulerStub#scheduleを呼び出す

• Jobの登録ができればJobScheduler.RESULT

• FAILUREが返ってくる。

• なので、Jobの登録ができたかどうかしっかり見てあげよう

• http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/core/java/android/app/JobSchedulerImpl.java#schedule

JobSchedulerStub#schedule

• JobSchedulerStub#schedule

• enforceValidJobRequestやcanPersistJobsでJob登録ができるかどうかとチェック

• JobSchedulerService#scheduleを呼び出す

• http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#813

JobSchedulerService#schedule

• JobSchedulerService#schedule

• JobInfoとアプリのUIDでJobStatusを生成。Framework側ではJobInfoはJobStatusとして管理される

• cancelJob + cancelJobImplで同じJobが登録されていたらcancelする(Job ID + UIDが同じJobStatus)

• pendingとしてqueueに溜まってるJobもcancel。ActiveServices(実際に動いてるJob)を全部チェックして、同じJobがあればcancel処理を行う。

JobSchedulerService#stopTrackingJobs

• JobStoreにある同じJobをremove

• 各StateControllerから同じJobをremove(removeするにもJobStatus

が各StateControllerの条件にあっているかどうかをチェックしてる)

• 全JobStatusを管理するクラスとしてJobStoreが使われている

• http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#190

JobSchedulerService#startTrackingJob• 基本的にJobSchedulerService#stopTrackingJobと逆のことする

(JobStatusのadd)

• 各StateControllerにJobStatusをaddする(addするにもJobStatusが各StateControllerの条件にあっているかどうかをチェックしてる)

• mReadyToRock(JobSchedulerの準備??)がtrueになっていないと、基本的にはaddもremoveもできない

• http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#373

JobSchedulerService#maybeQueueReadyJobsForExecutionLockedH

• The state of at least one job has changed.(少なくとも一つのジョブの状態が変更された)

• ready(実行可能)になっているjobがいくつ存在するかで決めているっぽい

• Right now the policy is such:

• If >1 of the ready jobs is idle mode we send all of them off

• if more than 2 network connectivity jobs are ready we send them all off.

• If more than 4 jobs total are ready we send them all off.

• http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#622

Demo

Demo

• JobInfo.Builder#setRequiredNetworkType(JobInfo.NETWORK_ANY)のjobを1つずつ登録

• 1個目のjobを登録したら、jobはREADYのままで止まっている(Pendingにも入ってない)

• 2個目のjobを登録したら、jobがActiveになった

• 上の条件に合致したので、jobをpending queueに移動させて、実行したのだと推測

再起動後も動くJobが作れる

• JobInfo.Builder#setPersisted(true)でJobが再起動後も実行される

• 開発者が再起動後に自分でまたJobを登録する必要がない

• JobInfo.Builder#setExtras(PersistableBundle)で再起動後も値を引き継げる

• 素晴らしい!

あれ?再起動後も引き継げるってことは?• Jobの情報を再起動時のためにどこかに保持する必要がある

• ということは、ファイルとかに書き出さないとだよね?

• おぉ!これは面白そう!

ということでこいつ...どこかに保存してるぞ...• /data/system/job/jobs.xml

• JobStoreというクラスがそこら辺管理してる

• JobSchedulerにJob(Persisted = true)が

追加された or 削除された際に内容がSyncされる

ということでこいつ...どこかに保存してるぞ...

• http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobStore.java

• 書き込み処理は主にこれ

• http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobStore.java#WriteJobsMapToDiskRunnable

ココらへんにそれっぽい処理が書いてある

/data/system/job/jobs.xml

<?xml version='1.0' encoding='utf-8' standalone='yes' ?><job-info version="0"> <job jobid="0" package="com.os.operando.jobschedulersample" class="com.os.operando.jobschedulersample.services.MyJobService" uid="10054"> <constraints connectivity="true" /> <periodic period="1000" deadline="1446590637790" delay="1446590636790" /> <extras /> </job> <job jobid="20536" package="android" class="com.android.server.backup.FullBackupJob" uid="1000"> <constraints unmetered="true" idle="true" charging="true" /> <one-off /> <extras /> </job> <job jobid="1" package="com.android.providers.downloads" class="com.android.providers.downloads.DownloadIdleService" uid="10005"> <constraints idle="true" charging="true" /> <periodic period="86400000" deadline="1446675936870" delay="1446589536870" /> <extras /> </job> <job jobid="800" package="android" class="com.android.server.pm.BackgroundDexOptService" uid="1000"> <constraints idle="true" charging="true" /> <one-off delay="1446589526460" /> <extras /> </job></job-info>

PersistableBundleの中身

• JobにセットしたPersistableBundleの中身も書き込まれます

• xmlへの書き込み処理はここでやってる

• http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/core/java/android/os/PersistableBundle.java#restoreFromXml

• 書き込み処理はJobStore内から呼び出される

• http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobStore.java#608

PersistableBundleの中身

<job jobid="0" package="com.os.operando.jobschedulersample" class="com.os.operando.jobschedulersample.services.MyJobService" uid="10059"> <constraints connectivity="true" idle="true" charging="true" /> <periodic period="1000" deadline="1446648619790" delay="1446648618790" /> <extras> <int name="id" value="0" /> </extras></job>

PersistableBundleの書き込み

• PersistableBundleの情報を保存する処理は、保存するXMLだけ用意してあげれば、どんなものでも使える汎用的なものっぽい。

• PersistableBundle#restoreFromXmlがhideなので、サードパーティからは使えないけど・・・。

• http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/core/java/android/os/PersistableBundle.java#restoreFromXml

RAMサイズによって同時実行できるJobの数が変わる!?• System propertyのro.config.low_ram=true場合

• 同時実行できるJobの数は1つ

• System propertyのro.config.low_ram=faseの場合

• 同時実行できるJobの数は3つ

• ro.config.low_ramはAndroid4.4で導入された、メモリ搭載量が少ないターゲット向けの設定

• Android Wearとかはro.config.low_ram=trueかな?

RAMサイズによって同時実行できるJobの数が変わる!?• え、じゃ同時に3つのJobしかできないじゃん

• はい、そうです。

• すいません・・・。(別に俺が実装したわけじゃない

• 大きなJobが走ると他のJobが実行できない

• どんまい…

RAMサイズによって同時実行できるJobの数が変わる!?

• http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#77

• http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#352

• https://source.android.com/devices/tech/config/low-ram.html

ココらへんにそれっぽい処理が書いてある

Demo

StateControllerの種類

• JobManager

• 各条件の監視をして、Jobの状態をコントロールする

• Controllerの生成箇所

• http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#313

Debugging JobScheduler

• adb shell dumpsys jobscheduler

• JobSchedulerに登録されているJobをDumpする。めっちゃ使う

• adb logcat -s JobSchedulerService

• JobSchedulerServiceのログ。あんまり出ないけど...

• idle状態にするコマンド

• adb shell dumpsys battery unplug

• adb shell dumpsys deviceidle enable

• adb shell dumpsys deviceidle step

• adb shell dumpsys deviceidle force-idle

面白コメント

// Let's go!

http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#348

// GO GO GO!

http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#367

Android M

Doze and App Standby

http://developer.android.com/about/versions/marshmallow/android-6.0-changes.html#behavior-power

• JobSchedulerってココらへんの話とも絡むよね?

• 深掘りしたい!!

• 省電力頑張りたい!

• DrodiKaigiで話したい!(このテーマで??

Thanks!