1 /* 2 * Copyright (C) 2018 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.providers.settings; 18 19 import static android.provider.DeviceConfig.DUMP_ARG_NAMESPACE; 20 import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE; 21 import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT; 22 import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT; 23 24 import android.aconfig.DeviceProtos; 25 import android.aconfig.nano.Aconfig; 26 import android.aconfig.nano.Aconfig.parsed_flag; 27 import android.aconfig.nano.Aconfig.parsed_flags; 28 import android.annotation.SuppressLint; 29 import android.app.ActivityManager; 30 import android.content.AttributionSource; 31 import android.content.IContentProvider; 32 import android.os.Binder; 33 import android.os.Build; 34 import android.os.Bundle; 35 import android.os.ParcelFileDescriptor; 36 import android.os.Process; 37 import android.os.RemoteException; 38 import android.os.ResultReceiver; 39 import android.os.ShellCallback; 40 import android.os.ShellCommand; 41 import android.provider.DeviceConfig; 42 import android.provider.DeviceConfigShellCommandHandler; 43 import android.provider.Settings; 44 import android.provider.Settings.Config.SyncDisabledMode; 45 import android.provider.UpdatableDeviceConfigServiceReadiness; 46 import android.util.Log; 47 import android.util.Slog; 48 49 import com.android.internal.util.FastPrintWriter; 50 51 import java.io.File; 52 import java.io.FileDescriptor; 53 import java.io.FileInputStream; 54 import java.io.FileOutputStream; 55 import java.io.IOException; 56 import java.io.PrintWriter; 57 import java.lang.reflect.Field; 58 import java.lang.reflect.Modifier; 59 import java.util.ArrayList; 60 import java.util.Arrays; 61 import java.util.Collections; 62 import java.util.HashMap; 63 import java.util.HashSet; 64 import java.util.List; 65 import java.util.Map; 66 import java.util.regex.Pattern; 67 68 /** 69 * Receives shell commands from the command line related to device config flags, and dispatches them 70 * to the SettingsProvider. 71 */ 72 public final class DeviceConfigService extends Binder { 73 private static final List<String> sAconfigTextProtoFilesOnDevice = List.of( 74 "/system/etc/aconfig_flags.pb", 75 "/system_ext/etc/aconfig_flags.pb", 76 "/product/etc/aconfig_flags.pb", 77 "/vendor/etc/aconfig_flags.pb"); 78 79 private static final List<String> PRIVATE_NAMESPACES = List.of( 80 "device_config_overrides", 81 "staged", 82 "token_staged"); 83 84 final SettingsProvider mProvider; 85 86 private static final String TAG = "DeviceConfigService"; 87 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 88 DeviceConfigService(SettingsProvider provider)89 public DeviceConfigService(SettingsProvider provider) { 90 mProvider = provider; 91 } 92 93 @Override onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)94 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 95 String[] args, ShellCallback callback, ResultReceiver resultReceiver) 96 throws RemoteException { 97 if (UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService()) { 98 callUpdableDeviceConfigShellCommandHandler(in, out, err, args, resultReceiver); 99 } else { 100 (new MyShellCommand(mProvider)) 101 .exec(this, in, out, err, args, callback, resultReceiver); 102 } 103 } 104 105 // TODO(b/364399200): add unit test 106 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)107 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 108 String filter = null; 109 if (android.provider.flags.Flags.dumpImprovements()) { 110 if (args.length > 0) { 111 switch (args[0]) { 112 case DUMP_ARG_NAMESPACE: 113 if (args.length < 2) { 114 throw new IllegalArgumentException("argument " + DUMP_ARG_NAMESPACE 115 + " requires an extra argument"); 116 } 117 filter = args[1]; 118 if (DEBUG) { 119 Slog.d(TAG, "dump(): setting filter as " + filter); 120 } 121 break; 122 default: 123 Slog.w(TAG, "dump(): ignoring invalid arguments: " + Arrays.toString(args)); 124 break; 125 } 126 } 127 if (filter == null) { 128 pw.print("SyncDisabledForTests: "); 129 MyShellCommand.getSyncDisabledForTests(pw, pw); 130 131 pw.print("UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService(): "); 132 pw.println(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService()); 133 } 134 135 pw.println("DeviceConfig provider: "); 136 DeviceConfig.dump(pw, /* prefix= */ " ", args); 137 } 138 139 IContentProvider iprovider = mProvider.getIContentProvider(); 140 pw.println("DeviceConfig flags:"); 141 Pattern lineFilter = filter == null ? null : Pattern.compile("^.*" + filter + ".*\\/.*$"); 142 for (String line : MyShellCommand.listAll(iprovider)) { 143 if (lineFilter == null || lineFilter.matcher(line).matches()) { 144 pw.println(line); 145 } 146 } 147 148 if (filter != null) { 149 // TODO(b/364399200): use filter to skip instead? 150 return; 151 } 152 } 153 getAconfigFlagNamesInDeviceConfig()154 private static HashSet<String> getAconfigFlagNamesInDeviceConfig() { 155 HashSet<String> nameSet = new HashSet<String>(); 156 try { 157 for (String fileName : sAconfigTextProtoFilesOnDevice) { 158 byte[] contents = (new FileInputStream(fileName)).readAllBytes(); 159 parsed_flags parsedFlags = parsed_flags.parseFrom(contents); 160 if (parsedFlags == null) { 161 Slog.e(TAG, "failed to parse aconfig protobuf from " + fileName); 162 continue; 163 } 164 165 for (parsed_flag flag : parsedFlags.parsedFlag) { 166 nameSet.add(flag.namespace + "/" + flag.package_ + "." + flag.name); 167 } 168 } 169 } catch (IOException e) { 170 Slog.e(TAG, "failed to read aconfig protobuf", e); 171 } 172 return nameSet; 173 } 174 callUpdableDeviceConfigShellCommandHandler(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ResultReceiver resultReceiver)175 private void callUpdableDeviceConfigShellCommandHandler(FileDescriptor in, FileDescriptor out, 176 FileDescriptor err, String[] args, ResultReceiver resultReceiver) { 177 int result = -1; 178 try ( 179 ParcelFileDescriptor inPfd = ParcelFileDescriptor.dup(in); 180 ParcelFileDescriptor outPfd = ParcelFileDescriptor.dup(out); 181 ParcelFileDescriptor errPfd = ParcelFileDescriptor.dup(err)) { 182 result = DeviceConfigShellCommandHandler.handleShellCommand(inPfd, outPfd, errPfd, 183 args); 184 } catch (IOException e) { 185 PrintWriter pw = new FastPrintWriter(new FileOutputStream(err)); 186 pw.println("dup() failed: " + e.getMessage()); 187 pw.flush(); 188 } finally { 189 resultReceiver.send(result, null); 190 } 191 } 192 193 static final class MyShellCommand extends ShellCommand { 194 final SettingsProvider mProvider; 195 private HashMap<String, parsed_flag> mAconfigParsedFlags; 196 197 enum CommandVerb { 198 GET, 199 PUT, 200 OVERRIDE, 201 CLEAR_OVERRIDE, 202 DELETE, 203 LIST, 204 LIST_NAMESPACES, 205 LIST_LOCAL_OVERRIDES, 206 RESET, 207 SET_SYNC_DISABLED_FOR_TESTS, 208 GET_SYNC_DISABLED_FOR_TESTS, 209 } 210 MyShellCommand(SettingsProvider provider)211 MyShellCommand(SettingsProvider provider) { 212 mProvider = provider; 213 214 if (Flags.checkRootAndReadOnly()) { 215 List<parsed_flag> parsedFlags; 216 try { 217 parsedFlags = DeviceProtos.loadAndParseFlagProtos(); 218 } catch (IOException e) { 219 throw new IllegalStateException("failed to parse aconfig protos"); 220 } 221 222 mAconfigParsedFlags = new HashMap(); 223 for (parsed_flag flag : parsedFlags) { 224 mAconfigParsedFlags.put(flag.package_ + "." + flag.name, flag); 225 } 226 } 227 } 228 229 /** 230 * Return true if a flag is aconfig. 231 */ isAconfigFlag(String name)232 private boolean isAconfigFlag(String name) { 233 return mAconfigParsedFlags.get(name) != null; 234 } 235 236 /** 237 * Return true if a flag is both aconfig and read-only. 238 * 239 * @return true if a flag is both aconfig and read-only 240 */ isReadOnly(String name)241 private boolean isReadOnly(String name) { 242 parsed_flag flag = mAconfigParsedFlags.get(name); 243 if (flag != null) { 244 if (flag.permission == Aconfig.READ_ONLY) { 245 return true; 246 } 247 } 248 return false; 249 } 250 251 /** 252 * Return true if the calling process is root. 253 * 254 * @return true if a flag is aconfig, and the calling process is root 255 */ isRoot()256 private boolean isRoot() { 257 return Binder.getCallingUid() == Process.ROOT_UID; 258 } 259 getSyncDisabledForTests(PrintWriter pOut, PrintWriter pErr)260 private static int getSyncDisabledForTests(PrintWriter pOut, PrintWriter pErr) { 261 int syncDisabledModeInt = DeviceConfig.getSyncDisabledMode(); 262 String syncDisabledModeString = formatSyncDisabledMode(syncDisabledModeInt); 263 if (syncDisabledModeString == null) { 264 pErr.println("Unknown mode: " + syncDisabledModeInt); 265 return -1; 266 } 267 pOut.println(syncDisabledModeString); 268 return 0; 269 } 270 getAllFlags(IContentProvider provider)271 public static HashMap<String, String> getAllFlags(IContentProvider provider) { 272 HashMap<String, String> allFlags = new HashMap<String, String>(); 273 for (DeviceConfig.Properties properties : DeviceConfig.getAllProperties()) { 274 List<String> keys = new ArrayList<>(properties.getKeyset()); 275 for (String flagName : properties.getKeyset()) { 276 String fullName = properties.getNamespace() + "/" + flagName; 277 allFlags.put(fullName, properties.getString(flagName, null)); 278 } 279 } 280 return allFlags; 281 } 282 listAll(IContentProvider provider)283 public static List<String> listAll(IContentProvider provider) { 284 HashMap<String, String> allFlags = getAllFlags(provider); 285 final ArrayList<String> lines = new ArrayList<>(); 286 for (String key : allFlags.keySet()) { 287 lines.add(key + "=" + allFlags.get(key)); 288 } 289 Collections.sort(lines); 290 return lines; 291 } 292 log(String msg)293 private static void log(String msg) { 294 if (Build.IS_DEBUGGABLE) { 295 Slog.wtf(TAG, msg); 296 } else { 297 Slog.e(TAG, msg); 298 } 299 } 300 listAllAconfigFlags(IContentProvider provider)301 public static List<String> listAllAconfigFlags(IContentProvider provider) { 302 HashMap<String, String> allFlags = getAllFlags(provider); 303 HashSet<String> aconfigFlagNames = getAconfigFlagNamesInDeviceConfig(); 304 final ArrayList<String> lines = new ArrayList<>(); 305 for (String aconfigFlag : aconfigFlagNames) { 306 String val = allFlags.get(aconfigFlag); 307 if (val != null) { 308 // aconfigFlag is in the form of [namespace]/[package].[flag_name] 309 int idx = aconfigFlag.indexOf("/"); 310 if (idx == -1 || idx == aconfigFlag.length() - 1 || idx == 0) { 311 log("invalid flag entry in device config: " + aconfigFlag); 312 continue; 313 } 314 315 // we intend to print out [package].[flag_name] [namespace]=val 316 String aconfigFlagNameByPackage = aconfigFlag.substring(idx + 1); 317 String namespace = aconfigFlag.substring(0, idx); 318 lines.add("flag:" + aconfigFlagNameByPackage + " namespace:" + namespace + 319 " value:" + val); 320 } 321 } 322 Collections.sort(lines); 323 return lines; 324 } 325 326 @SuppressLint("AndroidFrameworkRequiresPermission") 327 @Override onCommand(String cmd)328 public int onCommand(String cmd) { 329 if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) { 330 onHelp(); 331 return -1; 332 } 333 334 final PrintWriter perr = getErrPrintWriter(); 335 boolean isValid = false; 336 337 CommandVerb verb; 338 if ("get".equalsIgnoreCase(cmd)) { 339 verb = CommandVerb.GET; 340 } else if ("put".equalsIgnoreCase(cmd)) { 341 verb = CommandVerb.PUT; 342 } else if ("override".equalsIgnoreCase(cmd)) { 343 verb = CommandVerb.OVERRIDE; 344 } else if ("clear_override".equalsIgnoreCase(cmd)) { 345 verb = CommandVerb.CLEAR_OVERRIDE; 346 } else if ("delete".equalsIgnoreCase(cmd)) { 347 verb = CommandVerb.DELETE; 348 } else if ("list".equalsIgnoreCase(cmd)) { 349 verb = CommandVerb.LIST; 350 if (peekNextArg() == null) { 351 isValid = true; 352 } 353 } else if ("list_namespaces".equalsIgnoreCase(cmd)) { 354 verb = CommandVerb.LIST_NAMESPACES; 355 if (peekNextArg() == null) { 356 isValid = true; 357 } 358 } else if ("list_local_overrides".equalsIgnoreCase(cmd)) { 359 verb = CommandVerb.LIST_LOCAL_OVERRIDES; 360 if (peekNextArg() == null) { 361 isValid = true; 362 } 363 } else if ("reset".equalsIgnoreCase(cmd)) { 364 verb = CommandVerb.RESET; 365 } else if ("set_sync_disabled_for_tests".equalsIgnoreCase(cmd)) { 366 verb = CommandVerb.SET_SYNC_DISABLED_FOR_TESTS; 367 } else if ("get_sync_disabled_for_tests".equalsIgnoreCase(cmd)) { 368 verb = CommandVerb.GET_SYNC_DISABLED_FOR_TESTS; 369 if (peekNextArg() != null) { 370 perr.println("Bad arguments"); 371 return -1; 372 } 373 isValid = true; 374 } else { 375 // invalid 376 perr.println("Invalid command: " + cmd); 377 return -1; 378 } 379 380 // Parse args for those commands that have them. 381 int syncDisabledModeArg = -1; 382 int resetMode = -1; 383 boolean makeDefault = false; 384 String namespace = null; 385 String key = null; 386 String value = null; 387 String arg; 388 boolean publicOnly = false; 389 while ((arg = getNextArg()) != null) { 390 if (verb == CommandVerb.RESET) { 391 if (resetMode == -1) { 392 // RESET 1st arg (required) 393 if ("untrusted_defaults".equalsIgnoreCase(arg)) { 394 resetMode = Settings.RESET_MODE_UNTRUSTED_DEFAULTS; 395 } else if ("untrusted_clear".equalsIgnoreCase(arg)) { 396 resetMode = Settings.RESET_MODE_UNTRUSTED_CHANGES; 397 } else if ("trusted_defaults".equalsIgnoreCase(arg)) { 398 resetMode = Settings.RESET_MODE_TRUSTED_DEFAULTS; 399 } else { 400 // invalid 401 perr.println("Invalid reset mode: " + arg); 402 return -1; 403 } 404 if (peekNextArg() == null) { 405 isValid = true; 406 } 407 } else { 408 // RESET 2nd arg (optional) 409 namespace = arg; 410 if (peekNextArg() == null) { 411 isValid = true; 412 } else { 413 // invalid 414 perr.println("Too many arguments"); 415 return -1; 416 } 417 } 418 } else if (verb == CommandVerb.SET_SYNC_DISABLED_FOR_TESTS) { 419 if (syncDisabledModeArg == -1) { 420 // SET_SYNC_DISABLED_FOR_TESTS 1st arg (required) 421 syncDisabledModeArg = parseSyncDisabledMode(arg); 422 if (syncDisabledModeArg == -1) { 423 // invalid 424 perr.println("Invalid sync disabled mode: " + arg); 425 return -1; 426 } 427 if (peekNextArg() == null) { 428 isValid = true; 429 } 430 } 431 } else if (verb == CommandVerb.LIST_NAMESPACES) { 432 if (arg.equals("--public")) { 433 isValid = true; 434 publicOnly = true; 435 } 436 } else if (namespace == null) { 437 // GET, PUT, OVERRIDE, DELETE, LIST 1st arg 438 namespace = arg; 439 if (verb == CommandVerb.LIST) { 440 if (peekNextArg() == null) { 441 isValid = true; 442 } else { 443 // invalid 444 perr.println("Too many arguments"); 445 return -1; 446 } 447 } 448 } else if (key == null) { 449 // GET, PUT, OVERRIDE, DELETE 2nd arg 450 key = arg; 451 boolean validVerb = verb == CommandVerb.GET 452 || verb == CommandVerb.DELETE 453 || verb == CommandVerb.CLEAR_OVERRIDE; 454 if (validVerb) { 455 // GET, DELETE only have 2 args 456 if (peekNextArg() == null) { 457 isValid = true; 458 } else { 459 // invalid 460 perr.println("Too many arguments"); 461 return -1; 462 } 463 } 464 } else if (value == null) { 465 // PUT, OVERRIDE 3rd arg (required) 466 value = arg; 467 boolean validVerb = verb == CommandVerb.PUT 468 || verb == CommandVerb.OVERRIDE; 469 if (validVerb && peekNextArg() == null) { 470 isValid = true; 471 } 472 } else if ("default".equalsIgnoreCase(arg)) { 473 // PUT 4th arg (optional) 474 makeDefault = true; 475 if (verb == CommandVerb.PUT && peekNextArg() == null) { 476 isValid = true; 477 } else { 478 // invalid 479 perr.println("Too many arguments"); 480 return -1; 481 } 482 } 483 } 484 485 if (!isValid) { 486 perr.println("Bad arguments"); 487 return -1; 488 } 489 490 final IContentProvider iprovider = mProvider.getIContentProvider(); 491 final PrintWriter pout = getOutPrintWriter(); 492 switch (verb) { 493 case GET: 494 pout.println(DeviceConfig.getProperty(namespace, key)); 495 break; 496 case PUT: 497 if (Flags.checkRootAndReadOnly()) { 498 if (isAconfigFlag(key)) { 499 if (!isRoot()) { 500 pout.println("Error: must be root to write aconfig flag"); 501 break; 502 } 503 504 if (isReadOnly(key)) { 505 pout.println("Error: cannot write read-only flag"); 506 break; 507 } 508 } 509 } 510 511 DeviceConfig.setProperty(namespace, key, value, makeDefault); 512 break; 513 case OVERRIDE: 514 if (Flags.checkRootAndReadOnly()) { 515 if (isAconfigFlag(key)) { 516 if (!isRoot()) { 517 pout.println("Error: must be root to write aconfig flag"); 518 break; 519 } 520 521 if (isReadOnly(key)) { 522 pout.println("Error: cannot write read-only flag"); 523 break; 524 } 525 } 526 } 527 528 DeviceConfig.setLocalOverride(namespace, key, value); 529 break; 530 case CLEAR_OVERRIDE: 531 if (Flags.checkRootAndReadOnly()) { 532 if (isAconfigFlag(key)) { 533 if (!isRoot()) { 534 pout.println("Error: must be root to write aconfig flag"); 535 break; 536 } 537 538 if (isReadOnly(key)) { 539 pout.println("Error: cannot write read-only flag"); 540 break; 541 } 542 } 543 } 544 545 DeviceConfig.clearLocalOverride(namespace, key); 546 break; 547 case DELETE: 548 if (Flags.checkRootAndReadOnly()) { 549 if (isAconfigFlag(key)) { 550 if (!isRoot()) { 551 pout.println("Error: must be root to write aconfig flag"); 552 break; 553 } 554 555 if (isReadOnly(key)) { 556 pout.println("Error: cannot write read-only flag"); 557 break; 558 } 559 } 560 } 561 562 pout.println(delete(iprovider, namespace, key) 563 ? "Successfully deleted " + key + " from " + namespace 564 : "Failed to delete " + key + " from " + namespace); 565 break; 566 case LIST: 567 if (namespace != null) { 568 DeviceConfig.Properties properties = 569 DeviceConfig.getProperties(namespace); 570 List<String> keys = new ArrayList<>(properties.getKeyset()); 571 Collections.sort(keys); 572 for (String name : keys) { 573 pout.println(name + "=" + properties.getString(name, null)); 574 } 575 } else { 576 for (String line : listAll(iprovider)) { 577 boolean isPrivate = false; 578 for (String privateNamespace : PRIVATE_NAMESPACES) { 579 if (line.startsWith(privateNamespace)) { 580 isPrivate = true; 581 break; 582 } 583 } 584 585 if (!isPrivate) { 586 pout.println(line); 587 } 588 } 589 } 590 break; 591 case LIST_NAMESPACES: 592 List<String> namespaces; 593 if (publicOnly) { 594 namespaces = DeviceConfig.getPublicNamespaces(); 595 } else { 596 Field[] fields = DeviceConfig.class.getDeclaredFields(); 597 namespaces = new ArrayList<>(fields.length); 598 // TODO(b/265948913): once moved to mainline, it should call a hidden method 599 // directly 600 for (Field field : fields) { 601 int modifiers = field.getModifiers(); 602 try { 603 if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) 604 && field.getType().equals(String.class) 605 && field.getName().startsWith("NAMESPACE_")) { 606 namespaces.add((String) field.get(null)); 607 } 608 } catch (IllegalAccessException ignored) { } 609 } 610 } 611 for (int i = 0; i < namespaces.size(); i++) { 612 pout.println(namespaces.get(i)); 613 } 614 break; 615 case LIST_LOCAL_OVERRIDES: 616 Map<String, Map<String, String>> underlyingValues = 617 DeviceConfig.getUnderlyingValuesForOverriddenFlags(); 618 for (String overrideNamespace : underlyingValues.keySet()) { 619 Map<String, String> flagToValue = 620 underlyingValues.get(overrideNamespace); 621 for (String flag : flagToValue.keySet()) { 622 String flagText = overrideNamespace + "/" + flag; 623 String valueText = 624 DeviceConfig.getProperty(overrideNamespace, flag); 625 pout.println(flagText + "=" + valueText); 626 } 627 } 628 break; 629 case RESET: 630 DeviceConfig.resetToDefaults(resetMode, namespace); 631 break; 632 case SET_SYNC_DISABLED_FOR_TESTS: 633 DeviceConfig.setSyncDisabledMode(syncDisabledModeArg); 634 break; 635 case GET_SYNC_DISABLED_FOR_TESTS: 636 return getSyncDisabledForTests(pout, perr); 637 default: 638 perr.println("Unspecified command"); 639 return -1; 640 } 641 return 0; 642 } 643 644 @Override onHelp()645 public void onHelp() { 646 PrintWriter pw = getOutPrintWriter(); 647 pw.println("Device Config (device_config) commands:"); 648 pw.println(" help"); 649 pw.println(" Print this help text."); 650 pw.println(" get NAMESPACE KEY"); 651 pw.println(" Retrieve the current value of KEY from the given NAMESPACE."); 652 pw.println(" put NAMESPACE KEY VALUE [default]"); 653 pw.println(" Change the contents of KEY to VALUE for the given NAMESPACE."); 654 pw.println(" {default} to set as the default value."); 655 pw.println(" override NAMESPACE KEY VALUE"); 656 pw.println(" Set flag NAMESPACE/KEY to the given VALUE, and ignores " 657 + "server-updates for"); 658 pw.println(" this flag. This can still be called even if there is no underlying " 659 + "value set."); 660 pw.println(" delete NAMESPACE KEY"); 661 pw.println(" Delete the entry for KEY for the given NAMESPACE."); 662 pw.println(" clear_override NAMESPACE KEY"); 663 pw.println(" Clear local sticky flag override for KEY in the given NAMESPACE."); 664 pw.println(" list_namespaces [--public]"); 665 pw.println(" Prints the name of all (or just the public) namespaces."); 666 pw.println(" list [NAMESPACE]"); 667 pw.println(" Print all keys and values defined, optionally for the given " 668 + "NAMESPACE."); 669 pw.println(" list_local_overrides"); 670 pw.println(" Print all flags that have been overridden."); 671 pw.println(" reset RESET_MODE [NAMESPACE]"); 672 pw.println(" Reset all flag values, optionally for a NAMESPACE, according to " 673 + "RESET_MODE."); 674 pw.println(" RESET_MODE is one of {untrusted_defaults, untrusted_clear, " 675 + "trusted_defaults}"); 676 pw.println(" NAMESPACE limits which flags are reset if provided, otherwise all " 677 + "flags are reset"); 678 pw.println(" set_sync_disabled_for_tests SYNC_DISABLED_MODE"); 679 pw.println(" Modifies bulk property setting behavior for tests. When in one of the" 680 + " disabled modes"); 681 pw.println(" this ensures that config isn't overwritten. SYNC_DISABLED_MODE is " 682 + "one of:"); 683 pw.println(" none: Sync is not disabled. A reboot may be required to restart" 684 + " syncing."); 685 pw.println(" persistent: Sync is disabled, this state will survive a reboot."); 686 pw.println(" until_reboot: Sync is disabled until the next reboot."); 687 pw.println(" get_sync_disabled_for_tests"); 688 pw.println(" Prints one of the SYNC_DISABLED_MODE values, see" 689 + " set_sync_disabled_for_tests"); 690 } 691 delete(IContentProvider provider, String namespace, String key)692 private boolean delete(IContentProvider provider, String namespace, String key) { 693 String compositeKey = namespace + "/" + key; 694 boolean success; 695 696 try { 697 Bundle args = new Bundle(); 698 args.putInt(Settings.CALL_METHOD_USER_KEY, 699 ActivityManager.getService().getCurrentUser().id); 700 Bundle b = provider.call(new AttributionSource(Process.myUid(), 701 resolveCallingPackage(), null), Settings.AUTHORITY, 702 Settings.CALL_METHOD_DELETE_CONFIG, compositeKey, args); 703 success = (b != null && b.getInt(SettingsProvider.RESULT_ROWS_DELETED) == 1); 704 } catch (RemoteException e) { 705 throw new RuntimeException("Failed in IPC", e); 706 } 707 return success; 708 } 709 resolveCallingPackage()710 private static String resolveCallingPackage() { 711 switch (Binder.getCallingUid()) { 712 case Process.ROOT_UID: { 713 return "root"; 714 } 715 716 case Process.SHELL_UID: { 717 return "com.android.shell"; 718 } 719 720 default: { 721 return null; 722 } 723 } 724 } 725 } 726 parseSyncDisabledMode(String arg)727 private static @SyncDisabledMode int parseSyncDisabledMode(String arg) { 728 int syncDisabledMode; 729 if ("none".equalsIgnoreCase(arg)) { 730 syncDisabledMode = SYNC_DISABLED_MODE_NONE; 731 } else if ("persistent".equalsIgnoreCase(arg)) { 732 syncDisabledMode = SYNC_DISABLED_MODE_PERSISTENT; 733 } else if ("until_reboot".equalsIgnoreCase(arg)) { 734 syncDisabledMode = SYNC_DISABLED_MODE_UNTIL_REBOOT; 735 } else { 736 syncDisabledMode = -1; 737 } 738 return syncDisabledMode; 739 } 740 formatSyncDisabledMode(@yncDisabledMode int syncDisabledMode)741 private static String formatSyncDisabledMode(@SyncDisabledMode int syncDisabledMode) { 742 switch (syncDisabledMode) { 743 case SYNC_DISABLED_MODE_NONE: 744 return "none"; 745 case SYNC_DISABLED_MODE_PERSISTENT: 746 return "persistent"; 747 case SYNC_DISABLED_MODE_UNTIL_REBOOT: 748 return "until_reboot"; 749 default: 750 return null; 751 } 752 } 753 } 754