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