1 /* 2 * Copyright (C) 2011 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 17 package com.android.settings.net; 18 19 import static android.net.NetworkPolicy.CYCLE_NONE; 20 import static android.net.NetworkPolicy.LIMIT_DISABLED; 21 import static android.net.NetworkPolicy.SNOOZE_NEVER; 22 import static android.net.NetworkPolicy.WARNING_DISABLED; 23 import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER; 24 import static android.net.NetworkTemplate.MATCH_MOBILE_4G; 25 import static android.net.NetworkTemplate.MATCH_WIFI; 26 import static android.net.NetworkTemplate.buildTemplateMobile3gLower; 27 import static android.net.NetworkTemplate.buildTemplateMobile4g; 28 import static android.net.NetworkTemplate.buildTemplateMobileAll; 29 import static com.android.internal.util.Preconditions.checkNotNull; 30 31 import android.net.NetworkPolicy; 32 import android.net.NetworkPolicyManager; 33 import android.net.NetworkTemplate; 34 import android.net.wifi.WifiInfo; 35 import android.os.AsyncTask; 36 import android.text.TextUtils; 37 import android.text.format.Time; 38 39 import com.android.internal.util.Objects; 40 import com.google.android.collect.Lists; 41 import com.google.android.collect.Sets; 42 43 import java.util.ArrayList; 44 import java.util.HashSet; 45 46 /** 47 * Utility class to modify list of {@link NetworkPolicy}. Specifically knows 48 * about which policies can coexist. This editor offers thread safety when 49 * talking with {@link NetworkPolicyManager}. 50 */ 51 public class NetworkPolicyEditor { 52 // TODO: be more robust when missing policies from service 53 54 public static final boolean ENABLE_SPLIT_POLICIES = false; 55 56 private NetworkPolicyManager mPolicyManager; 57 private ArrayList<NetworkPolicy> mPolicies = Lists.newArrayList(); 58 59 public NetworkPolicyEditor(NetworkPolicyManager policyManager) { 60 mPolicyManager = checkNotNull(policyManager); 61 } 62 63 public void read() { 64 final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies(); 65 66 boolean modified = false; 67 mPolicies.clear(); 68 for (NetworkPolicy policy : policies) { 69 // TODO: find better place to clamp these 70 if (policy.limitBytes < -1) { 71 policy.limitBytes = LIMIT_DISABLED; 72 modified = true; 73 } 74 if (policy.warningBytes < -1) { 75 policy.warningBytes = WARNING_DISABLED; 76 modified = true; 77 } 78 79 mPolicies.add(policy); 80 } 81 82 // force combine any split policies when disabled 83 if (!ENABLE_SPLIT_POLICIES) { 84 modified |= forceMobilePolicyCombined(); 85 } 86 87 // when we cleaned policies above, write back changes 88 if (modified) writeAsync(); 89 } 90 91 public void writeAsync() { 92 // TODO: consider making more robust by passing through service 93 final NetworkPolicy[] policies = mPolicies.toArray(new NetworkPolicy[mPolicies.size()]); 94 new AsyncTask<Void, Void, Void>() { 95 @Override 96 protected Void doInBackground(Void... params) { 97 write(policies); 98 return null; 99 } 100 }.execute(); 101 } 102 103 public void write(NetworkPolicy[] policies) { 104 mPolicyManager.setNetworkPolicies(policies); 105 } 106 107 public boolean hasLimitedPolicy(NetworkTemplate template) { 108 final NetworkPolicy policy = getPolicy(template); 109 return policy != null && policy.limitBytes != LIMIT_DISABLED; 110 } 111 112 public NetworkPolicy getOrCreatePolicy(NetworkTemplate template) { 113 NetworkPolicy policy = getPolicy(template); 114 if (policy == null) { 115 policy = buildDefaultPolicy(template); 116 mPolicies.add(policy); 117 } 118 return policy; 119 } 120 121 public NetworkPolicy getPolicy(NetworkTemplate template) { 122 for (NetworkPolicy policy : mPolicies) { 123 if (policy.template.equals(template)) { 124 return policy; 125 } 126 } 127 return null; 128 } 129 130 public NetworkPolicy getPolicyMaybeUnquoted(NetworkTemplate template) { 131 NetworkPolicy policy = getPolicy(template); 132 if (policy != null) { 133 return policy; 134 } else { 135 return getPolicy(buildUnquotedNetworkTemplate(template)); 136 } 137 } 138 139 @Deprecated 140 private static NetworkPolicy buildDefaultPolicy(NetworkTemplate template) { 141 // TODO: move this into framework to share with NetworkPolicyManagerService 142 final int cycleDay; 143 final String cycleTimezone; 144 final boolean metered; 145 146 if (template.getMatchRule() == MATCH_WIFI) { 147 cycleDay = CYCLE_NONE; 148 cycleTimezone = Time.TIMEZONE_UTC; 149 metered = false; 150 } else { 151 final Time time = new Time(); 152 time.setToNow(); 153 cycleDay = time.monthDay; 154 cycleTimezone = time.timezone; 155 metered = true; 156 } 157 158 return new NetworkPolicy(template, cycleDay, cycleTimezone, WARNING_DISABLED, 159 LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, metered, true); 160 } 161 162 public int getPolicyCycleDay(NetworkTemplate template) { 163 return getPolicy(template).cycleDay; 164 } 165 166 public void setPolicyCycleDay(NetworkTemplate template, int cycleDay, String cycleTimezone) { 167 final NetworkPolicy policy = getOrCreatePolicy(template); 168 policy.cycleDay = cycleDay; 169 policy.cycleTimezone = cycleTimezone; 170 policy.inferred = false; 171 policy.clearSnooze(); 172 writeAsync(); 173 } 174 175 public long getPolicyWarningBytes(NetworkTemplate template) { 176 return getPolicy(template).warningBytes; 177 } 178 179 public void setPolicyWarningBytes(NetworkTemplate template, long warningBytes) { 180 final NetworkPolicy policy = getOrCreatePolicy(template); 181 policy.warningBytes = warningBytes; 182 policy.inferred = false; 183 policy.clearSnooze(); 184 writeAsync(); 185 } 186 187 public long getPolicyLimitBytes(NetworkTemplate template) { 188 return getPolicy(template).limitBytes; 189 } 190 191 public void setPolicyLimitBytes(NetworkTemplate template, long limitBytes) { 192 final NetworkPolicy policy = getOrCreatePolicy(template); 193 policy.limitBytes = limitBytes; 194 policy.inferred = false; 195 policy.clearSnooze(); 196 writeAsync(); 197 } 198 199 public boolean getPolicyMetered(NetworkTemplate template) { 200 NetworkPolicy policy = getPolicy(template); 201 if (policy != null) { 202 return policy.metered; 203 } else { 204 return false; 205 } 206 } 207 208 public void setPolicyMetered(NetworkTemplate template, boolean metered) { 209 boolean modified = false; 210 211 NetworkPolicy policy = getPolicy(template); 212 if (metered) { 213 if (policy == null) { 214 policy = buildDefaultPolicy(template); 215 policy.metered = true; 216 policy.inferred = false; 217 mPolicies.add(policy); 218 modified = true; 219 } else if (!policy.metered) { 220 policy.metered = true; 221 policy.inferred = false; 222 modified = true; 223 } 224 225 } else { 226 if (policy == null) { 227 // ignore when policy doesn't exist 228 } else if (policy.metered) { 229 policy.metered = false; 230 policy.inferred = false; 231 modified = true; 232 } 233 } 234 235 // Remove legacy unquoted policies while we're here 236 final NetworkTemplate unquoted = buildUnquotedNetworkTemplate(template); 237 final NetworkPolicy unquotedPolicy = getPolicy(unquoted); 238 if (unquotedPolicy != null) { 239 mPolicies.remove(unquotedPolicy); 240 modified = true; 241 } 242 243 if (modified) writeAsync(); 244 } 245 246 /** 247 * Remove any split {@link NetworkPolicy}. 248 */ 249 private boolean forceMobilePolicyCombined() { 250 final HashSet<String> subscriberIds = Sets.newHashSet(); 251 for (NetworkPolicy policy : mPolicies) { 252 subscriberIds.add(policy.template.getSubscriberId()); 253 } 254 255 boolean modified = false; 256 for (String subscriberId : subscriberIds) { 257 modified |= setMobilePolicySplitInternal(subscriberId, false); 258 } 259 return modified; 260 } 261 262 @Deprecated 263 public boolean isMobilePolicySplit(String subscriberId) { 264 boolean has3g = false; 265 boolean has4g = false; 266 for (NetworkPolicy policy : mPolicies) { 267 final NetworkTemplate template = policy.template; 268 if (Objects.equal(subscriberId, template.getSubscriberId())) { 269 switch (template.getMatchRule()) { 270 case MATCH_MOBILE_3G_LOWER: 271 has3g = true; 272 break; 273 case MATCH_MOBILE_4G: 274 has4g = true; 275 break; 276 } 277 } 278 } 279 return has3g && has4g; 280 } 281 282 @Deprecated 283 public void setMobilePolicySplit(String subscriberId, boolean split) { 284 if (setMobilePolicySplitInternal(subscriberId, split)) { 285 writeAsync(); 286 } 287 } 288 289 /** 290 * Mutate {@link NetworkPolicy} for given subscriber, combining or splitting 291 * the policy as requested. 292 * 293 * @return {@code true} when any {@link NetworkPolicy} was mutated. 294 */ 295 @Deprecated 296 private boolean setMobilePolicySplitInternal(String subscriberId, boolean split) { 297 final boolean beforeSplit = isMobilePolicySplit(subscriberId); 298 299 final NetworkTemplate template3g = buildTemplateMobile3gLower(subscriberId); 300 final NetworkTemplate template4g = buildTemplateMobile4g(subscriberId); 301 final NetworkTemplate templateAll = buildTemplateMobileAll(subscriberId); 302 303 if (split == beforeSplit) { 304 // already in requested state; skip 305 return false; 306 307 } else if (beforeSplit && !split) { 308 // combine, picking most restrictive policy 309 final NetworkPolicy policy3g = getPolicy(template3g); 310 final NetworkPolicy policy4g = getPolicy(template4g); 311 312 final NetworkPolicy restrictive = policy3g.compareTo(policy4g) < 0 ? policy3g 313 : policy4g; 314 mPolicies.remove(policy3g); 315 mPolicies.remove(policy4g); 316 mPolicies.add(new NetworkPolicy(templateAll, restrictive.cycleDay, 317 restrictive.cycleTimezone, restrictive.warningBytes, restrictive.limitBytes, 318 SNOOZE_NEVER, SNOOZE_NEVER, restrictive.metered, restrictive.inferred)); 319 return true; 320 321 } else if (!beforeSplit && split) { 322 // duplicate existing policy into two rules 323 final NetworkPolicy policyAll = getPolicy(templateAll); 324 mPolicies.remove(policyAll); 325 mPolicies.add(new NetworkPolicy(template3g, policyAll.cycleDay, policyAll.cycleTimezone, 326 policyAll.warningBytes, policyAll.limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, 327 policyAll.metered, policyAll.inferred)); 328 mPolicies.add(new NetworkPolicy(template4g, policyAll.cycleDay, policyAll.cycleTimezone, 329 policyAll.warningBytes, policyAll.limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, 330 policyAll.metered, policyAll.inferred)); 331 return true; 332 } else { 333 return false; 334 } 335 } 336 337 /** 338 * Build a revised {@link NetworkTemplate} that matches the same rule, but 339 * with an unquoted {@link NetworkTemplate#getNetworkId()}. Used to work 340 * around legacy bugs. 341 */ 342 private static NetworkTemplate buildUnquotedNetworkTemplate(NetworkTemplate template) { 343 if (template == null) return null; 344 final String networkId = template.getNetworkId(); 345 final String strippedNetworkId = WifiInfo.removeDoubleQuotes(networkId); 346 if (!TextUtils.equals(strippedNetworkId, networkId)) { 347 return new NetworkTemplate( 348 template.getMatchRule(), template.getSubscriberId(), strippedNetworkId); 349 } else { 350 return null; 351 } 352 } 353 } 354