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.location;
  
  import android.location.GnssCapabilities;
  import android.util.Log;
  
  import com.android.internal.annotations.GuardedBy;
  
  /**
   * Provides GNSS capabilities supported by the GNSS HAL implementation.
   */
  public class GnssCapabilitiesProvider {
      private static final String TAG = "GnssCapabilitiesProvider";
      private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
  
      private static final long GNSS_CAPABILITIES_TOP_HAL =
              GnssCapabilities.LOW_POWER_MODE | GnssCapabilities.SATELLITE_BLACKLIST
                      | GnssCapabilities.GEOFENCING | GnssCapabilities.MEASUREMENTS
                      | GnssCapabilities.NAV_MESSAGES;
  
      private static final long GNSS_CAPABILITIES_SUB_HAL_MEASUREMENT_CORRECTIONS =
              GnssCapabilities.MEASUREMENT_CORRECTIONS
                      | GnssCapabilities.MEASUREMENT_CORRECTIONS_LOS_SATS
                      | GnssCapabilities.MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH
                      | GnssCapabilities.MEASUREMENT_CORRECTIONS_REFLECTING_PLANE;
  
      // Capabilities in {@link android.location.GnssCapabilities} supported by GNSS chipset.
      @GuardedBy("this")
      private long mGnssCapabilities;
  
      /**
       * Returns the capabilities supported by the GNSS chipset.
       *
       * <p>The capabilities are described in {@link android.location.GnssCapabilities} and
       * their integer values correspond to the bit positions in the returned {@code long} value.
       */
      public long getGnssCapabilities() {
          synchronized (this) {
              return mGnssCapabilities;
          }
      }
  
      /**
       * Updates the general capabilities exposed through {@link android.location.GnssCapabilities}.
       */
      void setTopHalCapabilities(int topHalCapabilities) {
          long gnssCapabilities = 0;
          if (hasCapability(topHalCapabilities,
                  GnssLocationProvider.GPS_CAPABILITY_LOW_POWER_MODE)) {
              gnssCapabilities |= GnssCapabilities.LOW_POWER_MODE;
          }
          if (hasCapability(topHalCapabilities,
                  GnssLocationProvider.GPS_CAPABILITY_SATELLITE_BLACKLIST)) {
              gnssCapabilities |= GnssCapabilities.SATELLITE_BLACKLIST;
          }
          if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_GEOFENCING)) {
              gnssCapabilities |= GnssCapabilities.GEOFENCING;
          }
          if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_MEASUREMENTS)) {
              gnssCapabilities |= GnssCapabilities.MEASUREMENTS;
          }
          if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_NAV_MESSAGES)) {
              gnssCapabilities |= GnssCapabilities.NAV_MESSAGES;
          }
  
          synchronized (this) {
              mGnssCapabilities &= ~GNSS_CAPABILITIES_TOP_HAL;
              mGnssCapabilities |= gnssCapabilities;
              if (DEBUG) {
                  Log.d(TAG, "setTopHalCapabilities, mGnssCapabilities=0x" + Long.toHexString(
                          mGnssCapabilities) + ", " + GnssCapabilities.of(mGnssCapabilities));
              }
          }
      }
  
      /**
       * Updates the measurement corrections related capabilities exposed through
       * {@link android.location.GnssCapabilities}.
       */
      void setSubHalMeasurementCorrectionsCapabilities(int measurementCorrectionsCapabilities) {
          long gnssCapabilities = GnssCapabilities.MEASUREMENT_CORRECTIONS;
          if (hasCapability(measurementCorrectionsCapabilities,
                  GnssMeasurementCorrectionsProvider.CAPABILITY_LOS_SATS)) {
              gnssCapabilities |= GnssCapabilities.MEASUREMENT_CORRECTIONS_LOS_SATS;
          }
          if (hasCapability(measurementCorrectionsCapabilities,
                  GnssMeasurementCorrectionsProvider.CAPABILITY_EXCESS_PATH_LENGTH)) {
              gnssCapabilities |= GnssCapabilities.MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH;
          }
          if (hasCapability(measurementCorrectionsCapabilities,
                  GnssMeasurementCorrectionsProvider.CAPABILITY_REFLECTING_PLANE)) {
              gnssCapabilities |= GnssCapabilities.MEASUREMENT_CORRECTIONS_REFLECTING_PLANE;
          }
  
          synchronized (this) {
              mGnssCapabilities &= ~GNSS_CAPABILITIES_SUB_HAL_MEASUREMENT_CORRECTIONS;
              mGnssCapabilities |= gnssCapabilities;
              if (DEBUG) {
                  Log.d(TAG, "setSubHalMeasurementCorrectionsCapabilities, mGnssCapabilities=0x"
                          + Long.toHexString(mGnssCapabilities) + ", " + GnssCapabilities.of(
                          mGnssCapabilities));
              }
          }
      }
  
      private static  boolean hasCapability(int halCapabilities, int capability) {
          return (halCapabilities & capability) != 0;
      }
  }
  