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