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.pm;
  
  import android.os.SystemProperties;
  
  import dalvik.system.DexFile;
  
  /**
   * Manage (retrieve) mappings from compilation reason to compilation filter.
   */
  public class PackageManagerServiceCompilerMapping {
      // Names for compilation reasons.
      public static final String REASON_STRINGS[] = {
              "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive", "shared"
      };
  
      static final int REASON_SHARED_INDEX = 6;
  
      // Static block to ensure the strings array is of the right length.
      static {
          if (PackageManagerService.REASON_LAST + 1 != REASON_STRINGS.length) {
              throw new IllegalStateException("REASON_STRINGS not correct");
          }
          if (!"shared".equals(REASON_STRINGS[REASON_SHARED_INDEX])) {
              throw new IllegalStateException("REASON_STRINGS not correct because of shared index");
          }
      }
  
      private static String getSystemPropertyName(int reason) {
          if (reason < 0 || reason >= REASON_STRINGS.length) {
              throw new IllegalArgumentException("reason " + reason + " invalid");
          }
  
          return "pm.dexopt." + REASON_STRINGS[reason];
      }
  
      // Load the property for the given reason and check for validity. This will throw an
      // exception in case the reason or value are invalid.
      private static String getAndCheckValidity(int reason) {
          String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
          if (sysPropValue == null || sysPropValue.isEmpty() ||
                  !DexFile.isValidCompilerFilter(sysPropValue)) {
              throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
                      + "(reason " + REASON_STRINGS[reason] + ")");
          } else if (!isFilterAllowedForReason(reason, sysPropValue)) {
              throw new IllegalStateException("Value \"" + sysPropValue +"\" not allowed "
                      + "(reason " + REASON_STRINGS[reason] + ")");
          }
  
          return sysPropValue;
      }
  
      private static boolean isFilterAllowedForReason(int reason, String filter) {
          return reason != REASON_SHARED_INDEX || !DexFile.isProfileGuidedCompilerFilter(filter);
      }
  
      // Check that the properties are set and valid.
      // Note: this is done in a separate method so this class can be statically initialized.
      static void checkProperties() {
          // We're gonna check all properties and collect the exceptions, so we can give a general
          // overview. Store the exceptions here.
          RuntimeException toThrow = null;
  
          for (int reason = 0; reason <= PackageManagerService.REASON_LAST; reason++) {
              try {
                  // Check that the system property name is legal.
                  String sysPropName = getSystemPropertyName(reason);
                  if (sysPropName == null || sysPropName.isEmpty()) {
                      throw new IllegalStateException("Reason system property name \"" +
                              sysPropName +"\" for reason " + REASON_STRINGS[reason]);
                  }
  
                  // Check validity, ignore result.
                  getAndCheckValidity(reason);
              } catch (Exception exc) {
                  if (toThrow == null) {
                      toThrow = new IllegalStateException("PMS compiler filter settings are bad.");
                  }
                  toThrow.addSuppressed(exc);
              }
          }
  
          if (toThrow != null) {
              throw toThrow;
          }
      }
  
      public static String getCompilerFilterForReason(int reason) {
          return getAndCheckValidity(reason);
      }
  
      /**
       * Return the default compiler filter for compilation.
       *
       * We derive that from the traditional "dalvik.vm.dex2oat-filter" property and just make
       * sure this isn't profile-guided. Returns "speed" in case of invalid (or missing) values.
       */
      public static String getDefaultCompilerFilter() {
          String value = SystemProperties.get("dalvik.vm.dex2oat-filter");
          if (value == null || value.isEmpty()) {
              return "speed";
          }
  
          if (!DexFile.isValidCompilerFilter(value) ||
                  DexFile.isProfileGuidedCompilerFilter(value)) {
              return "speed";
          }
  
          return value;
      }
  
      public static String getReasonName(int reason) {
          if (reason == PackageManagerService.REASON_UNKNOWN) {
              return "unknown";
          }
          if (reason < 0 || reason >= REASON_STRINGS.length) {
              throw new IllegalArgumentException("reason " + reason + " invalid");
          }
          return REASON_STRINGS[reason];
      }
  }
  