blob: da2876910ee515cabc4a229faa49f467a4055baa [file] [log] [blame]
Matthew Williams6de79e22014-05-01 10:47:00 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
Christopher Tate7060b042014-06-09 19:50:00 -070017package com.android.server.job.controllers;
Matthew Williams6de79e22014-05-01 10:47:00 -070018
Jeff Sharkey1b6519b2016-04-28 15:33:18 -060019import android.app.job.JobInfo;
Matthew Williams6de79e22014-05-01 10:47:00 -070020import android.content.Context;
Matthew Williams6de79e22014-05-01 10:47:00 -070021import android.net.ConnectivityManager;
Christopher Tate8bbe7042017-03-15 11:07:46 -070022import android.net.ConnectivityManager.NetworkCallback;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060023import android.net.INetworkPolicyListener;
Christopher Tate8bbe7042017-03-15 11:07:46 -070024import android.net.Network;
25import android.net.NetworkCapabilities;
Matthew Williams6de79e22014-05-01 10:47:00 -070026import android.net.NetworkInfo;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060027import android.net.NetworkPolicyManager;
Jeff Sharkeycaa3f8d2017-10-25 16:12:00 -060028import android.net.TrafficStats;
Christopher Tate8bbe7042017-03-15 11:07:46 -070029import android.os.Process;
Matthew Williams6de79e22014-05-01 10:47:00 -070030import android.os.UserHandle;
Jeff Sharkeycaa3f8d2017-10-25 16:12:00 -060031import android.text.format.DateUtils;
Dianne Hackbornf9bac162017-04-20 17:17:48 -070032import android.util.ArraySet;
Matthew Williams9b9244b62014-05-14 11:06:04 -070033import android.util.Slog;
Matthew Williams6de79e22014-05-01 10:47:00 -070034
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060035import com.android.internal.annotations.GuardedBy;
Christopher Tate7060b042014-06-09 19:50:00 -070036import com.android.server.job.JobSchedulerService;
Jeff Sharkeycaa3f8d2017-10-25 16:12:00 -060037import com.android.server.job.JobServiceContext;
Christopher Tate7060b042014-06-09 19:50:00 -070038import com.android.server.job.StateChangedListener;
Matthew Williams6de79e22014-05-01 10:47:00 -070039
Matthew Williamseffacfa2014-06-05 20:56:40 -070040import java.io.PrintWriter;
Matthew Williams6de79e22014-05-01 10:47:00 -070041
42/**
Matthew Williams9b9244b62014-05-14 11:06:04 -070043 * Handles changes in connectivity.
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060044 * <p>
45 * Each app can have a different default networks or different connectivity
46 * status due to user-requested network policies, so we need to check
47 * constraints on a per-UID basis.
Matthew Williams6de79e22014-05-01 10:47:00 -070048 */
Dianne Hackborn6466c1c2017-06-13 10:33:19 -070049public final class ConnectivityController extends StateController implements
Matthew Williamseffacfa2014-06-05 20:56:40 -070050 ConnectivityManager.OnNetworkActiveListener {
Christopher Tate7060b042014-06-09 19:50:00 -070051 private static final String TAG = "JobScheduler.Conn";
Christopher Tate8bbe7042017-03-15 11:07:46 -070052 private static final boolean DEBUG = false;
Matthew Williams6de79e22014-05-01 10:47:00 -070053
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060054 private final ConnectivityManager mConnManager;
55 private final NetworkPolicyManager mNetPolicyManager;
Christopher Tate8bbe7042017-03-15 11:07:46 -070056 private boolean mConnected;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060057
58 @GuardedBy("mLock")
Dianne Hackbornf9bac162017-04-20 17:17:48 -070059 private final ArraySet<JobStatus> mTrackedJobs = new ArraySet<>();
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060060
Matthew Williams9b9244b62014-05-14 11:06:04 -070061 /** Singleton. */
62 private static ConnectivityController mSingleton;
Matthew Williamseffacfa2014-06-05 20:56:40 -070063 private static Object sCreationLock = new Object();
Matthew Williams691e93e2014-05-12 15:33:09 -070064
Christopher Tate7060b042014-06-09 19:50:00 -070065 public static ConnectivityController get(JobSchedulerService jms) {
Matthew Williamseffacfa2014-06-05 20:56:40 -070066 synchronized (sCreationLock) {
67 if (mSingleton == null) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -080068 mSingleton = new ConnectivityController(jms, jms.getContext(), jms.getLock());
Matthew Williamseffacfa2014-06-05 20:56:40 -070069 }
70 return mSingleton;
Matthew Williams9b9244b62014-05-14 11:06:04 -070071 }
Matthew Williams9b9244b62014-05-14 11:06:04 -070072 }
73
Dianne Hackborn33d31c52016-02-16 10:30:33 -080074 private ConnectivityController(StateChangedListener stateChangedListener, Context context,
75 Object lock) {
76 super(stateChangedListener, context, lock);
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060077
78 mConnManager = mContext.getSystemService(ConnectivityManager.class);
79 mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
80
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070081 mConnected = false;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060082
Christopher Tate8bbe7042017-03-15 11:07:46 -070083 mConnManager.registerDefaultNetworkCallback(mNetworkCallback);
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060084 mNetPolicyManager.registerListener(mNetPolicyListener);
Matthew Williams6de79e22014-05-01 10:47:00 -070085 }
86
87 @Override
Dianne Hackbornb0001f62016-02-16 10:30:33 -080088 public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
Christopher Tate60977f42017-04-13 13:48:46 -070089 if (jobStatus.hasConnectivityConstraint()) {
Jeff Sharkey43d2a172017-07-12 10:50:42 -060090 updateConstraintsSatisfied(jobStatus);
Dianne Hackbornb0001f62016-02-16 10:30:33 -080091 mTrackedJobs.add(jobStatus);
Dianne Hackbornf9bac162017-04-20 17:17:48 -070092 jobStatus.setTrackingController(JobStatus.TRACKING_CONNECTIVITY);
Matthew Williamseffacfa2014-06-05 20:56:40 -070093 }
94 }
95
96 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060097 public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
98 boolean forUpdate) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -070099 if (jobStatus.clearTrackingController(JobStatus.TRACKING_CONNECTIVITY)) {
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800100 mTrackedJobs.remove(jobStatus);
Matthew Williamseffacfa2014-06-05 20:56:40 -0700101 }
Matthew Williams6de79e22014-05-01 10:47:00 -0700102 }
103
Jeff Sharkeycaa3f8d2017-10-25 16:12:00 -0600104 /**
105 * Test to see if running the given job on the given network is sane.
106 * <p>
107 * For example, if a job is trying to send 10MB over a 128Kbps EDGE
108 * connection, it would take 10.4 minutes, and has no chance of succeeding
109 * before the job times out, so we'd be insane to try running it.
110 */
111 private boolean isSane(JobStatus jobStatus, NetworkCapabilities capabilities) {
112 final long estimatedBytes = jobStatus.getEstimatedNetworkBytes();
113 if (estimatedBytes == JobInfo.NETWORK_BYTES_UNKNOWN) {
114 // We don't know how large the job is; cross our fingers!
115 return true;
116 }
117 if (capabilities == null) {
118 // We don't know what the network is like; cross our fingers!
119 return true;
120 }
121
122 // We don't ask developers to differentiate between upstream/downstream
123 // in their size estimates, so test against the slowest link direction.
124 final long downstream = capabilities.getLinkDownstreamBandwidthKbps();
125 final long upstream = capabilities.getLinkUpstreamBandwidthKbps();
126 final long slowest;
127 if (downstream > 0 && upstream > 0) {
128 slowest = Math.min(downstream, upstream);
129 } else if (downstream > 0) {
130 slowest = downstream;
131 } else if (upstream > 0) {
132 slowest = upstream;
133 } else {
134 // We don't know what the network is like; cross our fingers!
135 return true;
136 }
137
138 final long estimatedMillis = ((estimatedBytes * DateUtils.SECOND_IN_MILLIS)
139 / (slowest * TrafficStats.KB_IN_BYTES / 8));
140 if (estimatedMillis > JobServiceContext.EXECUTING_TIMESLICE_MILLIS) {
141 // If we'd never finish before the timeout, we'd be insane!
142 Slog.w(TAG, "Estimated " + estimatedBytes + " bytes over " + slowest
143 + " kbps network would take " + estimatedMillis + "ms; that's insane!");
144 return false;
145 } else {
146 return true;
147 }
148 }
149
Jeff Sharkey43d2a172017-07-12 10:50:42 -0600150 private boolean updateConstraintsSatisfied(JobStatus jobStatus) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700151 // TODO: consider matching against non-active networks
152
Christopher Tate8bbe7042017-03-15 11:07:46 -0700153 final int jobUid = jobStatus.getSourceUid();
Jeff Sharkey1b6519b2016-04-28 15:33:18 -0600154 final boolean ignoreBlocked = (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
Jeff Sharkey43d2a172017-07-12 10:50:42 -0600155 final Network network = mConnManager.getActiveNetworkForUid(jobUid, ignoreBlocked);
Jeff Sharkeycaa3f8d2017-10-25 16:12:00 -0600156 final NetworkInfo info = mConnManager.getNetworkInfoForUid(network, jobUid, ignoreBlocked);
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700157 final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network);
Christopher Tate8bbe7042017-03-15 11:07:46 -0700158
Jeff Sharkeycaa3f8d2017-10-25 16:12:00 -0600159 final boolean connected = (info != null) && info.isConnected();
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700160 final boolean satisfied = jobStatus.getJob().getRequiredNetwork().networkCapabilities
161 .satisfiedByNetworkCapabilities(capabilities);
Jeff Sharkeycaa3f8d2017-10-25 16:12:00 -0600162 final boolean sane = isSane(jobStatus, capabilities);
Jeff Sharkey43d2a172017-07-12 10:50:42 -0600163
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700164 final boolean changed = jobStatus
165 .setConnectivityConstraintSatisfied(connected && satisfied && sane);
Christopher Tate8bbe7042017-03-15 11:07:46 -0700166
Jeff Sharkey76a02412017-10-24 16:55:04 -0600167 // Pass along the evaluated network for job to use; prevents race
168 // conditions as default routes change over time, and opens the door to
169 // using non-default routes.
170 jobStatus.network = network;
171
Christopher Tate8bbe7042017-03-15 11:07:46 -0700172 // Track system-uid connected/validated as a general reportable proxy for the
173 // overall state of connectivity constraint satisfiability.
174 if (jobUid == Process.SYSTEM_UID) {
175 mConnected = connected;
Christopher Tate8bbe7042017-03-15 11:07:46 -0700176 }
177
178 if (DEBUG) {
179 Slog.i(TAG, "Connectivity " + (changed ? "CHANGED" : "unchanged")
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700180 + " for " + jobStatus + ": connected=" + connected
181 + " satisfied=" + satisfied
182 + " sane=" + sane);
Christopher Tate8bbe7042017-03-15 11:07:46 -0700183 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600184 return changed;
185 }
186
Matthew Williams6de79e22014-05-01 10:47:00 -0700187 /**
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600188 * Update all jobs tracked by this controller.
189 *
190 * @param uid only update jobs belonging to this UID, or {@code -1} to
191 * update all tracked jobs.
Matthew Williams6de79e22014-05-01 10:47:00 -0700192 */
Jeff Sharkey43d2a172017-07-12 10:50:42 -0600193 private void updateTrackedJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800194 synchronized (mLock) {
Matthew Williamseffacfa2014-06-05 20:56:40 -0700195 boolean changed = false;
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700196 for (int i = mTrackedJobs.size()-1; i >= 0; i--) {
197 final JobStatus js = mTrackedJobs.valueAt(i);
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600198 if (uid == -1 || uid == js.getSourceUid()) {
Jeff Sharkey43d2a172017-07-12 10:50:42 -0600199 changed |= updateConstraintsSatisfied(js);
Matthew Williamseffacfa2014-06-05 20:56:40 -0700200 }
Matthew Williamseffacfa2014-06-05 20:56:40 -0700201 }
202 if (changed) {
203 mStateChangedListener.onControllerStateChanged();
Matthew Williams6de79e22014-05-01 10:47:00 -0700204 }
205 }
Matthew Williamseffacfa2014-06-05 20:56:40 -0700206 }
207
208 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700209 * We know the network has just come up. We want to run any jobs that are ready.
Matthew Williamseffacfa2014-06-05 20:56:40 -0700210 */
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600211 @Override
Dianne Hackborn532ea262017-03-17 17:50:55 -0700212 public void onNetworkActive() {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800213 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700214 for (int i = mTrackedJobs.size()-1; i >= 0; i--) {
215 final JobStatus js = mTrackedJobs.valueAt(i);
Christopher Tate7060b042014-06-09 19:50:00 -0700216 if (js.isReady()) {
Matthew Williamseffacfa2014-06-05 20:56:40 -0700217 if (DEBUG) {
Christopher Tate7060b042014-06-09 19:50:00 -0700218 Slog.d(TAG, "Running " + js + " due to network activity.");
Matthew Williamseffacfa2014-06-05 20:56:40 -0700219 }
Christopher Tate7060b042014-06-09 19:50:00 -0700220 mStateChangedListener.onRunJobNow(js);
Matthew Williamseffacfa2014-06-05 20:56:40 -0700221 }
222 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700223 }
Matthew Williams6de79e22014-05-01 10:47:00 -0700224 }
225
Christopher Tate8bbe7042017-03-15 11:07:46 -0700226 private final NetworkCallback mNetworkCallback = new NetworkCallback() {
Matthew Williams6de79e22014-05-01 10:47:00 -0700227 @Override
Christopher Tate8bbe7042017-03-15 11:07:46 -0700228 public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
229 if (DEBUG) {
230 Slog.v(TAG, "onCapabilitiesChanged() : " + networkCapabilities);
231 }
Jeff Sharkey43d2a172017-07-12 10:50:42 -0600232 updateTrackedJobs(-1);
Christopher Tate8bbe7042017-03-15 11:07:46 -0700233 }
234
235 @Override
236 public void onLost(Network network) {
237 if (DEBUG) {
238 Slog.v(TAG, "Network lost");
239 }
Jeff Sharkey43d2a172017-07-12 10:50:42 -0600240 updateTrackedJobs(-1);
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600241 }
242 };
243
Christopher Tate8bbe7042017-03-15 11:07:46 -0700244 private final INetworkPolicyListener mNetPolicyListener = new INetworkPolicyListener.Stub() {
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600245 @Override
246 public void onUidRulesChanged(int uid, int uidRules) {
Christopher Tate8bbe7042017-03-15 11:07:46 -0700247 if (DEBUG) {
248 Slog.v(TAG, "Uid rules changed for " + uid);
249 }
Jeff Sharkey43d2a172017-07-12 10:50:42 -0600250 updateTrackedJobs(uid);
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600251 }
252
253 @Override
254 public void onMeteredIfacesChanged(String[] meteredIfaces) {
Christopher Tate8bbe7042017-03-15 11:07:46 -0700255 // We track this via our NetworkCallback
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600256 }
257
258 @Override
259 public void onRestrictBackgroundChanged(boolean restrictBackground) {
Christopher Tate8bbe7042017-03-15 11:07:46 -0700260 if (DEBUG) {
261 Slog.v(TAG, "Background restriction change to " + restrictBackground);
262 }
Jeff Sharkey43d2a172017-07-12 10:50:42 -0600263 updateTrackedJobs(-1);
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600264 }
265
266 @Override
Felipe Leme0ecfcd12016-09-06 12:49:48 -0700267 public void onUidPoliciesChanged(int uid, int uidPolicies) {
Christopher Tate8bbe7042017-03-15 11:07:46 -0700268 if (DEBUG) {
269 Slog.v(TAG, "Uid policy changed for " + uid);
270 }
Jeff Sharkey43d2a172017-07-12 10:50:42 -0600271 updateTrackedJobs(uid);
Felipe Leme99d5d3d2016-05-16 13:30:57 -0700272 }
Matthew Williams6de79e22014-05-01 10:47:00 -0700273 };
Matthew Williamseffacfa2014-06-05 20:56:40 -0700274
275 @Override
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700276 public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
Christopher Tate8bbe7042017-03-15 11:07:46 -0700277 pw.print("Connectivity: connected=");
278 pw.print(mConnected);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700279 pw.print("Tracking ");
280 pw.print(mTrackedJobs.size());
281 pw.println(":");
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600282 for (int i = 0; i < mTrackedJobs.size(); i++) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700283 final JobStatus js = mTrackedJobs.valueAt(i);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700284 if (js.shouldDump(filterUid)) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700285 pw.print(" #");
286 js.printUniqueId(pw);
287 pw.print(" from ");
288 UserHandle.formatUid(pw, js.getSourceUid());
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700289 pw.print(": "); pw.print(js.getJob().getRequiredNetwork());
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700290 }
Matthew Williamseffacfa2014-06-05 20:56:40 -0700291 }
292 }
Robert Greenwalt6078b502014-06-11 16:05:07 -0700293}