Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2016 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.server.net;
     18 
     19 import static android.net.NetworkPolicyManager.POLICY_NONE;
     20 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
     21 import static android.net.wifi.WifiInfo.removeDoubleQuotes;
     22 import static com.android.server.net.NetworkPolicyManagerService.newWifiPolicy;
     23 import static com.android.server.net.NetworkPolicyManagerService.TAG;
     24 
     25 import java.io.PrintWriter;
     26 import java.util.ArrayList;
     27 import java.util.HashSet;
     28 import java.util.List;
     29 import java.util.Set;
     30 
     31 import android.content.Context;
     32 import android.net.INetworkPolicyManager;
     33 import android.net.NetworkPolicy;
     34 import android.net.NetworkTemplate;
     35 import android.net.wifi.WifiConfiguration;
     36 import android.net.wifi.WifiManager;
     37 import android.os.RemoteException;
     38 import android.os.ShellCommand;
     39 import android.util.Log;
     40 
     41 class NetworkPolicyManagerShellCommand extends ShellCommand {
     42 
     43     private final INetworkPolicyManager mInterface;
     44     private final WifiManager mWifiManager;
     45 
     46     NetworkPolicyManagerShellCommand(Context context, INetworkPolicyManager service) {
     47         mInterface = service;
     48         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
     49     }
     50 
     51     @Override
     52     public int onCommand(String cmd) {
     53         if (cmd == null) {
     54             return handleDefaultCommands(cmd);
     55         }
     56         final PrintWriter pw = getOutPrintWriter();
     57         try {
     58             switch(cmd) {
     59                 case "get":
     60                     return runGet();
     61                 case "set":
     62                     return runSet();
     63                 case "list":
     64                     return runList();
     65                 case "add":
     66                     return runAdd();
     67                 case "remove":
     68                     return runRemove();
     69                 default:
     70                     return handleDefaultCommands(cmd);
     71             }
     72         } catch (RemoteException e) {
     73             pw.println("Remote exception: " + e);
     74         }
     75         return -1;
     76     }
     77 
     78     @Override
     79     public void onHelp() {
     80         final PrintWriter pw = getOutPrintWriter();
     81         pw.println("Network policy manager (netpolicy) commands:");
     82         pw.println("  help");
     83         pw.println("    Print this help text.");
     84         pw.println("");
     85         pw.println("  add restrict-background-whitelist UID");
     86         pw.println("    Adds a UID to the whitelist for restrict background usage.");
     87         pw.println("  add restrict-background-blacklist UID");
     88         pw.println("    Adds a UID to the blacklist for restrict background usage.");
     89         pw.println("  get restrict-background");
     90         pw.println("    Gets the global restrict background usage status.");
     91         pw.println("  list wifi-networks [BOOLEAN]");
     92         pw.println("    Lists all saved wifi networks and whether they are metered or not.");
     93         pw.println("    If a boolean argument is passed, filters just the metered (or unmetered)");
     94         pw.println("    networks.");
     95         pw.println("  list restrict-background-whitelist");
     96         pw.println("    Lists UIDs that are whitelisted for restrict background usage.");
     97         pw.println("  list restrict-background-blacklist");
     98         pw.println("    Lists UIDs that are blacklisted for restrict background usage.");
     99         pw.println("  remove restrict-background-whitelist UID");
    100         pw.println("    Removes a UID from the whitelist for restrict background usage.");
    101         pw.println("  remove restrict-background-blacklist UID");
    102         pw.println("    Removes a UID from the blacklist for restrict background usage.");
    103         pw.println("  set metered-network ID BOOLEAN");
    104         pw.println("    Toggles whether the given wi-fi network is metered.");
    105         pw.println("  set restrict-background BOOLEAN");
    106         pw.println("    Sets the global restrict background usage status.");
    107     }
    108 
    109     private int runGet() throws RemoteException {
    110         final PrintWriter pw = getOutPrintWriter();
    111         final String type = getNextArg();
    112         if (type == null) {
    113             pw.println("Error: didn't specify type of data to get");
    114             return -1;
    115         }
    116         switch(type) {
    117             case "restrict-background":
    118                 return getRestrictBackground();
    119         }
    120         pw.println("Error: unknown get type '" + type + "'");
    121         return -1;
    122     }
    123 
    124     private int runSet() throws RemoteException  {
    125         final PrintWriter pw = getOutPrintWriter();
    126         final String type = getNextArg();
    127         if (type == null) {
    128             pw.println("Error: didn't specify type of data to set");
    129             return -1;
    130         }
    131         switch(type) {
    132             case "metered-network":
    133                 return setMeteredWifiNetwork();
    134             case "restrict-background":
    135                 return setRestrictBackground();
    136         }
    137         pw.println("Error: unknown set type '" + type + "'");
    138         return -1;
    139     }
    140 
    141     private int runList() throws RemoteException  {
    142         final PrintWriter pw = getOutPrintWriter();
    143         final String type = getNextArg();
    144         if (type == null) {
    145             pw.println("Error: didn't specify type of data to list");
    146             return -1;
    147         }
    148         switch(type) {
    149             case "wifi-networks":
    150                 return listWifiNetworks();
    151             case "restrict-background-whitelist":
    152                 return listRestrictBackgroundWhitelist();
    153             case "restrict-background-blacklist":
    154                 return listRestrictBackgroundBlacklist();
    155         }
    156         pw.println("Error: unknown list type '" + type + "'");
    157         return -1;
    158     }
    159 
    160     private int runAdd() throws RemoteException  {
    161         final PrintWriter pw = getOutPrintWriter();
    162         final String type = getNextArg();
    163         if (type == null) {
    164             pw.println("Error: didn't specify type of data to add");
    165             return -1;
    166         }
    167         switch(type) {
    168             case "restrict-background-whitelist":
    169                 return addRestrictBackgroundWhitelist();
    170             case "restrict-background-blacklist":
    171                 return addRestrictBackgroundBlacklist();
    172         }
    173         pw.println("Error: unknown add type '" + type + "'");
    174         return -1;
    175     }
    176 
    177     private int runRemove() throws RemoteException {
    178         final PrintWriter pw = getOutPrintWriter();
    179         final String type = getNextArg();
    180         if (type == null) {
    181             pw.println("Error: didn't specify type of data to remove");
    182             return -1;
    183         }
    184         switch(type) {
    185             case "restrict-background-whitelist":
    186                 return removeRestrictBackgroundWhitelist();
    187             case "restrict-background-blacklist":
    188                 return removeRestrictBackgroundBlacklist();
    189         }
    190         pw.println("Error: unknown remove type '" + type + "'");
    191         return -1;
    192     }
    193 
    194     private int listRestrictBackgroundWhitelist() throws RemoteException {
    195         final PrintWriter pw = getOutPrintWriter();
    196         final int[] uids = mInterface.getRestrictBackgroundWhitelistedUids();
    197         pw.print("Restrict background whitelisted UIDs: ");
    198         if (uids.length == 0) {
    199             pw.println("none");
    200         } else {
    201             for (int i = 0; i < uids.length; i++) {
    202                 int uid = uids[i];
    203                 pw.print(uid);
    204                 pw.print(' ');
    205             }
    206         }
    207         pw.println();
    208         return 0;
    209     }
    210 
    211     private int listRestrictBackgroundBlacklist() throws RemoteException {
    212         final PrintWriter pw = getOutPrintWriter();
    213 
    214         final int[] uids = mInterface.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND);
    215         pw.print("Restrict background blacklisted UIDs: ");
    216         if (uids.length == 0) {
    217             pw.println("none");
    218         } else {
    219             for (int i = 0; i < uids.length; i++) {
    220                 int uid = uids[i];
    221                 pw.print(uid);
    222                 pw.print(' ');
    223             }
    224         }
    225         pw.println();
    226         return 0;
    227     }
    228 
    229     private int getRestrictBackground() throws RemoteException {
    230         final PrintWriter pw = getOutPrintWriter();
    231         pw.print("Restrict background status: ");
    232         pw.println(mInterface.getRestrictBackground() ? "enabled" : "disabled");
    233         return 0;
    234     }
    235 
    236     private int setRestrictBackground() throws RemoteException {
    237         final int enabled = getNextBooleanArg();
    238         if (enabled < 0) {
    239             return enabled;
    240         }
    241         mInterface.setRestrictBackground(enabled > 0);
    242         return 0;
    243     }
    244 
    245     private int addRestrictBackgroundWhitelist() throws RemoteException {
    246       final int uid = getUidFromNextArg();
    247       if (uid < 0) {
    248           return uid;
    249       }
    250       mInterface.addRestrictBackgroundWhitelistedUid(uid);
    251       return 0;
    252     }
    253 
    254     private int removeRestrictBackgroundWhitelist() throws RemoteException {
    255         final int uid = getUidFromNextArg();
    256         if (uid < 0) {
    257             return uid;
    258         }
    259         mInterface.removeRestrictBackgroundWhitelistedUid(uid);
    260         return 0;
    261     }
    262 
    263     private int addRestrictBackgroundBlacklist() throws RemoteException {
    264         final int uid = getUidFromNextArg();
    265         if (uid < 0) {
    266             return uid;
    267         }
    268         mInterface.setUidPolicy(uid, POLICY_REJECT_METERED_BACKGROUND);
    269         return 0;
    270     }
    271 
    272     private int removeRestrictBackgroundBlacklist() throws RemoteException {
    273         final int uid = getUidFromNextArg();
    274         if (uid < 0) {
    275             return uid;
    276         }
    277         mInterface.setUidPolicy(uid, POLICY_NONE);
    278         return 0;
    279     }
    280 
    281     private int listWifiNetworks() throws RemoteException {
    282         final PrintWriter pw = getOutPrintWriter();
    283         final String arg = getNextArg();
    284         final Boolean filter = arg == null ? null : Boolean.valueOf(arg);
    285         for (NetworkPolicy policy : getWifiPolicies()) {
    286             if (filter != null && filter.booleanValue() != policy.metered) {
    287                 continue;
    288             }
    289             pw.print(getNetworkId(policy));
    290             pw.print(';');
    291             pw.println(policy.metered);
    292         }
    293         return 0;
    294     }
    295 
    296     private int setMeteredWifiNetwork() throws RemoteException {
    297         final PrintWriter pw = getOutPrintWriter();
    298         final String id = getNextArg();
    299         if (id == null) {
    300             pw.println("Error: didn't specify ID");
    301             return -1;
    302         }
    303         final String arg = getNextArg();
    304         if (arg == null) {
    305             pw.println("Error: didn't specify BOOLEAN");
    306             return -1;
    307         }
    308         final boolean metered = Boolean.valueOf(arg);
    309         final NetworkPolicy[] policies = mInterface.getNetworkPolicies(null);
    310         boolean changed = false;
    311         // First try to find a policy with such id
    312         for (NetworkPolicy policy : policies) {
    313             if (policy.template.isMatchRuleMobile() || policy.metered == metered) {
    314                 continue;
    315             }
    316             final String networkId = getNetworkId(policy);
    317             if (id.equals(networkId)) {
    318                 Log.i(TAG, "Changing " + networkId + " metered status to " + metered);
    319                 policy.metered = metered;
    320                 changed = true;
    321             }
    322         }
    323         if (changed) {
    324             mInterface.setNetworkPolicies(policies);
    325             return 0;
    326         }
    327         // Policy not found: check if there is a saved wi-fi with such id.
    328         for (WifiConfiguration config : mWifiManager.getConfiguredNetworks()) {
    329             final String ssid = removeDoubleQuotes(config.SSID);
    330             if (id.equals(ssid)) {
    331                 final NetworkPolicy policy = newPolicy(ssid);
    332                 policy.metered = true;
    333                 Log.i(TAG, "Creating new policy for " + ssid + ": " + policy);
    334                 final NetworkPolicy[] newPolicies = new NetworkPolicy[policies.length + 1];
    335                 System.arraycopy(policies, 0, newPolicies, 0, policies.length);
    336                 newPolicies[newPolicies.length - 1] = policy;
    337                 mInterface.setNetworkPolicies(newPolicies);
    338             }
    339         }
    340         return 0;
    341     }
    342 
    343     private List<NetworkPolicy> getWifiPolicies() throws RemoteException {
    344         // First gets a list of saved wi-fi networks.
    345         final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
    346         final int size = configs != null ? configs.size() : 0;
    347         final Set<String> ssids = new HashSet<>(size);
    348         if (configs != null) {
    349             for (WifiConfiguration config : configs) {
    350                 ssids.add(removeDoubleQuotes(config.SSID));
    351             }
    352         }
    353 
    354         // Then gets the saved policies.
    355         final NetworkPolicy[] policies = mInterface.getNetworkPolicies(null);
    356         final List<NetworkPolicy> wifiPolicies = new ArrayList<NetworkPolicy>(policies.length);
    357         for (NetworkPolicy policy: policies) {
    358             if (!policy.template.isMatchRuleMobile()) {
    359                 wifiPolicies.add(policy);
    360                 final String netId = getNetworkId(policy);
    361                 ssids.remove(netId);
    362             }
    363         }
    364         // Finally, creates new default policies for saved WI-FIs not policied yet.
    365         for (String ssid : ssids) {
    366             final NetworkPolicy policy = newPolicy(ssid);
    367             wifiPolicies.add(policy);
    368         }
    369         return wifiPolicies;
    370     }
    371 
    372     private NetworkPolicy newPolicy(String ssid) {
    373         final NetworkTemplate template = NetworkTemplate.buildTemplateWifi(ssid);
    374         final NetworkPolicy policy = newWifiPolicy(template, false);
    375         return policy;
    376     }
    377 
    378     private String getNetworkId(NetworkPolicy policy) {
    379         return removeDoubleQuotes(policy.template.getNetworkId());
    380     }
    381 
    382     private int getNextBooleanArg() {
    383         final PrintWriter pw = getOutPrintWriter();
    384         final String arg = getNextArg();
    385         if (arg == null) {
    386             pw.println("Error: didn't specify BOOLEAN");
    387             return -1;
    388         }
    389         return Boolean.valueOf(arg) ? 1 : 0;
    390     }
    391 
    392     private int getUidFromNextArg() {
    393         final PrintWriter pw = getOutPrintWriter();
    394         final String arg = getNextArg();
    395         if (arg == null) {
    396             pw.println("Error: didn't specify UID");
    397             return -1;
    398         }
    399         try {
    400             return Integer.parseInt(arg);
    401         } catch (NumberFormatException e) {
    402             pw.println("Error: UID (" + arg + ") should be a number");
    403             return -2;
    404         }
    405     }
    406 }
    407