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