1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.settings.datausage;
16 
17 import android.content.Context;
18 import android.net.INetworkPolicyListener;
19 import android.net.NetworkPolicyManager;
20 import android.os.Handler;
21 import android.os.RemoteException;
22 import android.util.SparseIntArray;
23 
24 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
25 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
26 import com.android.settings.overlay.FeatureFactory;
27 
28 import java.util.ArrayList;
29 
30 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
31 import static android.net.NetworkPolicyManager.POLICY_NONE;
32 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
33 
34 public class DataSaverBackend {
35 
36     private static final String TAG = "DataSaverBackend";
37 
38     private final Context mContext;
39     private final MetricsFeatureProvider mMetricsFeatureProvider;
40 
41     private final Handler mHandler = new Handler();
42     private final NetworkPolicyManager mPolicyManager;
43     private final ArrayList<Listener> mListeners = new ArrayList<>();
44     private SparseIntArray mUidPolicies = new SparseIntArray();
45     private boolean mWhitelistInitialized;
46     private boolean mBlacklistInitialized;
47 
48     // TODO: Staticize into only one.
DataSaverBackend(Context context)49     public DataSaverBackend(Context context) {
50         mContext = context;
51         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
52         mPolicyManager = NetworkPolicyManager.from(context);
53     }
54 
addListener(Listener listener)55     public void addListener(Listener listener) {
56         mListeners.add(listener);
57         if (mListeners.size() == 1) {
58             mPolicyManager.registerListener(mPolicyListener);
59         }
60         listener.onDataSaverChanged(isDataSaverEnabled());
61     }
62 
remListener(Listener listener)63     public void remListener(Listener listener) {
64         mListeners.remove(listener);
65         if (mListeners.size() == 0) {
66             mPolicyManager.unregisterListener(mPolicyListener);
67         }
68     }
69 
isDataSaverEnabled()70     public boolean isDataSaverEnabled() {
71         return mPolicyManager.getRestrictBackground();
72     }
73 
setDataSaverEnabled(boolean enabled)74     public void setDataSaverEnabled(boolean enabled) {
75         mPolicyManager.setRestrictBackground(enabled);
76         mMetricsFeatureProvider.action(
77                 mContext, MetricsEvent.ACTION_DATA_SAVER_MODE, enabled ? 1 : 0);
78     }
79 
refreshWhitelist()80     public void refreshWhitelist() {
81         loadWhitelist();
82     }
83 
setIsWhitelisted(int uid, String packageName, boolean whitelisted)84     public void setIsWhitelisted(int uid, String packageName, boolean whitelisted) {
85         final int policy = whitelisted ? POLICY_ALLOW_METERED_BACKGROUND : POLICY_NONE;
86         mPolicyManager.setUidPolicy(uid, policy);
87         mUidPolicies.put(uid, policy);
88         if (whitelisted) {
89             mMetricsFeatureProvider.action(
90                     mContext, MetricsEvent.ACTION_DATA_SAVER_WHITELIST, packageName);
91         }
92     }
93 
isWhitelisted(int uid)94     public boolean isWhitelisted(int uid) {
95         loadWhitelist();
96         return mUidPolicies.get(uid, POLICY_NONE) == POLICY_ALLOW_METERED_BACKGROUND;
97     }
98 
getWhitelistedCount()99     public int getWhitelistedCount() {
100         int count = 0;
101         loadWhitelist();
102         for (int i = 0; i < mUidPolicies.size(); i++) {
103             if (mUidPolicies.valueAt(i) == POLICY_ALLOW_METERED_BACKGROUND) {
104                 count++;
105             }
106         }
107         return count;
108     }
109 
loadWhitelist()110     private void loadWhitelist() {
111         if (mWhitelistInitialized) return;
112 
113         for (int uid : mPolicyManager.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND)) {
114             mUidPolicies.put(uid, POLICY_ALLOW_METERED_BACKGROUND);
115         }
116         mWhitelistInitialized = true;
117     }
118 
refreshBlacklist()119     public void refreshBlacklist() {
120         loadBlacklist();
121     }
122 
setIsBlacklisted(int uid, String packageName, boolean blacklisted)123     public void setIsBlacklisted(int uid, String packageName, boolean blacklisted) {
124         final int policy = blacklisted ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE;
125         mPolicyManager.setUidPolicy(uid, policy);
126         mUidPolicies.put(uid, policy);
127         if (blacklisted) {
128             mMetricsFeatureProvider.action(
129                     mContext, MetricsEvent.ACTION_DATA_SAVER_BLACKLIST, packageName);
130         }
131     }
132 
isBlacklisted(int uid)133     public boolean isBlacklisted(int uid) {
134         loadBlacklist();
135         return mUidPolicies.get(uid, POLICY_NONE) == POLICY_REJECT_METERED_BACKGROUND;
136     }
137 
loadBlacklist()138     private void loadBlacklist() {
139         if (mBlacklistInitialized) return;
140         for (int uid : mPolicyManager.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) {
141             mUidPolicies.put(uid, POLICY_REJECT_METERED_BACKGROUND);
142         }
143         mBlacklistInitialized = true;
144     }
145 
handleRestrictBackgroundChanged(boolean isDataSaving)146     private void handleRestrictBackgroundChanged(boolean isDataSaving) {
147         for (int i = 0; i < mListeners.size(); i++) {
148             mListeners.get(i).onDataSaverChanged(isDataSaving);
149         }
150     }
151 
handleWhitelistChanged(int uid, boolean isWhitelisted)152     private void handleWhitelistChanged(int uid, boolean isWhitelisted) {
153         for (int i = 0; i < mListeners.size(); i++) {
154             mListeners.get(i).onWhitelistStatusChanged(uid, isWhitelisted);
155         }
156     }
157 
handleBlacklistChanged(int uid, boolean isBlacklisted)158     private void handleBlacklistChanged(int uid, boolean isBlacklisted) {
159         for (int i = 0; i < mListeners.size(); i++) {
160             mListeners.get(i).onBlacklistStatusChanged(uid, isBlacklisted);
161         }
162     }
163 
handleUidPoliciesChanged(int uid, int newPolicy)164     private void handleUidPoliciesChanged(int uid, int newPolicy) {
165         loadWhitelist();
166         loadBlacklist();
167 
168         final int oldPolicy = mUidPolicies.get(uid, POLICY_NONE);
169         if (newPolicy == POLICY_NONE) {
170             mUidPolicies.delete(uid);
171         } else {
172             mUidPolicies.put(uid, newPolicy);
173         }
174 
175         final boolean wasWhitelisted = oldPolicy == POLICY_ALLOW_METERED_BACKGROUND;
176         final boolean wasBlacklisted = oldPolicy == POLICY_REJECT_METERED_BACKGROUND;
177         final boolean isWhitelisted = newPolicy == POLICY_ALLOW_METERED_BACKGROUND;
178         final boolean isBlacklisted = newPolicy == POLICY_REJECT_METERED_BACKGROUND;
179 
180         if (wasWhitelisted != isWhitelisted) {
181             handleWhitelistChanged(uid, isWhitelisted);
182         }
183 
184         if (wasBlacklisted != isBlacklisted) {
185             handleBlacklistChanged(uid, isBlacklisted);
186         }
187 
188     }
189 
190     private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
191         @Override
192         public void onUidRulesChanged(int uid, int uidRules) throws RemoteException {
193         }
194 
195         @Override
196         public void onUidPoliciesChanged(final int uid, final int uidPolicies) {
197             mHandler.post(new Runnable() {
198                 @Override
199                 public void run() {
200                     handleUidPoliciesChanged(uid, uidPolicies);
201                 }
202             });
203         }
204 
205         @Override
206         public void onMeteredIfacesChanged(String[] strings) throws RemoteException {
207         }
208 
209         @Override
210         public void onRestrictBackgroundChanged(final boolean isDataSaving) throws RemoteException {
211             mHandler.post(new Runnable() {
212                 @Override
213                 public void run() {
214                     handleRestrictBackgroundChanged(isDataSaving);
215                 }
216             });
217         }
218     };
219 
220     public interface Listener {
onDataSaverChanged(boolean isDataSaving)221         void onDataSaverChanged(boolean isDataSaving);
onWhitelistStatusChanged(int uid, boolean isWhitelisted)222         void onWhitelistStatusChanged(int uid, boolean isWhitelisted);
onBlacklistStatusChanged(int uid, boolean isBlacklisted)223         void onBlacklistStatusChanged(int uid, boolean isBlacklisted);
224     }
225 }
226