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