content: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.server.net;
  
  import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
  import static android.net.NetworkPolicyManager.POLICY_NONE;
  import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
  
  import android.content.Context;
  import android.net.NetworkPolicyManager;
  import android.net.wifi.WifiConfiguration;
  import android.net.wifi.WifiManager;
  import android.os.RemoteException;
  import android.os.ShellCommand;
  
  import java.io.PrintWriter;
  import java.util.List;
  
  class NetworkPolicyManagerShellCommand extends ShellCommand {
  
      private final NetworkPolicyManagerService mInterface;
      private final WifiManager mWifiManager;
  
      NetworkPolicyManagerShellCommand(Context context, NetworkPolicyManagerService service) {
          mInterface = service;
          mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
      }
  
      @Override
      public int onCommand(String cmd) {
          if (cmd == null) {
              return handleDefaultCommands(cmd);
          }
          final PrintWriter pw = getOutPrintWriter();
          try {
              switch(cmd) {
                  case "get":
                      return runGet();
                  case "set":
                      return runSet();
                  case "list":
                      return runList();
                  case "add":
                      return runAdd();
                  case "remove":
                      return runRemove();
                  default:
                      return handleDefaultCommands(cmd);
              }
          } catch (RemoteException e) {
              pw.println("Remote exception: " + e);
          }
          return -1;
      }
  
      @Override
      public void onHelp() {
          final PrintWriter pw = getOutPrintWriter();
          pw.println("Network policy manager (netpolicy) commands:");
          pw.println("  help");
          pw.println("    Print this help text.");
          pw.println("");
          pw.println("  add restrict-background-whitelist UID");
          pw.println("    Adds a UID to the whitelist for restrict background usage.");
          pw.println("  add restrict-background-blacklist UID");
          pw.println("    Adds a UID to the blacklist for restrict background usage.");
          pw.println("  add app-idle-whitelist UID");
          pw.println("    Adds a UID to the temporary app idle whitelist.");
          pw.println("  get restrict-background");
          pw.println("    Gets the global restrict background usage status.");
          pw.println("  list wifi-networks [true|false]");
          pw.println("    Lists all saved wifi networks and whether they are metered or not.");
          pw.println("    If a boolean argument is passed, filters just the metered (or unmetered)");
          pw.println("    networks.");
          pw.println("  list restrict-background-whitelist");
          pw.println("    Lists UIDs that are whitelisted for restrict background usage.");
          pw.println("  list restrict-background-blacklist");
          pw.println("    Lists UIDs that are blacklisted for restrict background usage.");
          pw.println("  remove restrict-background-whitelist UID");
          pw.println("    Removes a UID from the whitelist for restrict background usage.");
          pw.println("  remove restrict-background-blacklist UID");
          pw.println("    Removes a UID from the blacklist for restrict background usage.");
          pw.println("  remove app-idle-whitelist UID");
          pw.println("    Removes a UID from the temporary app idle whitelist.");
          pw.println("  set metered-network ID [undefined|true|false]");
          pw.println("    Toggles whether the given wi-fi network is metered.");
          pw.println("  set restrict-background BOOLEAN");
          pw.println("    Sets the global restrict background usage status.");
          pw.println("  set sub-plan-owner subId [packageName]");
          pw.println("    Sets the data plan owner package for subId.");
      }
  
      private int runGet() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          final String type = getNextArg();
          if (type == null) {
              pw.println("Error: didn't specify type of data to get");
              return -1;
          }
          switch(type) {
              case "restrict-background":
                  return getRestrictBackground();
          }
          pw.println("Error: unknown get type '" + type + "'");
          return -1;
      }
  
      private int runSet() throws RemoteException  {
          final PrintWriter pw = getOutPrintWriter();
          final String type = getNextArg();
          if (type == null) {
              pw.println("Error: didn't specify type of data to set");
              return -1;
          }
          switch(type) {
              case "metered-network":
                  return setMeteredWifiNetwork();
              case "restrict-background":
                  return setRestrictBackground();
              case "sub-plan-owner":
                  return setSubPlanOwner();
          }
          pw.println("Error: unknown set type '" + type + "'");
          return -1;
      }
  
      private int runList() throws RemoteException  {
          final PrintWriter pw = getOutPrintWriter();
          final String type = getNextArg();
          if (type == null) {
              pw.println("Error: didn't specify type of data to list");
              return -1;
          }
          switch(type) {
              case "app-idle-whitelist":
                  return listAppIdleWhitelist();
              case "wifi-networks":
                  return listWifiNetworks();
              case "restrict-background-whitelist":
                  return listRestrictBackgroundWhitelist();
              case "restrict-background-blacklist":
                  return listRestrictBackgroundBlacklist();
          }
          pw.println("Error: unknown list type '" + type + "'");
          return -1;
      }
  
      private int runAdd() throws RemoteException  {
          final PrintWriter pw = getOutPrintWriter();
          final String type = getNextArg();
          if (type == null) {
              pw.println("Error: didn't specify type of data to add");
              return -1;
          }
          switch(type) {
              case "restrict-background-whitelist":
                  return addRestrictBackgroundWhitelist();
              case "restrict-background-blacklist":
                  return addRestrictBackgroundBlacklist();
              case "app-idle-whitelist":
                  return addAppIdleWhitelist();
          }
          pw.println("Error: unknown add type '" + type + "'");
          return -1;
      }
  
      private int runRemove() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          final String type = getNextArg();
          if (type == null) {
              pw.println("Error: didn't specify type of data to remove");
              return -1;
          }
          switch(type) {
              case "restrict-background-whitelist":
                  return removeRestrictBackgroundWhitelist();
              case "restrict-background-blacklist":
                  return removeRestrictBackgroundBlacklist();
              case "app-idle-whitelist":
                  return removeAppIdleWhitelist();
          }
          pw.println("Error: unknown remove type '" + type + "'");
          return -1;
      }
  
      private int listUidPolicies(String msg, int policy) throws RemoteException {
          final int[] uids = mInterface.getUidsWithPolicy(policy);
          return listUidList(msg, uids);
      }
  
      private int listUidList(String msg, int[] uids) {
          final PrintWriter pw = getOutPrintWriter();
          pw.print(msg); pw.print(": ");
          if (uids.length == 0) {
              pw.println("none");
          } else {
              for (int i = 0; i < uids.length; i++) {
                  int uid = uids[i];
                  pw.print(uid);
                  pw.print(' ');
              }
          }
          pw.println();
          return 0;
      }
  
      private int listRestrictBackgroundWhitelist() throws RemoteException {
          return listUidPolicies("Restrict background whitelisted UIDs",
                  POLICY_ALLOW_METERED_BACKGROUND);
      }
  
      private int listRestrictBackgroundBlacklist() throws RemoteException {
          return listUidPolicies("Restrict background blacklisted UIDs",
                  POLICY_REJECT_METERED_BACKGROUND);
      }
  
      private int listAppIdleWhitelist() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          final int[] uids = mInterface.getAppIdleWhitelist();
          return listUidList("App Idle whitelisted UIDs", uids);
      }
  
      private int getRestrictBackground() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          pw.print("Restrict background status: ");
          pw.println(mInterface.getRestrictBackground() ? "enabled" : "disabled");
          return 0;
      }
  
      private int setRestrictBackground() throws RemoteException {
          final int enabled = getNextBooleanArg();
          if (enabled < 0) {
              return enabled;
          }
          mInterface.setRestrictBackground(enabled > 0);
          return 0;
      }
  
      private int setSubPlanOwner() throws RemoteException {
          final int subId = Integer.parseInt(getNextArgRequired());
          final String packageName = getNextArg();
          mInterface.setSubscriptionPlansOwner(subId, packageName);
          return 0;
      }
  
      private int setUidPolicy(int policy) throws RemoteException {
          final int uid = getUidFromNextArg();
          if (uid < 0) {
              return uid;
          }
          mInterface.setUidPolicy(uid, policy);
          return 0;
      }
  
      private int resetUidPolicy(String errorMessage, int expectedPolicy) throws RemoteException {
          final int uid = getUidFromNextArg();
          if (uid < 0) {
              return uid;
          }
          int actualPolicy = mInterface.getUidPolicy(uid);
          if (actualPolicy != expectedPolicy) {
              final PrintWriter pw = getOutPrintWriter();
              pw.print("Error: UID "); pw.print(uid); pw.print(' '); pw.println(errorMessage);
              return -1;
          }
          mInterface.setUidPolicy(uid, POLICY_NONE);
          return 0;
      }
  
      private int addRestrictBackgroundWhitelist() throws RemoteException {
          return setUidPolicy(POLICY_ALLOW_METERED_BACKGROUND);
      }
  
      private int removeRestrictBackgroundWhitelist() throws RemoteException {
          return resetUidPolicy("not whitelisted", POLICY_ALLOW_METERED_BACKGROUND);
      }
  
      private int addRestrictBackgroundBlacklist() throws RemoteException {
          return setUidPolicy(POLICY_REJECT_METERED_BACKGROUND);
      }
  
      private int removeRestrictBackgroundBlacklist() throws RemoteException {
          return resetUidPolicy("not blacklisted", POLICY_REJECT_METERED_BACKGROUND);
      }
  
      private int setAppIdleWhitelist(boolean isWhitelisted) {
          final int uid = getUidFromNextArg();
          if (uid < 0) {
              return uid;
          }
          mInterface.setAppIdleWhitelist(uid, isWhitelisted);
          return 0;
      }
  
      private int addAppIdleWhitelist() throws RemoteException {
          return setAppIdleWhitelist(true);
      }
  
      private int removeAppIdleWhitelist() throws RemoteException {
          return setAppIdleWhitelist(false);
      }
  
      private int listWifiNetworks() {
          final PrintWriter pw = getOutPrintWriter();
          final String arg = getNextArg();
          final int match;
          if (arg == null) {
              match = WifiConfiguration.METERED_OVERRIDE_NONE;
          } else if (Boolean.parseBoolean(arg)) {
              match = WifiConfiguration.METERED_OVERRIDE_METERED;
          } else {
              match = WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
          }
  
          final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
          for (WifiConfiguration config : configs) {
              if (arg == null || config.meteredOverride == match) {
                  pw.print(NetworkPolicyManager.resolveNetworkId(config));
                  pw.print(';');
                  pw.println(overrideToString(config.meteredOverride));
              }
          }
          return 0;
      }
  
      private int setMeteredWifiNetwork() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          final String networkId = getNextArg();
          if (networkId == null) {
              pw.println("Error: didn't specify networkId");
              return -1;
          }
          final String arg = getNextArg();
          if (arg == null) {
              pw.println("Error: didn't specify meteredOverride");
              return -1;
          }
          mInterface.setWifiMeteredOverride(NetworkPolicyManager.resolveNetworkId(networkId),
                  stringToOverride(arg));
          return -1;
      }
  
      private static String overrideToString(int override) {
          switch (override) {
              case WifiConfiguration.METERED_OVERRIDE_METERED: return "true";
              case WifiConfiguration.METERED_OVERRIDE_NOT_METERED: return "false";
              default: return "none";
          }
      }
  
      private static int stringToOverride(String override) {
          switch (override) {
              case "true": return WifiConfiguration.METERED_OVERRIDE_METERED;
              case "false": return WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
              default: return WifiConfiguration.METERED_OVERRIDE_NONE;
          }
      }
  
      private int getNextBooleanArg() {
          final PrintWriter pw = getOutPrintWriter();
          final String arg = getNextArg();
          if (arg == null) {
              pw.println("Error: didn't specify BOOLEAN");
              return -1;
          }
          return Boolean.valueOf(arg) ? 1 : 0;
      }
  
      private int getUidFromNextArg() {
          final PrintWriter pw = getOutPrintWriter();
          final String arg = getNextArg();
          if (arg == null) {
              pw.println("Error: didn't specify UID");
              return -1;
          }
          try {
              return Integer.parseInt(arg);
          } catch (NumberFormatException e) {
              pw.println("Error: UID (" + arg + ") should be a number");
              return -2;
          }
      }
  }
  