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