ConnectivityController.java revision 576430f36ac04843fed5697ecae1716489c164bb
1/* 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 17package com.android.server.job.controllers; 18 19import android.app.job.JobInfo; 20import android.content.BroadcastReceiver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.net.ConnectivityManager; 25import android.net.INetworkPolicyListener; 26import android.net.NetworkInfo; 27import android.net.NetworkPolicyManager; 28import android.os.UserHandle; 29import android.util.Slog; 30 31import com.android.internal.os.BackgroundThread; 32import com.android.internal.annotations.GuardedBy; 33import com.android.server.job.JobSchedulerService; 34import com.android.server.job.StateChangedListener; 35 36import java.io.PrintWriter; 37import java.util.ArrayList; 38 39/** 40 * Handles changes in connectivity. 41 * <p> 42 * Each app can have a different default networks or different connectivity 43 * status due to user-requested network policies, so we need to check 44 * constraints on a per-UID basis. 45 */ 46public class ConnectivityController extends StateController implements 47 ConnectivityManager.OnNetworkActiveListener { 48 private static final String TAG = "JobScheduler.Conn"; 49 50 private final ConnectivityManager mConnManager; 51 private final NetworkPolicyManager mNetPolicyManager; 52 53 @GuardedBy("mLock") 54 private final ArrayList<JobStatus> mTrackedJobs = new ArrayList<JobStatus>(); 55 56 /** Singleton. */ 57 private static ConnectivityController mSingleton; 58 private static Object sCreationLock = new Object(); 59 60 public static ConnectivityController get(JobSchedulerService jms) { 61 synchronized (sCreationLock) { 62 if (mSingleton == null) { 63 mSingleton = new ConnectivityController(jms, jms.getContext(), jms.getLock()); 64 } 65 return mSingleton; 66 } 67 } 68 69 private ConnectivityController(StateChangedListener stateChangedListener, Context context, 70 Object lock) { 71 super(stateChangedListener, context, lock); 72 73 mConnManager = mContext.getSystemService(ConnectivityManager.class); 74 mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class); 75 76 final IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); 77 mContext.registerReceiverAsUser( 78 mConnectivityReceiver, UserHandle.SYSTEM, intentFilter, null, null); 79 80 mNetPolicyManager.registerListener(mNetPolicyListener); 81 } 82 83 @Override 84 public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { 85 if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint() 86 || jobStatus.hasNotRoamingConstraint()) { 87 updateConstraintsSatisfied(jobStatus); 88 mTrackedJobs.add(jobStatus); 89 } 90 } 91 92 @Override 93 public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, 94 boolean forUpdate) { 95 if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint() 96 || jobStatus.hasNotRoamingConstraint()) { 97 mTrackedJobs.remove(jobStatus); 98 } 99 } 100 101 private boolean updateConstraintsSatisfied(JobStatus jobStatus) { 102 final boolean ignoreBlocked = (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0; 103 final NetworkInfo info = mConnManager.getActiveNetworkInfoForUid(jobStatus.getSourceUid(), 104 ignoreBlocked); 105 final boolean connected = (info != null) && info.isConnected(); 106 final boolean unmetered = connected && !info.isMetered(); 107 final boolean notRoaming = connected && !info.isRoaming(); 108 109 boolean changed = false; 110 changed |= jobStatus.setConnectivityConstraintSatisfied(connected); 111 changed |= jobStatus.setUnmeteredConstraintSatisfied(unmetered); 112 changed |= jobStatus.setNotRoamingConstraintSatisfied(notRoaming); 113 return changed; 114 } 115 116 /** 117 * Update all jobs tracked by this controller. 118 * 119 * @param uid only update jobs belonging to this UID, or {@code -1} to 120 * update all tracked jobs. 121 */ 122 private void updateTrackedJobs(int uid) { 123 synchronized (mLock) { 124 boolean changed = false; 125 for (int i = 0; i < mTrackedJobs.size(); i++) { 126 final JobStatus js = mTrackedJobs.get(i); 127 if (uid == -1 || uid == js.getSourceUid()) { 128 changed |= updateConstraintsSatisfied(js); 129 } 130 } 131 if (changed) { 132 mStateChangedListener.onControllerStateChanged(); 133 } 134 } 135 } 136 137 /** 138 * We know the network has just come up. We want to run any jobs that are ready. 139 */ 140 @Override 141 public synchronized void onNetworkActive() { 142 synchronized (mLock) { 143 for (int i = 0; i < mTrackedJobs.size(); i++) { 144 final JobStatus js = mTrackedJobs.get(i); 145 if (js.isReady()) { 146 if (DEBUG) { 147 Slog.d(TAG, "Running " + js + " due to network activity."); 148 } 149 mStateChangedListener.onRunJobNow(js); 150 } 151 } 152 } 153 } 154 155 private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() { 156 @Override 157 public void onReceive(Context context, Intent intent) { 158 updateTrackedJobs(-1); 159 } 160 }; 161 162 private INetworkPolicyListener mNetPolicyListener = new INetworkPolicyListener.Stub() { 163 @Override 164 public void onUidRulesChanged(int uid, int uidRules) { 165 updateTrackedJobs(uid); 166 } 167 168 @Override 169 public void onMeteredIfacesChanged(String[] meteredIfaces) { 170 updateTrackedJobs(-1); 171 } 172 173 @Override 174 public void onRestrictBackgroundChanged(boolean restrictBackground) { 175 updateTrackedJobs(-1); 176 } 177 178 @Override 179 public void onRestrictBackgroundWhitelistChanged(int uid, boolean whitelisted) { 180 updateTrackedJobs(uid); 181 } 182 183 @Override 184 public void onRestrictBackgroundBlacklistChanged(int uid, boolean blacklisted) { 185 updateTrackedJobs(uid); 186 } 187 }; 188 189 @Override 190 public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { 191 pw.println("Connectivity."); 192 pw.print("Tracking "); 193 pw.print(mTrackedJobs.size()); 194 pw.println(":"); 195 for (int i = 0; i < mTrackedJobs.size(); i++) { 196 final JobStatus js = mTrackedJobs.get(i); 197 if (js.shouldDump(filterUid)) { 198 pw.print(" #"); 199 js.printUniqueId(pw); 200 pw.print(" from "); 201 UserHandle.formatUid(pw, js.getSourceUid()); 202 pw.print(": C="); pw.print(js.hasConnectivityConstraint()); 203 pw.print(": UM="); pw.print(js.hasUnmeteredConstraint()); 204 pw.print(": NR="); pw.println(js.hasNotRoamingConstraint()); 205 } 206 } 207 } 208} 209