content: /*
   * Copyright (C) 2019 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.utils;
  
  import android.annotation.Nullable;
  import android.provider.DeviceConfig;
  
  import com.android.internal.annotations.VisibleForTesting;
  import com.android.server.RescueParty;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  
  /**
   * Utilities for interacting with the {@link android.provider.DeviceConfig}.
   *
   * @hide
   */
  public final class FlagNamespaceUtils {
      /**
       * Special String used for communicating through {@link #RESET_PLATFORM_PACKAGE_FLAG} that
       * Settings were reset by the RescueParty, no actual namespace with this name exists in
       * {@link DeviceConfig}.
       */
      public static final String NAMESPACE_NO_PACKAGE = "no_package";
  
      /**
       * Name of the special namespace in DeviceConfig table used for communicating resets.
       */
      @VisibleForTesting
      public static final String NAMESPACE_RESCUE_PARTY = "rescue_party_namespace";
      /**
       * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY}, holding all known {@link
       * DeviceConfig} namespaces, as a {@link #DELIMITER} separated String. It's updated after the
       * first time flags are written to the new namespace in the {@link DeviceConfig}.
       */
      @VisibleForTesting
      public static final String ALL_KNOWN_NAMESPACES_FLAG = "all_known_namespaces";
      /**
       * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY} with integer counter
       * suffix added to it, holding {@link DeviceConfig} namespace value whose flags were recently
       * reset by the {@link RescueParty}. It's updated by {@link RescueParty} every time given
       * namespace flags are reset.
       */
      @VisibleForTesting
      public static final String RESET_PLATFORM_PACKAGE_FLAG = "reset_platform_package";
      private static final String DELIMITER = ":";
      /**
       * Maximum value of the counter used in combination with {@link #RESET_PLATFORM_PACKAGE_FLAG}
       * when communicating recently reset by the RescueParty namespace values.
       */
      private static final int MAX_COUNTER_VALUE = 50;
  
      private static int sKnownResetNamespacesFlagCounter = -1;
  
      /**
       * Sets the union of {@link #RESET_PLATFORM_PACKAGE_FLAG} with
       * {@link #sKnownResetNamespacesFlagCounter} in the DeviceConfig for each namespace
       * in the consumed namespacesList. These flags are used for communicating the namespaces
       * (aka platform packages) whose flags in {@link DeviceConfig} were just reset
       * by the RescueParty.
       */
      public static void addToKnownResetNamespaces(@Nullable List<String> namespacesList) {
          if (namespacesList == null) {
              return;
          }
          for (String namespace : namespacesList) {
              addToKnownResetNamespaces(namespace);
          }
      }
  
      /**
       * Sets the union of {@link #RESET_PLATFORM_PACKAGE_FLAG} with
       * {@link #sKnownResetNamespacesFlagCounter} in the DeviceConfig for the consumed namespace.
       * This flag is used for communicating the namespace (aka platform package) whose flags
       * in {@link DeviceConfig} were just reset by the RescueParty.
       */
      public static void addToKnownResetNamespaces(String namespace) {
          int nextFlagCounter = incrementAndRetrieveResetNamespacesFlagCounter();
          DeviceConfig.setProperty(NAMESPACE_RESCUE_PARTY,
                  RESET_PLATFORM_PACKAGE_FLAG + nextFlagCounter,
                  namespace, /*makeDefault=*/ true);
      }
  
      /**
       * Reset all namespaces in DeviceConfig with consumed resetMode.
       */
      public static void resetDeviceConfig(int resetMode) {
          resetDeviceConfig(resetMode, getAllKnownDeviceConfigNamespacesList());
      }
  
      /**
       * Reset all consumed namespaces in DeviceConfig with consumed resetMode.
       */
      public static void resetDeviceConfig(int resetMode, List<String> namespacesList) {
          for (String namespace : namespacesList) {
              DeviceConfig.resetToDefaults(resetMode, namespace);
          }
          addToKnownResetNamespaces(namespacesList);
      }
  
      /**
       * Resets known reset namespaces flag counter for tests only.
       */
      @VisibleForTesting
      public static void resetKnownResetNamespacesFlagCounterForTest() {
          sKnownResetNamespacesFlagCounter = -1;
      }
  
      /**
       * Returns a list of all known DeviceConfig namespaces, except for the special {@link
       * #NAMESPACE_RESCUE_PARTY}
       */
      private static List<String> getAllKnownDeviceConfigNamespacesList() {
          String namespacesStr = DeviceConfig.getProperty(NAMESPACE_RESCUE_PARTY,
                  ALL_KNOWN_NAMESPACES_FLAG);
          List<String> namespacesList = toStringList(namespacesStr);
          namespacesList.remove(NAMESPACE_RESCUE_PARTY);
          return namespacesList;
      }
  
      private static List<String> toStringList(String serialized) {
          if (serialized == null || serialized.length() == 0) {
              return new ArrayList<>();
          }
          return Arrays.asList(serialized.split(DELIMITER));
      }
  
      private static int incrementAndRetrieveResetNamespacesFlagCounter() {
          sKnownResetNamespacesFlagCounter++;
          if (sKnownResetNamespacesFlagCounter == MAX_COUNTER_VALUE) {
              sKnownResetNamespacesFlagCounter = 0;
          }
          return sKnownResetNamespacesFlagCounter;
      }
  }
  