- Shinobu Okano(@operandoOS)
- Mercari, Inc.
- 今週で23歳になりました!ありがとうございます!
- 最近iPhone6s Plus買いました(笑)
- まったりAndroid Framework Code Reading #2やります!
- メルカリでやります!きてね!
- https://mandroidfcr.doorkeeper.jp/events/33925
- 簡単に言ってバッテリー消費を削減するプロジェクト
- Android Lで行われたもの
- Battery Historian
- JobScheduler
- AlarmManagerが省電力化(4.4 KitKat)
- Android Lから導入されたAPI
- 様々な条件のJobをスケジュールしてくれるAPI
- 使うことで消費電力を意識した実装ができる
- 開発者が頑張らなくていいAPI
- JobSchedulerはAndroid Framework Services(System Service系)
- マルチユーザ用にも設計されている(当たり前か)
- Schedulerであって、Alarmではない
- 特定の時間に実行!みたいな感じではない
* バックアップは24時間ごとに行われる
- バックアップは充電中、WiFi接続、アイドル状態の3つの条件が満たされた時に行われる
- この条件(24時間,充電中,WiFi接続,アイドル状態)を制御してるのがJobScheduler
- http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/backup/java/com/android/server/backup/FullBackupJob.java
http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/backup/java/com/android/server/backup/FullBackupJob.java
public static void schedule(Context ctx, long minDelay) {
JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sIdleService)
.setRequiresDeviceIdle(true)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(true);
if (minDelay > 0) {
builder.setMinimumLatency(minDelay);
}
js.schedule(builder.build());
}- JobScheduler.schedule(JobInfo)
- プロセス間通信でJobSchedulerStub#scheduleを呼び出す
- Jobの登録ができればJobScheduler.RESULT_SUCCESSが返ってくる。登録に失敗した場合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
- 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
- JobInfoとアプリのUIDでJobStatusを生成。Framework側ではJobInfoはJobStatusとして管理される
- cancelJob + cancelJobImplで同じJobが登録されていたらcancelする(Job ID + UIDが同じJobStatus)
- pendingとしてqueueに溜まってるJobもcancel。ActiveServices(実際に動いてるJob)を全部チェックして、同じJobがあればcancel処理を行う。
- 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#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
- 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
- JobInfo.Builder#setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)のjobを1つずつ登録
- 1個目のjobを登録したら、jobはREADYのままで止まっている(Pendingにも入ってない)
- 2個目のjobを登録したら、jobがActiveになった
- 上の条件に合致したので、jobをpending queueに移動させて、実行したのだと推測
- JobInfo.Builder#setPersisted(true)でJobが再起動後も実行される
- 開発者が再起動後に自分でまたJobを登録する必要がない
- JobInfo.Builder#setExtras(PersistableBundle)で再起動後も値を引き継げる
- 素晴らしい!
PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putInt("id", i);
JobInfo jobInfo = new JobInfo.Builder(i, serviceName)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.setExtras(persistableBundle)
.build();
scheduler.schedule(jobInfo);- Jobの情報を再起動時のためにどこかに保持する必要がある
- ということは、ファイルとかに書き出さないとだよね?
- おぉ!これは面白そう!
- /data/system/job/jobs.xml
- JobStoreというクラスがそこら辺管理してる
- http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobStore.java
- JobSchedulerに新しいJob(Persisted = true)が追加された / Job(Persisted = true)が削除された際に、内容がSyncされる。
- 書き込み処理は主にこれ
- http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobStore.java#WriteJobsMapToDiskRunnable
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<job-info version="0">
<job jobid="808" package="android" class="com.android.server.MountServiceIdler" uid="1000">
<constraints idle="true" charging="true" />
<one-off delay="1446660001930" />
<extras />
</job>
<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="20537" package="android" class="com.android.server.backup.KeyValueBackupJob" uid="1000">
<constraints connectivity="true" charging="true" />
<one-off deadline="1446675931860" delay="1446604253797" />
<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>- 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
<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の情報を保存する処理は、保存するXMLだけ用意してあげれば、どんなものでも使える汎用的なものっぽい。
- PersistableBundle#restoreFromXmlがhideなので、サードパーティからは使えないけど・・・。
- http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/core/java/android/os/PersistableBundle.java#restoreFromXml
- System propertyのro.config.low_ram=trueの場合、同時実行できるJobの数は1つ
- System propertyのro.config.low_ram=faseの場合、同時実行できるJobの数は3つ
- ココらへん見ると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
- ro.config.low_ramはAndroid4.4で導入された、メモリ搭載量が少ないターゲット向けの設定
- Android Wearとかはro.config.low_ram=trueかな?
http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#77
/** The number of concurrent jobs we run at one time. */
private static final int MAX_JOB_CONTEXTS_COUNT
= ActivityManager.isLowRamDeviceStatic() ? 1 : 3;http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/core/java/android/app/ActivityManager.java#isLowRamDeviceStatic
// ActivityManager.java
public static boolean isLowRamDeviceStatic() {
return "true".equals(SystemProperties.get("ro.config.low_ram", "false"));
}- JobManager
- 各条件の監視をして、Jobの状態をコントロールする
- StateController (抽象クラス)
- AppIdleController
- BatteryController
- ConnectivityController
- IdleController
- JobStatus
- TimeController
- Controllerの生成箇所
- http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#313
- 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
-
/frameworks/base/services/core/java/com/android/server/job/controllers
-
Auto BackのJobSchedular