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.google.android.collect.Lists; 40 import com.google.android.collect.Sets; 41 42 import java.util.ArrayList; 43 import java.util.HashSet; 44 import java.util.Objects; 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 final NetworkPolicy policy = getPolicy(template); 164 return (policy != null) ? policy.cycleDay : -1; 165 } 166 167 public void setPolicyCycleDay(NetworkTemplate template, int cycleDay, String cycleTimezone) { 168 final NetworkPolicy policy = getOrCreatePolicy(template); 169 policy.cycleDay = cycleDay; 170 policy.cycleTimezone = cycleTimezone; 171 policy.inferred = false; 172 policy.clearSnooze(); 173 writeAsync(); 174 } 175 176 public long getPolicyWarningBytes(NetworkTemplate template) { 177 final NetworkPolicy policy = getPolicy(template); 178 return (policy != null) ? policy.warningBytes : WARNING_DISABLED; 179 } 180 181 public void setPolicyWarningBytes(NetworkTemplate template, long warningBytes) { 182 final NetworkPolicy policy = getOrCreatePolicy(template); 183 policy.warningBytes = warningBytes; 184 policy.inferred = false; 185 policy.clearSnooze(); 186 writeAsync(); 187 } 188 189 public long getPolicyLimitBytes(NetworkTemplate template) { 190 final NetworkPolicy policy = getPolicy(template); 191 return (policy != null) ? policy.limitBytes : LIMIT_DISABLED; 192 } 193 194 public void setPolicyLimitBytes(NetworkTemplate template, long limitBytes) { 195 final NetworkPolicy policy = getOrCreatePolicy(template); 196 policy.limitBytes = limitBytes; 197 policy.inferred = false; 198 policy.clearSnooze(); 199 writeAsync(); 200 } 201 202 public boolean getPolicyMetered(NetworkTemplate template) { 203 NetworkPolicy policy = getPolicy(template); 204 if (policy != null) { 205 return policy.metered; 206 } else { 207 return false; 208 } 209 } 210 211 public void setPolicyMetered(NetworkTemplate template, boolean metered) { 212 boolean modified = false; 213 214 NetworkPolicy policy = getPolicy(template); 215 if (metered) { 216 if (policy == null) { 217 policy = buildDefaultPolicy(template); 218 policy.metered = true; 219 policy.inferred = false; 220 mPolicies.add(policy); 221 modified = true; 222 } else if (!policy.metered) { 223 policy.metered = true; 224 policy.inferred = false; 225 modified = true; 226 } 227 228 } else { 229 if (policy == null) { 230 // ignore when policy doesn't exist 231 } else if (policy.metered) { 232 policy.metered = false; 233 policy.inferred = false; 234 modified = true; 235 } 236 } 237 238 // Remove legacy unquoted policies while we're here 239 final NetworkTemplate unquoted = buildUnquotedNetworkTemplate(template); 240 final NetworkPolicy unquotedPolicy = getPolicy(unquoted); 241 if (unquotedPolicy != null) { 242 mPolicies.remove(unquotedPolicy); 243 modified = true; 244 } 245 246 if (modified) writeAsync(); 247 } 248 249 /** 250 * Remove any split {@link NetworkPolicy}. 251 */ 252 private boolean forceMobilePolicyCombined() { 253 final HashSet<String> subscriberIds = Sets.newHashSet(); 254 for (NetworkPolicy policy : mPolicies) { 255 subscriberIds.add(policy.template.getSubscriberId()); 256 } 257 258 boolean modified = false; 259 for (String subscriberId : subscriberIds) { 260 modified |= setMobilePolicySplitInternal(subscriberId, false); 261 } 262 return modified; 263 } 264 265 @Deprecated 266 public boolean isMobilePolicySplit(String subscriberId) { 267 boolean has3g = false; 268 boolean has4g = false; 269 for (NetworkPolicy policy : mPolicies) { 270 final NetworkTemplate template = policy.template; 271 if (Objects.equals(subscriberId, template.getSubscriberId())) { 272 switch (template.getMatchRule()) { 273 case MATCH_MOBILE_3G_LOWER: 274 has3g = true; 275 break; 276 case MATCH_MOBILE_4G: 277 has4g = true; 278 break; 279 } 280 } 281 } 282 return has3g && has4g; 283 } 284 285 @Deprecated 286 public void setMobilePolicySplit(String subscriberId, boolean split) { 287 if (setMobilePolicySplitInternal(subscriberId, split)) { 288 writeAsync(); 289 } 290 } 291 292 /** 293 * Mutate {@link NetworkPolicy} for given subscriber, combining or splitting 294 * the policy as requested. 295 * 296 * @return {@code true} when any {@link NetworkPolicy} was mutated. 297 */ 298 @Deprecated 299 private boolean setMobilePolicySplitInternal(String subscriberId, boolean split) { 300 final boolean beforeSplit = isMobilePolicySplit(subscriberId); 301 302 final NetworkTemplate template3g = buildTemplateMobile3gLower(subscriberId); 303 final NetworkTemplate template4g = buildTemplateMobile4g(subscriberId); 304 final NetworkTemplate templateAll = buildTemplateMobileAll(subscriberId); 305 306 if (split == beforeSplit) { 307 // already in requested state; skip 308 return false; 309 310 } else if (beforeSplit && !split) { 311 // combine, picking most restrictive policy 312 final NetworkPolicy policy3g = getPolicy(template3g); 313 final NetworkPolicy policy4g = getPolicy(template4g); 314 315 NetworkPolicy restrictive = null; 316 if ((policy3g == null) && (policy4g == null)) { 317 return false; 318 } else if (policy3g == null) { 319 restrictive = policy4g; 320 } else if (policy4g == null) { 321 restrictive = policy3g; 322 } else { 323 restrictive = policy3g.compareTo(policy4g) < 0 ? policy3g : policy4g; 324 } 325 mPolicies.remove(policy3g); 326 mPolicies.remove(policy4g); 327 mPolicies.add(new NetworkPolicy(templateAll, restrictive.cycleDay, 328 restrictive.cycleTimezone, restrictive.warningBytes, restrictive.limitBytes, 329 SNOOZE_NEVER, SNOOZE_NEVER, restrictive.metered, restrictive.inferred)); 330 return true; 331 332 } else if (!beforeSplit && split) { 333 // duplicate existing policy into two rules 334 final NetworkPolicy policyAll = getPolicy(templateAll); 335 if (policyAll == null) { 336 return false; 337 } 338 mPolicies.remove(policyAll); 339 mPolicies.add(new NetworkPolicy(template3g, policyAll.cycleDay, policyAll.cycleTimezone, 340 policyAll.warningBytes, policyAll.limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, 341 policyAll.metered, policyAll.inferred)); 342 mPolicies.add(new NetworkPolicy(template4g, policyAll.cycleDay, policyAll.cycleTimezone, 343 policyAll.warningBytes, policyAll.limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, 344 policyAll.metered, policyAll.inferred)); 345 return true; 346 } else { 347 return false; 348 } 349 } 350 351 /** 352 * Build a revised {@link NetworkTemplate} that matches the same rule, but 353 * with an unquoted {@link NetworkTemplate#getNetworkId()}. Used to work 354 * around legacy bugs. 355 */ 356 private static NetworkTemplate buildUnquotedNetworkTemplate(NetworkTemplate template) { 357 if (template == null) return null; 358 final String networkId = template.getNetworkId(); 359 final String strippedNetworkId = WifiInfo.removeDoubleQuotes(networkId); 360 if (!TextUtils.equals(strippedNetworkId, networkId)) { 361 return new NetworkTemplate( 362 template.getMatchRule(), template.getSubscriberId(), strippedNetworkId); 363 } else { 364 return null; 365 } 366 } 367 } 368