Home | History | Annotate | Download | only in net
      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