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 static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
18 import static android.net.NetworkPolicyManager.POLICY_NONE;
19 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
20 
21 import android.app.settings.SettingsEnums;
22 import android.content.Context;
23 import android.net.NetworkPolicyManager;
24 import android.util.SparseIntArray;
25 
26 import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
27 import com.android.settings.overlay.FeatureFactory;
28 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
29 import com.android.settingslib.utils.ThreadUtils;
30 
31 import org.jetbrains.annotations.NotNull;
32 
33 import java.util.ArrayList;
34 
35 public class DataSaverBackend {
36 
37     private static final String TAG = "DataSaverBackend";
38 
39     private final Context mContext;
40     private final MetricsFeatureProvider mMetricsFeatureProvider;
41 
42     private final NetworkPolicyManager mPolicyManager;
43     private final DynamicDenylistManager mDynamicDenylistManager;
44     private final ArrayList<Listener> mListeners = new ArrayList<>();
45     private SparseIntArray mUidPolicies = new SparseIntArray();
46     private boolean mAllowlistInitialized;
47     private boolean mDenylistInitialized;
48 
49     // TODO: Staticize into only one.
DataSaverBackend(@otNull Context context)50     public DataSaverBackend(@NotNull Context context) {
51         // TODO(b/246537614):Use fragment context to DataSaverBackend class will caused memory leak
52         mContext = context.getApplicationContext();
53         mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
54         mPolicyManager = NetworkPolicyManager.from(mContext);
55         mDynamicDenylistManager = DynamicDenylistManager.getInstance(mContext);
56     }
57 
addListener(Listener listener)58     public void addListener(Listener listener) {
59         mListeners.add(listener);
60         if (mListeners.size() == 1) {
61             mPolicyManager.registerListener(mPolicyListener);
62         }
63         listener.onDataSaverChanged(isDataSaverEnabled());
64     }
65 
remListener(Listener listener)66     public void remListener(Listener listener) {
67         mListeners.remove(listener);
68         if (mListeners.size() == 0) {
69             mPolicyManager.unregisterListener(mPolicyListener);
70         }
71     }
72 
isDataSaverEnabled()73     public boolean isDataSaverEnabled() {
74         return mPolicyManager.getRestrictBackground();
75     }
76 
setDataSaverEnabled(boolean enabled)77     public void setDataSaverEnabled(boolean enabled) {
78         mPolicyManager.setRestrictBackground(enabled);
79         mMetricsFeatureProvider.action(
80                 mContext, SettingsEnums.ACTION_DATA_SAVER_MODE, enabled ? 1 : 0);
81     }
82 
refreshAllowlist()83     public void refreshAllowlist() {
84         loadAllowlist();
85     }
86 
setIsAllowlisted(int uid, String packageName, boolean allowlisted)87     public void setIsAllowlisted(int uid, String packageName, boolean allowlisted) {
88         final int policy = allowlisted ? POLICY_ALLOW_METERED_BACKGROUND : POLICY_NONE;
89         mDynamicDenylistManager.setUidPolicyLocked(uid, policy);
90         mUidPolicies.put(uid, policy);
91         if (allowlisted) {
92             mMetricsFeatureProvider.action(
93                     mContext, SettingsEnums.ACTION_DATA_SAVER_WHITELIST, packageName);
94         }
95     }
96 
isAllowlisted(int uid)97     public boolean isAllowlisted(int uid) {
98         loadAllowlist();
99         return mUidPolicies.get(uid, POLICY_NONE) == POLICY_ALLOW_METERED_BACKGROUND;
100     }
101 
loadAllowlist()102     private void loadAllowlist() {
103         if (mAllowlistInitialized) {
104             return;
105         }
106 
107         for (int uid : mPolicyManager.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND)) {
108             mUidPolicies.put(uid, POLICY_ALLOW_METERED_BACKGROUND);
109         }
110         mAllowlistInitialized = true;
111     }
112 
refreshDenylist()113     public void refreshDenylist() {
114         loadDenylist();
115     }
116 
setIsDenylisted(int uid, String packageName, boolean denylisted)117     public void setIsDenylisted(int uid, String packageName, boolean denylisted) {
118         final int policy = denylisted ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE;
119         mDynamicDenylistManager.setUidPolicyLocked(uid, policy);
120         mUidPolicies.put(uid, policy);
121         if (denylisted) {
122             mMetricsFeatureProvider.action(
123                     mContext, SettingsEnums.ACTION_DATA_SAVER_BLACKLIST, packageName);
124         }
125     }
126 
isDenylisted(int uid)127     public boolean isDenylisted(int uid) {
128         loadDenylist();
129         return mUidPolicies.get(uid, POLICY_NONE) == POLICY_REJECT_METERED_BACKGROUND
130                 && mDynamicDenylistManager.isInManualDenylist(uid);
131     }
132 
loadDenylist()133     private void loadDenylist() {
134         if (mDenylistInitialized) {
135             return;
136         }
137         for (int uid : mPolicyManager.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) {
138             mUidPolicies.put(uid, POLICY_REJECT_METERED_BACKGROUND);
139         }
140         mDenylistInitialized = true;
141     }
142 
handleRestrictBackgroundChanged(boolean isDataSaving)143     private void handleRestrictBackgroundChanged(boolean isDataSaving) {
144         for (int i = 0; i < mListeners.size(); i++) {
145             mListeners.get(i).onDataSaverChanged(isDataSaving);
146         }
147     }
148 
handleAllowlistChanged(int uid, boolean isAllowlisted)149     private void handleAllowlistChanged(int uid, boolean isAllowlisted) {
150         for (int i = 0; i < mListeners.size(); i++) {
151             mListeners.get(i).onAllowlistStatusChanged(uid, isAllowlisted);
152         }
153     }
154 
handleDenylistChanged(int uid, boolean isDenylisted)155     private void handleDenylistChanged(int uid, boolean isDenylisted) {
156         for (int i = 0; i < mListeners.size(); i++) {
157             mListeners.get(i).onDenylistStatusChanged(uid, isDenylisted);
158         }
159     }
160 
handleUidPoliciesChanged(int uid, int newPolicy)161     private void handleUidPoliciesChanged(int uid, int newPolicy) {
162         loadAllowlist();
163         loadDenylist();
164 
165         final int oldPolicy = mUidPolicies.get(uid, POLICY_NONE);
166         if (newPolicy == POLICY_NONE) {
167             mUidPolicies.delete(uid);
168         } else {
169             mUidPolicies.put(uid, newPolicy);
170         }
171 
172         final boolean wasAllowlisted = oldPolicy == POLICY_ALLOW_METERED_BACKGROUND;
173         final boolean wasDenylisted = oldPolicy == POLICY_REJECT_METERED_BACKGROUND;
174         final boolean isAllowlisted = newPolicy == POLICY_ALLOW_METERED_BACKGROUND;
175         final boolean isDenylisted = newPolicy == POLICY_REJECT_METERED_BACKGROUND;
176 
177         if (wasAllowlisted != isAllowlisted) {
178             handleAllowlistChanged(uid, isAllowlisted);
179         }
180 
181         if (wasDenylisted != isDenylisted) {
182             handleDenylistChanged(uid, isDenylisted);
183         }
184 
185     }
186 
187     private final NetworkPolicyManager.Listener mPolicyListener =
188             new NetworkPolicyManager.Listener() {
189         @Override
190         public void onUidPoliciesChanged(final int uid, final int uidPolicies) {
191             ThreadUtils.postOnMainThread(() -> handleUidPoliciesChanged(uid, uidPolicies));
192         }
193 
194         @Override
195         public void onRestrictBackgroundChanged(final boolean isDataSaving) {
196             ThreadUtils.postOnMainThread(() -> handleRestrictBackgroundChanged(isDataSaving));
197         }
198     };
199 
200     public interface Listener {
onDataSaverChanged(boolean isDataSaving)201         void onDataSaverChanged(boolean isDataSaving);
202 
203         /** This is called when allow list status is changed. */
onAllowlistStatusChanged(int uid, boolean isAllowlisted)204         default void onAllowlistStatusChanged(int uid, boolean isAllowlisted) {}
205 
206         /** This is called when deny list status is changed. */
onDenylistStatusChanged(int uid, boolean isDenylisted)207         default void onDenylistStatusChanged(int uid, boolean isDenylisted) {}
208     }
209 }
210