ConnectivityController.java revision 88c4135d88eb59320fe93801088bcd6c47e50efb
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 19 20import android.content.BroadcastReceiver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.net.ConnectivityManager; 25import android.net.NetworkInfo; 26import android.os.ServiceManager; 27import android.os.UserHandle; 28import android.util.Slog; 29 30import com.android.server.ConnectivityService; 31import com.android.server.job.JobSchedulerService; 32import com.android.server.job.StateChangedListener; 33 34import java.io.PrintWriter; 35import java.util.LinkedList; 36import java.util.List; 37 38/** 39 * Handles changes in connectivity. 40 * We are only interested in metered vs. unmetered networks, and we're interested in them on a 41 * per-user basis. 42 */ 43public class ConnectivityController extends StateController implements 44 ConnectivityManager.OnNetworkActiveListener { 45 private static final String TAG = "JobScheduler.Conn"; 46 47 private final List<JobStatus> mTrackedJobs = new LinkedList<JobStatus>(); 48 private final BroadcastReceiver mConnectivityChangedReceiver = 49 new ConnectivityChangedReceiver(); 50 /** Singleton. */ 51 private static ConnectivityController mSingleton; 52 private static Object sCreationLock = new Object(); 53 /** Track whether the latest active network is metered. */ 54 private boolean mNetworkUnmetered; 55 /** Track whether the latest active network is connected. */ 56 private boolean mNetworkConnected; 57 58 public static ConnectivityController get(JobSchedulerService jms) { 59 synchronized (sCreationLock) { 60 if (mSingleton == null) { 61 mSingleton = new ConnectivityController(jms, jms.getContext(), jms.getLock()); 62 } 63 return mSingleton; 64 } 65 } 66 67 private ConnectivityController(StateChangedListener stateChangedListener, Context context, 68 Object lock) { 69 super(stateChangedListener, context, lock); 70 // Register connectivity changed BR. 71 IntentFilter intentFilter = new IntentFilter(); 72 intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); 73 mContext.registerReceiverAsUser( 74 mConnectivityChangedReceiver, UserHandle.ALL, intentFilter, null, null); 75 ConnectivityService cs = 76 (ConnectivityService)ServiceManager.getService(Context.CONNECTIVITY_SERVICE); 77 if (cs != null) { 78 if (cs.getActiveNetworkInfo() != null) { 79 mNetworkConnected = cs.getActiveNetworkInfo().isConnected(); 80 mNetworkUnmetered = mNetworkConnected && !cs.isActiveNetworkMetered(); 81 } else { 82 mNetworkConnected = mNetworkUnmetered = false; 83 } 84 } 85 } 86 87 @Override 88 public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { 89 if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) { 90 jobStatus.setConnectivityConstraintSatisfied(mNetworkConnected); 91 jobStatus.setUnmeteredConstraintSatisfied(mNetworkUnmetered); 92 mTrackedJobs.add(jobStatus); 93 } 94 } 95 96 @Override 97 public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) { 98 if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) { 99 mTrackedJobs.remove(jobStatus); 100 } 101 } 102 103 /** 104 * @param userId Id of the user for whom we are updating the connectivity state. 105 */ 106 private void updateTrackedJobs(int userId) { 107 synchronized (mLock) { 108 boolean changed = false; 109 for (JobStatus js : mTrackedJobs) { 110 if (js.getUserId() != userId) { 111 continue; 112 } 113 changed |= js.setConnectivityConstraintSatisfied(mNetworkConnected); 114 changed |= js.setUnmeteredConstraintSatisfied(mNetworkUnmetered); 115 } 116 if (changed) { 117 mStateChangedListener.onControllerStateChanged(); 118 } 119 } 120 } 121 122 /** 123 * We know the network has just come up. We want to run any jobs that are ready. 124 */ 125 public synchronized void onNetworkActive() { 126 synchronized (mLock) { 127 for (JobStatus js : mTrackedJobs) { 128 if (js.isReady()) { 129 if (DEBUG) { 130 Slog.d(TAG, "Running " + js + " due to network activity."); 131 } 132 mStateChangedListener.onRunJobNow(js); 133 } 134 } 135 } 136 } 137 138 class ConnectivityChangedReceiver extends BroadcastReceiver { 139 /** 140 * We'll receive connectivity changes for each user here, which we process independently. 141 * We are only interested in the active network here. We're only interested in the active 142 * network, b/c the end result of this will be for apps to try to hit the network. 143 * @param context The Context in which the receiver is running. 144 * @param intent The Intent being received. 145 */ 146 // TODO: Test whether this will be called twice for each user. 147 @Override 148 public void onReceive(Context context, Intent intent) { 149 if (DEBUG) { 150 Slog.d(TAG, "Received connectivity event: " + intent.getAction() + " u" 151 + context.getUserId()); 152 } 153 final String action = intent.getAction(); 154 if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { 155 final int networkType = 156 intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, 157 ConnectivityManager.TYPE_NONE); 158 // Connectivity manager for THIS context - important! 159 final ConnectivityManager connManager = (ConnectivityManager) 160 context.getSystemService(Context.CONNECTIVITY_SERVICE); 161 final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo(); 162 final int userid = context.getUserId(); 163 // This broadcast gets sent a lot, only update if the active network has changed. 164 if (activeNetwork == null) { 165 mNetworkUnmetered = false; 166 mNetworkConnected = false; 167 updateTrackedJobs(userid); 168 } else if (activeNetwork.getType() == networkType) { 169 mNetworkUnmetered = false; 170 mNetworkConnected = !intent.getBooleanExtra( 171 ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); 172 if (mNetworkConnected) { // No point making the call if we know there's no conn. 173 mNetworkUnmetered = !connManager.isActiveNetworkMetered(); 174 } 175 updateTrackedJobs(userid); 176 } 177 } else { 178 if (DEBUG) { 179 Slog.d(TAG, "Unrecognised action in intent: " + action); 180 } 181 } 182 } 183 }; 184 185 @Override 186 public void dumpControllerStateLocked(PrintWriter pw) { 187 pw.println("Conn."); 188 pw.println("connected: " + mNetworkConnected + " unmetered: " + mNetworkUnmetered); 189 for (JobStatus js: mTrackedJobs) { 190 pw.println(String.valueOf(js.getJobId() + "," + js.getUid()) 191 + ": C=" + js.hasConnectivityConstraint() 192 + ", UM=" + js.hasUnmeteredConstraint()); 193 } 194 } 195} 196