Out with the old; in with the new
Switch to the official "JobScheduler" etc naming.
Bug 14997851
Change-Id: I73a61aaa9af0740c114d08188bd97c52f3ac86b7
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
new file mode 100644
index 0000000..7e79ff7
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job.controllers;
+
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.server.ConnectivityService;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.StateChangedListener;
+
+import java.io.PrintWriter;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Handles changes in connectivity.
+ * We are only interested in metered vs. unmetered networks, and we're interested in them on a
+ * per-user basis.
+ */
+public class ConnectivityController extends StateController implements
+ ConnectivityManager.OnNetworkActiveListener {
+ private static final String TAG = "JobScheduler.Conn";
+
+ private final List<JobStatus> mTrackedJobs = new LinkedList<JobStatus>();
+ private final BroadcastReceiver mConnectivityChangedReceiver =
+ new ConnectivityChangedReceiver();
+ /** Singleton. */
+ private static ConnectivityController mSingleton;
+ private static Object sCreationLock = new Object();
+ /** Track whether the latest active network is metered. */
+ private boolean mNetworkUnmetered;
+ /** Track whether the latest active network is connected. */
+ private boolean mNetworkConnected;
+
+ public static ConnectivityController get(JobSchedulerService jms) {
+ synchronized (sCreationLock) {
+ if (mSingleton == null) {
+ mSingleton = new ConnectivityController(jms, jms.getContext());
+ }
+ return mSingleton;
+ }
+ }
+
+ private ConnectivityController(StateChangedListener stateChangedListener, Context context) {
+ super(stateChangedListener, context);
+ // Register connectivity changed BR.
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ mContext.registerReceiverAsUser(
+ mConnectivityChangedReceiver, UserHandle.ALL, intentFilter, null, null);
+ ConnectivityService cs =
+ (ConnectivityService)ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+ if (cs != null) {
+ if (cs.getActiveNetworkInfo() != null) {
+ mNetworkConnected = cs.getActiveNetworkInfo().isConnected();
+ }
+ mNetworkUnmetered = mNetworkConnected && !cs.isActiveNetworkMetered();
+ }
+ }
+
+ @Override
+ public void maybeStartTrackingJob(JobStatus jobStatus) {
+ if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
+ synchronized (mTrackedJobs) {
+ jobStatus.connectivityConstraintSatisfied.set(mNetworkConnected);
+ jobStatus.unmeteredConstraintSatisfied.set(mNetworkUnmetered);
+ mTrackedJobs.add(jobStatus);
+ }
+ }
+ }
+
+ @Override
+ public void maybeStopTrackingJob(JobStatus jobStatus) {
+ if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
+ synchronized (mTrackedJobs) {
+ mTrackedJobs.remove(jobStatus);
+ }
+ }
+ }
+
+ /**
+ * @param userId Id of the user for whom we are updating the connectivity state.
+ */
+ private void updateTrackedJobs(int userId) {
+ synchronized (mTrackedJobs) {
+ boolean changed = false;
+ for (JobStatus js : mTrackedJobs) {
+ if (js.getUserId() != userId) {
+ continue;
+ }
+ boolean prevIsConnected =
+ js.connectivityConstraintSatisfied.getAndSet(mNetworkConnected);
+ boolean prevIsMetered = js.unmeteredConstraintSatisfied.getAndSet(mNetworkUnmetered);
+ if (prevIsConnected != mNetworkConnected || prevIsMetered != mNetworkUnmetered) {
+ changed = true;
+ }
+ }
+ if (changed) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+ }
+
+ /**
+ * We know the network has just come up. We want to run any jobs that are ready.
+ */
+ public synchronized void onNetworkActive() {
+ synchronized (mTrackedJobs) {
+ for (JobStatus js : mTrackedJobs) {
+ if (js.isReady()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Running " + js + " due to network activity.");
+ }
+ mStateChangedListener.onRunJobNow(js);
+ }
+ }
+ }
+ }
+
+ class ConnectivityChangedReceiver extends BroadcastReceiver {
+ /**
+ * We'll receive connectivity changes for each user here, which we process independently.
+ * We are only interested in the active network here. We're only interested in the active
+ * network, b/c the end result of this will be for apps to try to hit the network.
+ * @param context The Context in which the receiver is running.
+ * @param intent The Intent being received.
+ */
+ // TODO: Test whether this will be called twice for each user.
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received connectivity event: " + intent.getAction() + " u"
+ + context.getUserId());
+ }
+ final String action = intent.getAction();
+ if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ final int networkType =
+ intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
+ ConnectivityManager.TYPE_NONE);
+ // Connectivity manager for THIS context - important!
+ final ConnectivityManager connManager = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
+ final int userid = context.getUserId();
+ // This broadcast gets sent a lot, only update if the active network has changed.
+ if (activeNetwork == null) {
+ mNetworkUnmetered = false;
+ mNetworkConnected = false;
+ updateTrackedJobs(userid);
+ } else if (activeNetwork.getType() == networkType) {
+ mNetworkUnmetered = false;
+ mNetworkConnected = !intent.getBooleanExtra(
+ ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+ if (mNetworkConnected) { // No point making the call if we know there's no conn.
+ mNetworkUnmetered = !connManager.isActiveNetworkMetered();
+ }
+ updateTrackedJobs(userid);
+ }
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Unrecognised action in intent: " + action);
+ }
+ }
+ }
+ };
+
+ @Override
+ public void dumpControllerState(PrintWriter pw) {
+ pw.println("Conn.");
+ pw.println("connected: " + mNetworkConnected + " unmetered: " + mNetworkUnmetered);
+ for (JobStatus js: mTrackedJobs) {
+ pw.println(String.valueOf(js.hashCode()).substring(0, 3) + ".."
+ + ": C=" + js.hasConnectivityConstraint()
+ + ", UM=" + js.hasUnmeteredConstraint());
+ }
+ }
+}
\ No newline at end of file