├── 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 |
--------------------------------------------------------------------------------