├── README.md ├── README_Android_M_6.0.md └── job.xml /README.md: -------------------------------------------------------------------------------- 1 | # JobScheduler Code Reading 2 | 3 | **Android N 7.1.1_r1.0のソースコードをベースに調べたもの** 4 | 5 | http://tools.oesf.biz/android-7.1.1_r1.0/ 6 | 7 | # Project Volta 8 | 9 | * 簡単に言ってバッテリー消費を削減するプロジェクト 10 | * Android Lで行われたもの 11 | * [Battery Historian](https://github.com/google/battery-historian) 12 | * [JobScheduler](http://developer.android.com/about/versions/android-5.0.html#Power) 13 | * [AlarmManagerが省電力化(4.4 KitKat)](http://developer.android.com/about/versions/android-4.4.html#BehaviorAlarms) 14 | 15 | 16 | # About JobScheduler 17 | 18 | * Android Lから導入されたAPI 19 | * 様々な条件のJobをスケジュールしてくれるAPI 20 | * 使うことで消費電力を意識した実装ができる 21 | * 開発者が頑張らなくていいAPI 22 | * JobSchedulerはAndroid Framework Services(System Service系) 23 | * マルチユーザ用にも設計されている(当たり前か) 24 | * Schedulerであって、Alarmではない 25 | * 特定の時間に実行!みたいな感じではない 26 | 27 | 28 | # JobSchedulerの使い方 29 | 30 | * [Android API21から追加されたJobSchedulerに
慣れていこう](http://blog.techfirm.co.jp/2015/10/19/android-api21%E3%81%8B%E3%82%89%E8%BF%BD%E5%8A%A0%E3%81%95%E3%82%8C%E3%81%9Fjobscheduler%E3%81%AB%E6%85%A3%E3%82%8C%E3%81%A6%E3%81%84%E3%81%93%E3%81%86/) 31 | * [Using the JobScheduler API on Android Lollipop](http://code.tutsplus.com/tutorials/using-the-jobscheduler-api-on-android-lollipop--cms-23562) 32 | 33 | 34 | # JobScheduler Sample 35 | 36 | * [googlesamples/android-JobScheduler](https://github.com/googlesamples/android-JobScheduler) 37 | * [operando/JobScheduler-Sample](https://github.com/operando/JobScheduler-Sample) 38 | * 実験用 39 | 40 | 41 | ## Android Mの実装と比較してみて 42 | 43 | * 色々変わってそう 44 | * Mの時より複雑な実装になってる印象... 45 | 46 | 47 | ## Android Mの時と比べて同時実行できるJobの数に変更はあったのか 48 | 49 | * なんか変わったっぽい 50 | * Android Mの時のまとめは以下 51 | * https://github.com/operando/JobScheduler-Code-Reading#ramサイズによって同時実行できるjobの数が変わる 52 | * 最大で16こ Jobが同時に実行できるようになってるのかな? 53 | 54 | http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#109 55 | 56 | ```java 57 | /** The maximum number of concurrent jobs we run at one time. */ 58 | private static final int MAX_JOB_CONTEXTS_COUNT = 16; 59 | ``` 60 | 61 | 62 | ## なんかSettingの値を取り出してJobSchedulerの設定値みたいなの作ってるところ 63 | 64 | * これややこしいなー 65 | * なんかSettingを値を読み込んで処理するところがあるんだけどー 66 | * 読み込んでる値が`ALARM_MANAGER_CONSTANTS = "alarm_manager_constants"` ってやつなんだよね 67 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#302 68 | * `ALARM_MANAGER_CONSTANTS` 69 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/core/java/android/provider/Settings.java#ALARM_MANAGER_CONSTANTS 70 | * JobSchedulerService#startメソッドでContentResolverを作ってる 71 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#287 72 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/core/java/android/provider/Settings.java#8056 73 | * Settings.Global.getStringの処理 74 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/core/java/android/provider/Settings.java#8650 75 | 76 | ## JobServiceContextに残された謎なコード 77 | 78 | * Android Mのことのコードと同じだ 79 | * でもこの定数どこからもアクセスされてない 80 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobServiceContext.java#68 81 | 82 | ```java 83 | 68 /** Define the maximum # of jobs allowed to run on a service at once. */ 84 | 69 private static final int defaultMaxActiveJobsPerService = 85 | 70 ActivityManager.isLowRamDeviceStatic() ? 1 : 3; 86 | ``` 87 | 88 | ## BatteryController 89 | 90 | http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/BatteryController.java 91 | 92 | ### ChargingTracker 93 | 94 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/BatteryController.java122 95 | * BroadcastReceiverを継承してる 96 | * chargingのステータスをトラッキングしてるやーつー 97 | 98 | * Battery系に関するIntentFilterを設定してる 99 | * LocalServices.getService(BatteryManagerInternal.class)取得できるのはこれ? 100 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/BatteryService.java#883 101 | 102 | http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/BatteryController.java#134 103 | 104 | ```java 105 | 134 public void startTracking() { 106 | 135 IntentFilter filter = new IntentFilter(); 107 | 136 108 | 137 // Battery health. 109 | 138 filter.addAction(Intent.ACTION_BATTERY_LOW); 110 | 139 filter.addAction(Intent.ACTION_BATTERY_OKAY); 111 | 140 // Charging/not charging. 112 | 141 filter.addAction(BatteryManager.ACTION_CHARGING); 113 | 142 filter.addAction(BatteryManager.ACTION_DISCHARGING); 114 | 143 mContext.registerReceiver(this, filter); 115 | 144 116 | 145 // Initialise tracker state. 117 | 146 BatteryManagerInternal batteryManagerInternal = 118 | 147 LocalServices.getService(BatteryManagerInternal.class); 119 | 148 mBatteryHealthy = !batteryManagerInternal.getBatteryLevelLow(); 120 | 149 mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); 121 | 150 } 122 | ``` 123 | 124 | http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/BatteryController.java#156 125 | 126 | 127 | ```java 128 | // onReceiveの処理 129 | 156 @Override 130 | 157 public void onReceive(Context context, Intent intent) { 131 | 158 onReceiveInternal(intent); 132 | 159 } 133 | 160 134 | 161 @VisibleForTesting 135 | 162 public void onReceiveInternal(Intent intent) { 136 | 163 final String action = intent.getAction(); 137 | 164 if (Intent.ACTION_BATTERY_LOW.equals(action)) { 138 | 165 if (DEBUG) { 139 | 166 Slog.d(TAG, "Battery life too low to do work. @ " 140 | 167 + SystemClock.elapsedRealtime()); 141 | 168 } 142 | 169 // If we get this action, the battery is discharging => it isn't plugged in so 143 | 170 // there's no work to cancel. We track this variable for the case where it is 144 | 171 // charging, but hasn't been for long enough to be healthy. 145 | 172 mBatteryHealthy = false; 146 | 173 } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) { 147 | 174 if (DEBUG) { 148 | 175 Slog.d(TAG, "Battery life healthy enough to do work. @ " 149 | 176 + SystemClock.elapsedRealtime()); 150 | 177 } 151 | 178 mBatteryHealthy = true; 152 | 179 maybeReportNewChargingState(); 153 | 180 } else if (BatteryManager.ACTION_CHARGING.equals(action)) { 154 | 181 if (DEBUG) { 155 | 182 Slog.d(TAG, "Received charging intent, fired @ " 156 | 183 + SystemClock.elapsedRealtime()); 157 | 184 } 158 | 185 mCharging = true; 159 | 186 maybeReportNewChargingState(); 160 | 187 } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { 161 | 188 if (DEBUG) { 162 | 189 Slog.d(TAG, "Disconnected from power."); 163 | 190 } 164 | 191 mCharging = false; 165 | 192 maybeReportNewChargingState(); 166 | 193 } 167 | 194 } 168 | 195 } 169 | ``` 170 | 171 | 172 | * jobの実装とかしてる 173 | 174 | http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/BatteryController.java#97 175 | 176 | ```java 177 | 97 private void maybeReportNewChargingState() { 178 | 98 final boolean stablePower = mChargeTracker.isOnStablePower(); 179 | 99 if (DEBUG) { 180 | 100 Slog.d(TAG, "maybeReportNewChargingState: " + stablePower); 181 | 101 } 182 | 102 boolean reportChange = false; 183 | 103 synchronized (mLock) { 184 | 104 for (JobStatus ts : mTrackedTasks) { 185 | 105 boolean previous = ts.setChargingConstraintSatisfied(stablePower); 186 | 106 if (previous != stablePower) { 187 | 107 reportChange = true; 188 | 108 } 189 | 109 } 190 | 110 } 191 | 111 // Let the scheduler know that state has changed. This may or may not result in an 192 | 112 // execution. 193 | 113 if (reportChange) { 194 | 114 mStateChangedListener.onControllerStateChanged(); 195 | 115 } 196 | 116 // Also tell the scheduler that any ready jobs should be flushed. 197 | 117 if (stablePower) { 198 | 118 mStateChangedListener.onRunJobNow(null); 199 | 119 } 200 | 120 } 201 | ``` 202 | 203 | ## TODO: mMaxActiveJobsの謎をとく 204 | 205 | * 動的に変わる 206 | * adb shell dumpsys jobschedulerすると今どのくらいなのかわかる 207 | 208 | ```java 209 | 161 /** 210 | 162 * Current limit on the number of concurrent JobServiceContext entries we want to 211 | 163 * keep actively running a job. 212 | 164 */ 213 | 165 int mMaxActiveJobs = 1; 214 | ``` 215 | 216 | 217 | ## adb shell dumpsys jobscheduler用のコマンドがAndroid Nから増えてる 218 | 219 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerShellCommand.java 220 | * adb shell dumpsys jobscheduler -h 221 | 222 | ## Debugging JobScheduler 223 | 224 | * adb shell dumpsys jobscheduler 225 | * JobSchedulerに登録されているJobをDumpする 226 | * めっちゃ使う 227 | * 実際に処理してるクラスは以下 228 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerShellCommand.java 229 | * adb logcat -s JobSchedulerService 230 | * JobSchedulerServiceのログ。あんまり出ないけど... 231 | * idle状態にするコマンド 232 | * adb shell dumpsys battery unplug 233 | * adb shell dumpsys deviceidle enable 234 | * adb shell dumpsys deviceidle step 235 | * adb shell dumpsys deviceidle force-idle 236 | 237 | 238 | # StateControllerの種類 239 | 240 | * JobManager 241 | * 各条件の監視をして、Jobの状態をコントロールする 242 | * [StateController](http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/StateController.java) (抽象クラス) 243 | * [AppIdleController](http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/AppIdleController.java) 244 | * [BatteryController](http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/BatteryController.java) 245 | * [ConnectivityController](http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/ConnectivityController.java) 246 | * [IdleController](http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/IdleController.java) 247 | * [JobStatus](http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/JobStatus.java) 248 | * [TimeController](http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/TimeController.java) 249 | 250 | 251 | ## Let's go!とGO GO GO!は健在 252 | 253 | Android Mのコードにもあった面白コメント 254 | 255 | ### Let's go! 256 | 257 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#834 258 | 259 | 260 | ### GO GO GO! 261 | 262 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#856 263 | 264 | 265 | ## adb shell dumpsys jobschedulerのメモ 266 | 267 | * Job historyが見れるようになってる 268 | * 過去に実行したものかな? 269 | * dump処理はここでやってる 270 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#1807 271 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobPackageTracker.java 272 | * 色んなところで 273 | 274 | ```java 275 | Job history: 276 | -1h09m18s553ms START: u0a38 com.android.calendar/com.google/hogehogehogehogehoge@gmail.com:android 277 | -1h09m18s537ms STOP: u0a38 com.android.calendar/com.google/hogehogehogehogehoge@gmail.com:android 278 | -1h09m18s530ms STOP: u0a38 com.android.calendar/com.google/hogehogehogehogehoge@gmail.com:android 279 | -1h09m17s480ms STOP: u0a38 com.android.calendar/com.google/hogehogehogehogehoge@gmail.com:android 280 | -1h09m17s464ms START: u0a38 com.android.calendar/com.google/hogehogehogehogehoge@gmail.com:android 281 | -1h09m17s458ms START: u0a38 com.android.calendar/com.google/hogehogehogehogehoge@gmail.com:android 282 | -1h09m17s437ms STOP: u0a38 com.android.calendar/com.google/hogehogehogehogehoge@gmail.com:android 283 | -1h09m16s660ms STOP: u0a38 com.android.calendar/com.google/hogehogehogehogehoge@gmail.com:android 284 | -1h09m16s640ms START: u0a38 com.android.calendar/com.google/hogehogehogehogehoge@gmail.com:android 285 | -1h09m15s896ms STOP: u0a38 com.android.calendar/com.google/hogehogehogehogehoge@gmail.com:android 286 | -1h08m36s297ms START: u0a63 com.google.android.apps.photos/com.google.android.libraries.social.mediamonitor.MediaMonitorJobSchedulerService 287 | -1h08m36s293ms START: u0a63 com.google.android.apps.photos/.camerashortcut.CameraShortcutJobSchedulerService 288 | -1h08m36s068ms STOP: u0a63 com.google.android.apps.photos/.camerashortcut.CameraShortcutJobSchedulerService 289 | -1h08m36s065ms STOP: u0a63 com.google.android.apps.photos/com.google.android.libraries.social.mediamonitor.MediaMonitorJobSchedulerService 290 | -1h08m30s410ms START: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 291 | -1h08m30s245ms STOP: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 292 | -1h08m19s747ms START: u0a24 DownloadManager:com.android.providers.downloads 293 | -1h08m18s751ms STOP: u0a24 DownloadManager:com.android.providers.downloads 294 | -1h07m14s726ms START: u0a13 com.google.android.gms.fitness/com.google/hogehogehogehogehoge@gmail.com:android 295 | -1h07m14s579ms STOP: u0a13 com.google.android.gms.fitness/com.google/hogehogehogehogehoge@gmail.com:android 296 | -1h07m14s427ms START: u0a24 DownloadManager:com.android.providers.downloads 297 | -1h07m14s076ms START: u0a84 com.google.android.apps.plus.content.EsProvider/com.google/hogehogehogehogehoge@gmail.com:android 298 | -1h07m12s364ms STOP: u0a24 DownloadManager:com.android.providers.downloads 299 | -1h07m10s852ms STOP: u0a84 com.google.android.apps.plus.content.EsProvider/com.google/hogehogehogehogehoge@gmail.com:android 300 | -1h04m06s448ms START: u0a63 com.google.android.apps.photos/com.google.android.libraries.social.mediamonitor.MediaMonitorJobSchedulerService 301 | -1h04m06s446ms START: u0a63 com.google.android.apps.photos/.camerashortcut.CameraShortcutJobSchedulerService 302 | -1h04m06s208ms STOP: u0a63 com.google.android.apps.photos/.camerashortcut.CameraShortcutJobSchedulerService 303 | -1h04m06s206ms STOP: u0a63 com.google.android.apps.photos/com.google.android.libraries.social.mediamonitor.MediaMonitorJobSchedulerService 304 | -1h04m00s221ms START: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 305 | -1h03m59s711ms STOP: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 306 | -1h03m59s051ms START: u0a24 DownloadManager:com.android.providers.downloads 307 | -1h03m06s618ms STOP: u0a24 DownloadManager:com.android.providers.downloads 308 | -1h01m51s953ms START: u0a24 DownloadManager:com.android.providers.downloads 309 | -1h01m50s347ms STOP: u0a24 DownloadManager:com.android.providers.downloads 310 | -1h00m44s706ms START: u0a69 gmail-ls/com.google/hogehogehogehogehoge@gmail.com:android 311 | -1h00m36s208ms STOP: u0a69 gmail-ls/com.google/hogehogehogehogehoge@gmail.com:android 312 | -1h00m33s197ms START: u0a5 DownloadManager:com.android.providers.downloads 313 | -1h00m33s031ms STOP: u0a5 DownloadManager:com.android.providers.downloads 314 | -1h00m10s056ms START: u0a24 DownloadManager:com.android.providers.downloads 315 | -1h00m08s859ms STOP: u0a24 DownloadManager:com.android.providers.downloads 316 | -59m12s824ms START: u0a24 DownloadManager:com.android.providers.downloads 317 | -59m11s876ms STOP: u0a24 DownloadManager:com.android.providers.downloads 318 | -58m27s773ms START: u0a24 DownloadManager:com.android.providers.downloads 319 | -58m26s496ms STOP: u0a24 DownloadManager:com.android.providers.downloads 320 | -57m08s811ms START: u0a24 DownloadManager:com.android.providers.downloads 321 | -57m08s044ms STOP: u0a24 DownloadManager:com.android.providers.downloads 322 | -56m18s764ms START: u0a24 DownloadManager:com.android.providers.downloads 323 | -56m14s989ms STOP: u0a24 DownloadManager:com.android.providers.downloads 324 | -54m47s067ms START: u0a63 com.google.android.apps.photos/.camerashortcut.CameraShortcutJobSchedulerService 325 | -54m47s064ms START: u0a63 com.google.android.apps.photos/com.google.android.libraries.social.mediamonitor.MediaMonitorJobSchedulerService 326 | -54m46s918ms STOP: u0a63 com.google.android.apps.photos/.camerashortcut.CameraShortcutJobSchedulerService 327 | -54m46s780ms STOP: u0a63 com.google.android.apps.photos/com.google.android.libraries.social.mediamonitor.MediaMonitorJobSchedulerService 328 | -54m40s155ms START: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 329 | -54m39s279ms STOP: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 330 | -54m30s790ms START: u0a24 DownloadManager:com.android.providers.downloads 331 | -54m28s403ms STOP: u0a24 DownloadManager:com.android.providers.downloads 332 | -53m59s214ms START: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 333 | -53m58s569ms STOP: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 334 | -53m02s793ms START: u0a24 DownloadManager:com.android.providers.downloads 335 | -53m01s628ms STOP: u0a24 DownloadManager:com.android.providers.downloads 336 | -52m30s913ms START: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 337 | -52m26s506ms STOP: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 338 | -51m38s043ms START: u0a24 DownloadManager:com.android.providers.downloads 339 | -51m35s364ms STOP: u0a24 DownloadManager:com.android.providers.downloads 340 | -50m12s114ms START: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 341 | -50m11s764ms STOP: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 342 | -47m54s434ms START: u0a63 com.google.android.apps.photos/com.google.android.libraries.social.mediamonitor.MediaMonitorJobSchedulerService 343 | -47m54s422ms START: u0a63 com.google.android.apps.photos/.camerashortcut.CameraShortcutJobSchedulerService 344 | -47m54s339ms STOP: u0a63 com.google.android.apps.photos/.camerashortcut.CameraShortcutJobSchedulerService 345 | -47m54s337ms STOP: u0a63 com.google.android.apps.photos/com.google.android.libraries.social.mediamonitor.MediaMonitorJobSchedulerService 346 | -47m30s004ms START: u0a24 DownloadManager:com.android.providers.downloads 347 | -47m29s191ms STOP: u0a24 DownloadManager:com.android.providers.downloads 348 | -45m52s917ms START: u0a24 DownloadManager:com.android.providers.downloads 349 | -45m49s560ms STOP: u0a24 DownloadManager:com.android.providers.downloads 350 | -45m19s885ms START: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 351 | -45m19s430ms STOP: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 352 | -45m04s907ms START: u0a24 DownloadManager:com.android.providers.downloads 353 | -45m03s907ms STOP: u0a24 DownloadManager:com.android.providers.downloads 354 | -44m05s837ms START: u0a24 DownloadManager:com.android.providers.downloads 355 | -44m05s072ms STOP: u0a24 DownloadManager:com.android.providers.downloads 356 | -43m49s010ms START: u0a13 sync:android 357 | -43m48s990ms STOP: u0a13 sync:android 358 | -35m45s066ms START: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 359 | -35m44s858ms STOP: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 360 | -33m57s905ms START: u0a71 tv.abema/com.evernote.android.job.v21.PlatformJobService 361 | -33m54s103ms STOP: u0a71 tv.abema/com.evernote.android.job.v21.PlatformJobService 362 | -19m06s748ms START: u0a78 com.facebook.orca/com.facebook.analytics2.logger.LollipopUploadService 363 | -19m06s722ms START: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 364 | -19m05s954ms STOP: u0a78 com.facebook.orca/com.facebook.analytics2.logger.LollipopUploadService 365 | -19m05s932ms STOP: u0a63 com.google.android.apps.photos/.dbprocessor.impl.DatabaseProcessorJobService 366 | -7m14s698ms START: u0a13 com.google.android.gms.fitness/com.google/hogehogehogehogehoge@gmail.com:android 367 | -7m14s516ms STOP: u0a13 com.google.android.gms.fitness/com.google/hogehogehogehogehoge@gmail.com:android 368 | -7m13s958ms START: u0a84 com.google.android.apps.plus.content.EsProvider/com.google/hogehogehogehogehoge@gmail.com:android 369 | -7m12s930ms STOP: u0a84 com.google.android.apps.plus.content.EsProvider/com.google/hogehogehogehogehoge@gmail.com:android 370 | -2m45s701ms START: u0a71 tv.abema/com.evernote.android.job.v21.PlatformJobService 371 | -2m45s680ms START: u0a145 com.google.android.apps.docs.editors.kix/com.google/hogehogehogehogehoge@gmail.com:android 372 | -2m45s438ms STOP: u0a145 com.google.android.apps.docs.editors.kix/com.google/hogehogehogehogehoge@gmail.com:android 373 | -2m45s397ms START: u0a145 com.google.android.apps.docs.editors.kix/com.google/hogehogehogehogehoge@gmail.com:android 374 | -2m45s342ms STOP: u0a145 com.google.android.apps.docs.editors.kix/com.google/hogehogehogehogehoge@gmail.com:android 375 | -2m43s883ms STOP: u0a71 tv.abema/com.evernote.android.job.v21.PlatformJobService 376 | ``` 377 | 378 | ## 雑メモ 379 | 380 | * なんかまた内部向け独自パーサー見つけた 381 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/core/java/android/util/KeyValueListParser.java 382 | * Settingの値とかをパースするのに使われてるっぽい。以下とか。 383 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/core/java/android/provider/Settings.java#ALARM_MANAGER_CONSTANTS 384 | 385 | 386 | * http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java# 387 | -------------------------------------------------------------------------------- /README_Android_M_6.0.md: -------------------------------------------------------------------------------- 1 | # JobScheduler Code Reading 2 | 3 | * **Android M 6.0.0_r1.0のソースコードをベースに調べたもの** 4 | 5 | 6 | # Project Volta 7 | 8 | * 簡単に言ってバッテリー消費を削減するプロジェクト 9 | * Android Lで行われたもの 10 | * [Battery Historian](https://github.com/google/battery-historian) 11 | * [JobScheduler](http://developer.android.com/about/versions/android-5.0.html#Power) 12 | * [AlarmManagerが省電力化(4.4 KitKat)](http://developer.android.com/about/versions/android-4.4.html#BehaviorAlarms) 13 | 14 | 15 | # About JobScheduler 16 | 17 | * Android Lから導入されたAPI 18 | * 様々な条件のJobをスケジュールしてくれるAPI 19 | * 使うことで消費電力を意識した実装ができる 20 | * 開発者が頑張らなくていいAPI 21 | * JobSchedulerはAndroid Framework Services(System Service系) 22 | * マルチユーザ用にも設計されている(当たり前か) 23 | * Schedulerであって、Alarmではない 24 | * 特定の時間に実行!みたいな感じではない 25 | 26 | 27 | # JobSchedulerの使い方 28 | 29 | * [Android API21から追加されたJobSchedulerに
慣れていこう](http://blog.techfirm.co.jp/2015/10/19/android-api21%E3%81%8B%E3%82%89%E8%BF%BD%E5%8A%A0%E3%81%95%E3%82%8C%E3%81%9Fjobscheduler%E3%81%AB%E6%85%A3%E3%82%8C%E3%81%A6%E3%81%84%E3%81%93%E3%81%86/) 30 | * [Using the JobScheduler API on Android Lollipop](http://code.tutsplus.com/tutorials/using-the-jobscheduler-api-on-android-lollipop--cms-23562) 31 | 32 | 33 | # JobScheduler Sample 34 | 35 | * [googlesamples/android-JobScheduler](https://github.com/googlesamples/android-JobScheduler) 36 | * [operando/JobScheduler-Sample](https://github.com/operando/JobScheduler-Sample) 37 | * 実験用 38 | 39 | 40 | ## Android MのAuto BackupのJobSchedulerを使ってる 41 | 42 | * Auto Backupがされる条件 43 | * バックアップは24時間ごとに行われる 44 | * バックアップは充電中、WiFi接続、アイドル状態の3つの条件が満たされた時に行われる 45 | * この条件(24時間,充電中,WiFi接続,アイドル状態)を制御してるのがJobScheduler 46 | * http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/backup/java/com/android/server/backup/FullBackupJob.java 47 | * ↓参考程度 48 | * http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/backup/java/com/android/server/backup/KeyValueBackupJob.java#60 49 | * http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/backup/java/com/android/server/backup/ 50 | 51 | 52 | ##### http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/backup/java/com/android/server/backup/FullBackupJob.java 53 | 54 | ```java 55 | public static void schedule(Context ctx, long minDelay) { 56 | JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE); 57 | JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sIdleService) 58 | .setRequiresDeviceIdle(true) 59 | .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) 60 | .setRequiresCharging(true); 61 | if (minDelay > 0) { 62 | builder.setMinimumLatency(minDelay); 63 | } 64 | js.schedule(builder.build()); 65 | } 66 | ``` 67 | 68 | 69 | ## JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);で取得できるJobSchedulerの実態 70 | 71 | * [JobSchedulerImpl](http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/core/java/android/app/JobSchedulerImpl.java) 72 | 73 | 74 | ## RAMサイズによって同時実行できるJobの数が変わる 75 | 76 | * System propertyのro.config.low_ram=trueの場合、**同時実行できるJobの数は1つ** 77 | * System propertyのro.config.low_ram=faseの場合、**同時実行できるJobの数は3つ** 78 | * ココらへん見るとJobの数についてわかる 79 | * http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#77 80 | * ro.config.low_ramはAndroid4.4で導入された、メモリ搭載量が少ないターゲット向けの設定 81 | * https://source.android.com/devices/tech/config/low-ram.html 82 | * Android Wearとかはro.config.low_ram=trueかな? 83 | * JobServiceContextの数が1 or 3ってこと。JobServiceContextで実際にJobを走らせる 84 | * 自分でbuildする時に、JobServiceContextを生成する数を増やせば同時実行できるJob数は増える 85 | * 機種依存とかでいじってるのなくはないかも... 86 | 87 | ###### http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#77 88 | 89 | ```java 90 | /** The number of concurrent jobs we run at one time. */ 91 | private static final int MAX_JOB_CONTEXTS_COUNT 92 | = ActivityManager.isLowRamDeviceStatic() ? 1 : 3; 93 | ``` 94 | 95 | ##### http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/core/java/android/app/ActivityManager.java#isLowRamDeviceStatic 96 | 97 | ```java 98 | // ActivityManager.java 99 | public static boolean isLowRamDeviceStatic() { 100 | return "true".equals(SystemProperties.get("ro.config.low_ram", "false")); 101 | } 102 | ``` 103 | 104 | ##### http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#352 105 | 106 | ```java 107 | // Create the "runners". 108 | for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { 109 | mActiveServices.add( 110 | new JobServiceContext(this, mBatteryStats, 111 | getContext().getMainLooper())); 112 | } 113 | ``` 114 | 115 | 116 | ## デバイス再起動後も動くJobが作れる 117 | 118 | * JobInfo.Builder#setPersisted(true)でJobが再起動後も実行される 119 | * 開発者が再起動後に自分でまたJobを登録する必要がない 120 | * JobInfo.Builder#setExtras(PersistableBundle)で再起動後も値を引き継げる 121 | 122 | #### サンプル(こんな感じ) 123 | 124 | ```java 125 | PersistableBundle persistableBundle = new PersistableBundle(); 126 | persistableBundle.putInt("id", i); 127 | JobInfo jobInfo = new JobInfo.Builder(i, serviceName) 128 | .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 129 | .setPersisted(true) 130 | .setExtras(persistableBundle) 131 | .build(); 132 | scheduler.schedule(jobInfo); 133 | ``` 134 | 135 | 136 | ## 再起動後も引き継げるってことはJobの情報をどこかに保存してるぞ... 137 | 138 | * ここ → **/data/system/job/jobs.xml** 139 | * 見るためには要Root 140 | * [JobStore](http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobStore.java)でそこら辺管理してる 141 | * http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobStore.java 142 | * JobSchedulerに新しいJob(Persisted = trueのJob)が追加された または Jobが削除された際に、ファイルの内容がSyncされる 143 | * JobStore#add / JobStore#remove -> JobStore#WriteJobsMapToDiskRunnable -> WriteJobsMapToDiskRunnable 144 | * 書き込み処理は主にこれ 145 | * http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobStore.java#WriteJobsMapToDiskRunnable 146 | 147 | ## [/data/system/job/jobs.xmlの中身(サンプル)](./job.xml) 148 | 149 | ```xml 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | value0 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | ``` 182 | 183 | ##### http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobStore.java#246 184 | 185 | ```java 186 | /** 187 | * Every time the state changes we write all the jobs in one swath, instead of trying to 188 | * track incremental changes. 189 | * @return Whether the operation was successful. This will only fail for e.g. if the system is 190 | * low on storage. If this happens, we continue as normal 191 | */ 192 | private void maybeWriteStatusToDiskAsync() { 193 | mDirtyOperations++; 194 | if (mDirtyOperations >= MAX_OPS_BEFORE_WRITE) { 195 | if (DEBUG) { 196 | Slog.v(TAG, "Writing jobs to disk."); 197 | } 198 | mIoHandler.post(new WriteJobsMapToDiskRunnable()); 199 | } 200 | } 201 | ``` 202 | 203 | ## JobInfo.Builder#setExtrasでセットしたPersistableBundle 204 | 205 | * PersistableBundleの内容は/data/system/job/jobs.xmlに書き込まれている 206 | 207 | ```xml 208 | 209 | 210 | 211 | 212 | 213 | value0 214 | 215 | 216 | ``` 217 | 218 | * xmlへの書き込み処理はここでやってる 219 | * http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/core/java/android/os/PersistableBundle.java#restoreFromXml 220 | * 書き込み処理はJobStoreないから呼び出される 221 | * http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobStore.java#608 222 | * PersistableBundleの情報を保存する処理は、保存するXMLだけ用意してあげれば、どんなものでも使える汎用的なものっぽい。 223 | * restoreFromXmlがhideなので、サードパーティからは使えないけど・・・。 224 | 225 | 226 | ## 端末起動時のJobScheduler実行 227 | 228 | * onBootPhaseのphaseがPHASE_THIRD_PARTY_APPS_CAN_STARTだったらJobを実行 229 | * 名前通り端末起動段階で、サードパーティアプリのJobが実行可能になったってこと 230 | * ここでmReadyToRockがtrueになる 231 | * mReadyToRockがfalseだとJobの追加とかができない 232 | * 同時実行できるJobの数分だけmActiveServices(List)にJobServiceContextをadd 233 | * JobStore(mJobs)からJob(JobStatus)を取り出して、各StateControllerに通知する 234 | * これは再起動前に/data/system/jobs/job.xmlに書き込まれた情報から取り出す 235 | * これが再起動後も実行可能はJob。JobInfo.Builder#setPersisted(true)したJob 236 | * 後はmHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();してJobが実行できるかチェック 237 | * GO GO GO! 238 | 239 | 240 | ##### http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#346 241 | 242 | ```java 243 | @Override 244 | public void onBootPhase(int phase) { 245 | if (PHASE_SYSTEM_SERVICES_READY == phase) { 246 | // Register br for package removals and user removals. 247 | final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 248 | filter.addDataScheme("package"); 249 | getContext().registerReceiverAsUser( 250 | mBroadcastReceiver, UserHandle.ALL, filter, null, null); 251 | final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); 252 | userFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); 253 | getContext().registerReceiverAsUser( 254 | mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); 255 | mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE); 256 | } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 257 | synchronized (mJobs) { 258 | // Let's go! 259 | mReadyToRock = true; 260 | mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( 261 | BatteryStats.SERVICE_NAME)); 262 | // Create the "runners". 263 | for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { 264 | mActiveServices.add( 265 | new JobServiceContext(this, mBatteryStats, 266 | getContext().getMainLooper())); 267 | } 268 | // Attach jobs to their controllers. 269 | ArraySet jobs = mJobs.getJobs(); 270 | for (int i=0; i(); 404 | mControllers.add(ConnectivityController.get(this)); 405 | mControllers.add(TimeController.get(this)); 406 | mControllers.add(IdleController.get(this)); 407 | mControllers.add(BatteryController.get(this)); 408 | mControllers.add(AppIdleController.get(this)); 409 | 410 | mHandler = new JobHandler(context.getMainLooper()); 411 | mJobSchedulerStub = new JobSchedulerStub(); 412 | mJobs = JobStore.initAndGet(this); 413 | } 414 | ``` 415 | 416 | 417 | ## [IdleController](http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/IdleController.java) 418 | 419 | * JobInfo#setRequiresDeviceIdleがtrueのjobだけ登録されている 420 | 421 | 422 | ## [AppIdleController](http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/AppIdleController.java) 423 | 424 | ``` 425 | Controls when apps are considered idle and if jobs pertaining to those apps should be executed. 426 | Apps that haven't been actively launched or accessed from a foreground app for a certain amount of time (maybe hours or days) are considered idle. 427 | When the app comes out of idle state, it will be allowed to run scheduled jobs. 428 | ``` 429 | 430 | * UsageStatsと関連性のあるjob controllerっぽい 431 | * アプリの使用具合をみてjobを制御してる?? 432 | * idle状態でも実行できるようにしているjobはここでも管理されるのか? 433 | * UsageStatsの状態変化によって、アプリがIdelかどうかを管理してるのかなー 434 | * UsageStatsもちゃんと見ないとだわ 435 | * http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/core/java/android/app/usage/UsageStatsManagerInternal.java#UsageStatsManagerInternal 436 | 437 | 438 | ## Jobの実行可能状態(ready)について 439 | 440 | * Jobがready(実行可能)になる条件は、Jobに登録した条件によって異なる 441 | * [JobStatus#isReady](http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/JobStatus.java#198) 442 | * readyは実行可能状態なので、readyになったからってすぐにJobが実行されるわけじゃない 443 | * System側が定義してる一定量のJobが溜まったら実行されるとか 444 | * TODO : ココらへんの実行される条件とかタイミングとか詳しく調べる 445 | * 条件が細かい... 446 | * TODO : もう少し詳しく調べる 447 | 448 | ##### http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/controllers/JobStatus.java#198 449 | 450 | ```java 451 | /** 452 | * @return Whether or not this job is ready to run, based on its requirements. This is true if 453 | * the constraints are satisfied or the deadline on the job has expired. 454 | */ 455 | public synchronized boolean isReady() { 456 | // Deadline constraint trumps other constraints 457 | // AppNotIdle implicit constraint trumps all! 458 | return (isConstraintsSatisfied() 459 | || (hasDeadlineConstraint() && deadlineConstraintSatisfied.get())) 460 | && appNotIdleConstraintSatisfied.get(); 461 | } 462 | ``` 463 | 464 | 465 | ## adb shell dumpsys jobscheduler 466 | 467 | * サンプル 468 | * JobSchedulerに登録されているJobをDumpする 469 | * JobSchedulerの状態を見る時にめっちゃ使う 470 | * TODO : Dumpされる情報を細かく調べる。意味とか 471 | 472 | ```java 473 | Started users: u0 u10 474 | Registered jobs: 475 | 326..:[ComponentInfo{com.android.providers.downloads/com.android.providers.downloads.DownloadIdleService},jId=1,u10,R=(-02:40,23:57:19),N=0,C=true,I=true,F=0,P=false,ANI=true] 476 | 425..:[ComponentInfo{android/com.android.server.pm.BackgroundDexOptService},jId=800,u0,R=(-2:20:18,none),N=0,C=true,I=true,F=0,P=false,ANI=true] 477 | 981..:[ComponentInfo{android/com.android.server.backup.KeyValueBackupJob},jId=20537,u0,R=(1:43:07,21:39:44),N=1,C=true,I=false,F=0,P=false,ANI=true] 478 | 105..:[ComponentInfo{com.os.operando.jobschedulersample/com.os.operando.jobschedulersample.services.MyJobService},jId=0,u0,R=(none,none),N=0,C=true,I=false,F=0,P=false,ANI=true] 479 | 174..:[ComponentInfo{android/com.android.server.MountServiceIdler},jId=808,u0,R=(16:47:50,none),N=0,C=true,I=true,F=0,P=false,ANI=true] 480 | 202..:[ComponentInfo{com.os.operando.jobschedulersample/com.os.operando.jobschedulersample.services.MyJobService},jId=0,u10,R=(none,none),N=0,C=true,I=false,F=0,P=false,ANI=true] 481 | 203..:[ComponentInfo{com.android.providers.downloads/com.android.providers.downloads.DownloadIdleService},jId=1,u0,R=(-20:17,23:39:42),N=0,C=true,I=true,F=0,P=true,ANI=true] 482 | 212..:[ComponentInfo{android/com.android.server.backup.FullBackupJob},jId=20536,u0,R=(none,none),N=2,C=true,I=true,F=0,P=false,ANI=true] 483 | 484 | Conn. 485 | connected: true unmetered: true 486 | 981..: C=true, UM=false 487 | 212..: C=false, UM=true 488 | 489 | Alarms (8429004) 490 | Next delay alarm in 6187s 491 | Next deadline alarm in 77984s 492 | Tracking: 493 | 981..: (14616797, 86413281) 494 | 203..: (7211953, 93611953) 495 | 326..: (8268760, 94668760) 496 | 174..: (68900000, N/A) 497 | 498 | Idle: false 499 | 5 500 | 425.. 501 | 174.. 502 | 203.. 503 | 326.. 504 | 212.. 505 | 506 | Batt. 507 | Stable power: false 508 | 42516548,174129466,98154298,203795623,32629981,212117473,105396486,202238442 509 | 510 | AppIdle 511 | Parole On: false 512 | android:idle=false, android:idle=false, android:idle=false, com.android.providers.downloads:idle=false, com.android.providers.downloads:idle=false, android:idle=false, com.os.operando.jobschedulersample:idle=false, com.os.operando.jobschedulersample:idle=false, 513 | 514 | Pending: 515 | 516 | Active jobs: 517 | 518 | mReadyToRock=true 519 | mDeviceIdleMode=false 520 | ``` 521 | 522 | ### マルチユーザ 523 | 524 | * マルチユーザのJobも管理してる 525 | 526 | ``` 527 | Started users: u0 u10 528 | ``` 529 | 530 | ### JobInfo.Builder#setRequiredNetworkTypeでセットした状態 531 | 532 | * N=0 : JobInfo.NETWORK_TYPE_NONE 533 | * N=1 : JobInfo.NETWORK_TYPE_ANY 534 | * N=2 : JobInfo.NETWORK_TYPE_UNMETERED(Wi-Fi) 535 | 536 | ``` 537 | 212..:[ComponentInfo{android/com.android.server.backup.KeyValueBackupJob},jId=20537,u0,R=(3:16:08,23:09:42),N=1,C=true,I=false,F=0,P=false,ANI=true] 538 | 231..:[ComponentInfo{android/com.android.server.backup.FullBackupJob},jId=20536,u0,R=(none,none),N=2,C=true,I=true,F=0,P=false,ANI=true] 539 | ``` 540 | 541 | ### (READY) 542 | * Jobが実行可能状態になったことを意味する 543 | 544 | ``` 545 | 144..:[ComponentInfo{com.os.operando.jobschedulersample/com.os.operando.jobschedulersample.services.MyJobService},jId=0,u0,R=(-00:18,00:38),N=1,C=false,I=false,F=0,P=false,ANI=true(READY) 546 | ``` 547 | 548 | ### Pending 549 | 550 | * Pending中のJob 551 | * Active Jobsの枠が開けば、実行される 552 | 553 | ``` 554 | Pending: 555 | 43620931 556 | 961017 557 | 228486462 558 | 229742495 559 | ``` 560 | 561 | 562 | ### Active Jobs 563 | 564 | * 実行中のJob 565 | * timeoutとかも設定されてる 566 | 567 | ``` 568 | Active jobs: 569 | Running for: 2s timeout=18770473 fromnow=597716 570 | 235..:[ComponentInfo{com.os.operando.jobschedulersample/com.os.operando.jobschedulersample.services.MyJobService},jId=0,u0,R=(-00:59,-00:02),N=1,C=false,I=false,F=0,P=false,ANI=true(READY)] 571 | ``` 572 | 573 | 574 | ## Debugging JobScheduler 575 | 576 | * adb shell dumpsys jobscheduler 577 | * JobSchedulerに登録されているJobをDumpする 578 | * めっちゃ使う 579 | * adb logcat -s JobSchedulerService 580 | * JobSchedulerServiceのログ。あんまり出ないけど... 581 | * idle状態にするコマンド 582 | * adb shell dumpsys battery unplug 583 | * adb shell dumpsys deviceidle enable 584 | * adb shell dumpsys deviceidle step 585 | * adb shell dumpsys deviceidle force-idle 586 | 587 | 588 | ## アプリアンインストール時・ユーザ削除時のJobSchedulerServiceのHandling 589 | 590 | * IntentFilterを使って、Handlingしてる 591 | * アンインストールされたアプリのJobの削除 592 | * 削除されたユーザのJobの削除 593 | * onBootPhaseはSystem Service系の起動段階を通知するものっぽい 594 | 595 | ##### http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#141 596 | 597 | ```java 598 | /** 599 | * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we 600 | * still clean up. On reinstall the package will have a new uid. 601 | */ 602 | private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 603 | @Override 604 | public void onReceive(Context context, Intent intent) { 605 | Slog.d(TAG, "Receieved: " + intent.getAction()); 606 | if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { 607 | // If this is an outright uninstall rather than the first half of an 608 | // app update sequence, cancel the jobs associated with the app. 609 | if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 610 | int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1); 611 | if (DEBUG) { 612 | Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); 613 | } 614 | cancelJobsForUid(uidRemoved); 615 | } 616 | } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { 617 | final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 618 | if (DEBUG) { 619 | Slog.d(TAG, "Removing jobs for user: " + userId); 620 | } 621 | cancelJobsForUser(userId); 622 | } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) { 623 | updateIdleMode(mPowerManager != null ? mPowerManager.isDeviceIdleMode() : false); 624 | } 625 | } 626 | }; 627 | ``` 628 | 629 | 630 | ##### http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#335 631 | 632 | ```java 633 | @Override 634 | public void onBootPhase(int phase) { 635 | if (PHASE_SYSTEM_SERVICES_READY == phase) { 636 | // Register br for package removals and user removals. 637 | final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 638 | filter.addDataScheme("package"); 639 | getContext().registerReceiverAsUser( 640 | mBroadcastReceiver, UserHandle.ALL, filter, null, null); 641 | final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); 642 | userFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); 643 | getContext().registerReceiverAsUser( 644 | mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); 645 | mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE); 646 | } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 647 | synchronized (mJobs) { 648 | // Let's go! 649 | mReadyToRock = true; 650 | mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( 651 | BatteryStats.SERVICE_NAME)); 652 | // Create the "runners". 653 | for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { 654 | mActiveServices.add( 655 | new JobServiceContext(this, mBatteryStats, 656 | getContext().getMainLooper())); 657 | } 658 | // Attach jobs to their controllers. 659 | ArraySet jobs = mJobs.getJobs(); 660 | for (int i=0; i1 of the ready jobs is idle mode we send all of them off 737 | * if more than 2 network connectivity jobs are ready we send them all off. 738 | * If more than 4 jobs total are ready we send them all off. 739 | * 検証 740 | * JobInfo.Builder#setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)のjobを1つずつ登録 741 | * 1個目のJobを登録したら、JobはREADYのままで止まっている(Pendingにも入ってない) 742 | * 2個目のJobを登録したら、JobがActiveになって実行された 743 | * 上の条件(2 network connectivity jobs are ready)に合致したので、Jobをpending queueに移動させて、実行したのだと推測 744 | * Jobがready(実行可能)になっても、登録されているJob全体として条件を満たしてなければJobがPendingまで移行しないってことっぽい 745 | * TODO : 他にもPendingになるルートがあるから、そこ調べる 746 | 747 | 748 | ##### http://tools.oesf.biz/android-6.0.0_r1.0/xref/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java#622 749 | 750 | ```java 751 | /** 752 | * The state of at least one job has changed. Here is where we could enforce various 753 | * policies on when we want to execute jobs. 754 | * Right now the policy is such: 755 | * If >1 of the ready jobs is idle mode we send all of them off 756 | * if more than 2 network connectivity jobs are ready we send them all off. 757 | * If more than 4 jobs total are ready we send them all off. 758 | * TODO: It would be nice to consolidate these sort of high-level policies somewhere. 759 | */ 760 | private void maybeQueueReadyJobsForExecutionLockedH() { 761 | int chargingCount = 0; 762 | int idleCount = 0; 763 | int backoffCount = 0; 764 | int connectivityCount = 0; 765 | List runnableJobs = new ArrayList(); 766 | ArraySet jobs = mJobs.getJobs(); 767 | for (int i=0; i 0) { 771 | backoffCount++; 772 | } 773 | if (job.hasIdleConstraint()) { 774 | idleCount++; 775 | } 776 | if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()) { 777 | connectivityCount++; 778 | } 779 | if (job.hasChargingConstraint()) { 780 | chargingCount++; 781 | } 782 | runnableJobs.add(job); 783 | } else if (isReadyToBeCancelledLocked(job)) { 784 | stopJobOnServiceContextLocked(job); 785 | } 786 | } 787 | if (backoffCount > 0 || 788 | idleCount >= MIN_IDLE_COUNT || 789 | connectivityCount >= MIN_CONNECTIVITY_COUNT || 790 | chargingCount >= MIN_CHARGING_COUNT || 791 | runnableJobs.size() >= MIN_READY_JOBS_COUNT) { 792 | if (DEBUG) { 793 | Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs."); 794 | } 795 | for (int i=0; i 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | value0 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | value1 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | --------------------------------------------------------------------------------