16de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams/*
26de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams * Copyright (C) 2014 The Android Open Source Project
36de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams *
46de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams * Licensed under the Apache License, Version 2.0 (the "License");
56de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams * you may not use this file except in compliance with the License.
66de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams * You may obtain a copy of the License at
76de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams *
86de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams *      http://www.apache.org/licenses/LICENSE-2.0
96de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams *
106de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams * Unless required by applicable law or agreed to in writing, software
116de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams * distributed under the License is distributed on an "AS IS" BASIS,
126de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams * See the License for the specific language governing permissions and
146de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams * limitations under the License
156de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams */
166de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams
177060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tatepackage com.android.server.job.controllers;
186de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams
191b6519bd87a59a10e9928e5772a17976692611daJeff Sharkeyimport android.app.job.JobInfo;
206de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williamsimport android.content.BroadcastReceiver;
216de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williamsimport android.content.Context;
226de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williamsimport android.content.Intent;
236de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williamsimport android.content.IntentFilter;
246de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williamsimport android.net.ConnectivityManager;
25f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkeyimport android.net.INetworkPolicyListener;
266de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williamsimport android.net.NetworkInfo;
27f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkeyimport android.net.NetworkPolicyManager;
286de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williamsimport android.os.UserHandle;
299b9244b6941110ea2d940d9fc8eed0cdff96a016Matthew Williamsimport android.util.Slog;
306de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams
31f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkeyimport com.android.internal.annotations.GuardedBy;
327060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport com.android.server.job.JobSchedulerService;
337060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport com.android.server.job.StateChangedListener;
346de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams
35effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williamsimport java.io.PrintWriter;
36f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkeyimport java.util.ArrayList;
376de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams
386de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams/**
399b9244b6941110ea2d940d9fc8eed0cdff96a016Matthew Williams * Handles changes in connectivity.
40f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey * <p>
41f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey * Each app can have a different default networks or different connectivity
42f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey * status due to user-requested network policies, so we need to check
43f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey * constraints on a per-UID basis.
446de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams */
45effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williamspublic class ConnectivityController extends StateController implements
46effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams        ConnectivityManager.OnNetworkActiveListener {
477060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    private static final String TAG = "JobScheduler.Conn";
486de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams
49f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey    private final ConnectivityManager mConnManager;
50f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey    private final NetworkPolicyManager mNetPolicyManager;
51f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey
52f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey    @GuardedBy("mLock")
53f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey    private final ArrayList<JobStatus> mTrackedJobs = new ArrayList<JobStatus>();
54f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey
559b9244b6941110ea2d940d9fc8eed0cdff96a016Matthew Williams    /** Singleton. */
569b9244b6941110ea2d940d9fc8eed0cdff96a016Matthew Williams    private static ConnectivityController mSingleton;
57effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams    private static Object sCreationLock = new Object();
58691e93e82cf97338a760c5fbf900ef29ed9224c2Matthew Williams
597060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    public static ConnectivityController get(JobSchedulerService jms) {
60effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams        synchronized (sCreationLock) {
61effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams            if (mSingleton == null) {
6233d31c5b70c7d056e799e34bb6eccbe6939714eaDianne Hackborn                mSingleton = new ConnectivityController(jms, jms.getContext(), jms.getLock());
63effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams            }
64effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams            return mSingleton;
659b9244b6941110ea2d940d9fc8eed0cdff96a016Matthew Williams        }
669b9244b6941110ea2d940d9fc8eed0cdff96a016Matthew Williams    }
679b9244b6941110ea2d940d9fc8eed0cdff96a016Matthew Williams
6833d31c5b70c7d056e799e34bb6eccbe6939714eaDianne Hackborn    private ConnectivityController(StateChangedListener stateChangedListener, Context context,
6933d31c5b70c7d056e799e34bb6eccbe6939714eaDianne Hackborn            Object lock) {
7033d31c5b70c7d056e799e34bb6eccbe6939714eaDianne Hackborn        super(stateChangedListener, context, lock);
71f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey
72f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        mConnManager = mContext.getSystemService(ConnectivityManager.class);
73f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
74f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey
75f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        final IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
766de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams        mContext.registerReceiverAsUser(
77f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey                mConnectivityReceiver, UserHandle.SYSTEM, intentFilter, null, null);
78f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey
79f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        mNetPolicyManager.registerListener(mNetPolicyListener);
806de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams    }
816de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams
826de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams    @Override
83b0001f6fb1383d9824c2733896b0b348e7f77240Dianne Hackborn    public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
84f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()
85f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey                || jobStatus.hasNotRoamingConstraint()) {
86f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey            updateConstraintsSatisfied(jobStatus);
87b0001f6fb1383d9824c2733896b0b348e7f77240Dianne Hackborn            mTrackedJobs.add(jobStatus);
886de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams        }
896de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams    }
906de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams
916de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams    @Override
92f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
93f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey            boolean forUpdate) {
94f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()
95f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey                || jobStatus.hasNotRoamingConstraint()) {
96b0001f6fb1383d9824c2733896b0b348e7f77240Dianne Hackborn            mTrackedJobs.remove(jobStatus);
97effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams        }
986de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams    }
996de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams
100f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey    private boolean updateConstraintsSatisfied(JobStatus jobStatus) {
1011b6519bd87a59a10e9928e5772a17976692611daJeff Sharkey        final boolean ignoreBlocked = (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
1021b6519bd87a59a10e9928e5772a17976692611daJeff Sharkey        final NetworkInfo info = mConnManager.getActiveNetworkInfoForUid(jobStatus.getSourceUid(),
1031b6519bd87a59a10e9928e5772a17976692611daJeff Sharkey                ignoreBlocked);
104f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        final boolean connected = (info != null) && info.isConnected();
105f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        final boolean unmetered = connected && !info.isMetered();
106f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        final boolean notRoaming = connected && !info.isRoaming();
107f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey
108f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        boolean changed = false;
109f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        changed |= jobStatus.setConnectivityConstraintSatisfied(connected);
110f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        changed |= jobStatus.setUnmeteredConstraintSatisfied(unmetered);
111f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        changed |= jobStatus.setNotRoamingConstraintSatisfied(notRoaming);
112f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        return changed;
113f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey    }
114f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey
1156de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams    /**
116f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey     * Update all jobs tracked by this controller.
117f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey     *
118f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey     * @param uid only update jobs belonging to this UID, or {@code -1} to
119f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey     *            update all tracked jobs.
1206de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams     */
121f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey    private void updateTrackedJobs(int uid) {
12233d31c5b70c7d056e799e34bb6eccbe6939714eaDianne Hackborn        synchronized (mLock) {
123effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams            boolean changed = false;
124f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey            for (int i = 0; i < mTrackedJobs.size(); i++) {
125f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey                final JobStatus js = mTrackedJobs.get(i);
126f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey                if (uid == -1 || uid == js.getSourceUid()) {
127f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey                    changed |= updateConstraintsSatisfied(js);
128effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams                }
129effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams            }
130effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams            if (changed) {
131effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams                mStateChangedListener.onControllerStateChanged();
1326de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams            }
1336de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams        }
134effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams    }
135effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams
136effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams    /**
1377060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate     * We know the network has just come up. We want to run any jobs that are ready.
138effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams     */
139f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey    @Override
140effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams    public synchronized void onNetworkActive() {
14133d31c5b70c7d056e799e34bb6eccbe6939714eaDianne Hackborn        synchronized (mLock) {
142f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey            for (int i = 0; i < mTrackedJobs.size(); i++) {
143f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey                final JobStatus js = mTrackedJobs.get(i);
1447060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                if (js.isReady()) {
145effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams                    if (DEBUG) {
1467060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                        Slog.d(TAG, "Running " + js + " due to network activity.");
147effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams                    }
1487060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                    mStateChangedListener.onRunJobNow(js);
149effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams                }
150effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams            }
1519b9244b6941110ea2d940d9fc8eed0cdff96a016Matthew Williams        }
1526de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams    }
1536de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams
154f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey    private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
1556de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams        @Override
1566de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams        public void onReceive(Context context, Intent intent) {
157f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey            updateTrackedJobs(-1);
158f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        }
159f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey    };
160f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey
161f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey    private INetworkPolicyListener mNetPolicyListener = new INetworkPolicyListener.Stub() {
162f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        @Override
163f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        public void onUidRulesChanged(int uid, int uidRules) {
164f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey            updateTrackedJobs(uid);
165f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        }
166f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey
167f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        @Override
168f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        public void onMeteredIfacesChanged(String[] meteredIfaces) {
169f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey            updateTrackedJobs(-1);
170f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        }
171f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey
172f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        @Override
173f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        public void onRestrictBackgroundChanged(boolean restrictBackground) {
174f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey            updateTrackedJobs(-1);
175f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        }
176f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey
177f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        @Override
178f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        public void onRestrictBackgroundWhitelistChanged(int uid, boolean whitelisted) {
179f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey            updateTrackedJobs(uid);
1806de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams        }
18199d5d3d7759883119665d928d110edb14df44eccFelipe Leme
18299d5d3d7759883119665d928d110edb14df44eccFelipe Leme        @Override
18399d5d3d7759883119665d928d110edb14df44eccFelipe Leme        public void onRestrictBackgroundBlacklistChanged(int uid, boolean blacklisted) {
18499d5d3d7759883119665d928d110edb14df44eccFelipe Leme            updateTrackedJobs(uid);
18599d5d3d7759883119665d928d110edb14df44eccFelipe Leme        }
1866de79e2b17fa0796ea4d39fd9555b563c484248dMatthew Williams    };
187effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams
188effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams    @Override
189ef3aa6ee53c5e4f1c50dd5a9b5821c54e449d4b3Dianne Hackborn    public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
190e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn        pw.println("Connectivity.");
191e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn        pw.print("Tracking ");
192e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn        pw.print(mTrackedJobs.size());
193e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn        pw.println(":");
194f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey        for (int i = 0; i < mTrackedJobs.size(); i++) {
195f07c7b9fd0a640bff4bf7690373613da217fe69bJeff Sharkey            final JobStatus js = mTrackedJobs.get(i);
196ef3aa6ee53c5e4f1c50dd5a9b5821c54e449d4b3Dianne Hackborn            if (js.shouldDump(filterUid)) {
197e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn                pw.print("  #");
198e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn                js.printUniqueId(pw);
199e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn                pw.print(" from ");
200e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn                UserHandle.formatUid(pw, js.getSourceUid());
201e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn                pw.print(": C="); pw.print(js.hasConnectivityConstraint());
202e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn                pw.print(": UM="); pw.print(js.hasUnmeteredConstraint());
203e9a988caca733d2f292991a52a0047685a69812fDianne Hackborn                pw.print(": NR="); pw.println(js.hasNotRoamingConstraint());
204ef3aa6ee53c5e4f1c50dd5a9b5821c54e449d4b3Dianne Hackborn            }
205effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams        }
206effacfa75bd9c2ebc889a7bc4f002c07f82f4c31Matthew Williams    }
2076078b50b017fbcf8d6cbf9f83226ed5667d5729eRobert Greenwalt}
208