content: /*
   * Copyright (C) 2017 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 android.hardware;
  
  import android.annotation.IntDef;
  import android.os.MemoryFile;
  
  import dalvik.system.CloseGuard;
  
  import java.io.IOException;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.nio.channels.Channel;
  import java.util.concurrent.atomic.AtomicBoolean;
  
  /**
   * Class representing a sensor direct channel. Use
   * {@link SensorManager#createDirectChannel(android.os.MemoryFile)} or
   * {@link SensorManager#createDirectChannel(android.hardware.HardwareBuffer)}
   * to obtain an object. The channel object can be then configured
   * (see {@link #configure(Sensor, int)})
   * to start delivery of sensor events into shared memory buffer.
   */
  public final class SensorDirectChannel implements Channel {
  
      // shared memory types
  
      /** @hide */
      @Retention(RetentionPolicy.SOURCE)
      @IntDef(flag = true, prefix = { "TYPE_" }, value = {
              TYPE_MEMORY_FILE,
              TYPE_HARDWARE_BUFFER
      })
      public @interface MemoryType {}
  
      /**
       * Shared memory type ashmem, wrapped in MemoryFile object.
       *
       * @see SensorManager#createDirectChannel(MemoryFile)
       */
      public static final int TYPE_MEMORY_FILE = 1;
  
      /**
       * Shared memory type wrapped by HardwareBuffer object.
       *
       * @see SensorManager#createDirectChannel(HardwareBuffer)
       */
      public static final int TYPE_HARDWARE_BUFFER = 2;
  
      // sensor rate levels
  
      /** @hide */
      @Retention(RetentionPolicy.SOURCE)
      @IntDef(flag = true, prefix = { "RATE_" }, value = {
              RATE_STOP,
              RATE_NORMAL,
              RATE_FAST,
              RATE_VERY_FAST
      })
      public @interface RateLevel {}
  
      /**
       * Sensor stopped (no event output).
       *
       * @see #configure(Sensor, int)
       */
      public static final int RATE_STOP = 0;
      /**
       * Sensor operates at nominal rate of 50Hz.
       *
       * The actual rate is expected to be between 55% to 220% of nominal rate, thus between 27.5Hz to
       * 110Hz.
       *
       * @see #configure(Sensor, int)
       */
      public static final int RATE_NORMAL = 1; //50Hz
      /**
       * Sensor operates at nominal rate of 200Hz.
       *
       * The actual rate is expected to be between 55% to 220% of nominal rate, thus between 110Hz to
       * 440Hz.
       *
       * @see #configure(Sensor, int)
       */
      public static final int RATE_FAST = 2; // ~200Hz
      /**
       * Sensor operates at nominal rate of 800Hz.
       *
       * The actual rate is expected to be between 55% to 220% of nominal rate, thus between 440Hz to
       * 1760Hz.
       *
       * @see #configure(Sensor, int)
       */
      public static final int RATE_VERY_FAST = 3; // ~800Hz
  
      /**
       * Determine if a channel is still valid. A channel is invalidated after {@link #close()} is
       * called.
       *
       * @return <code>true</code> if channel is valid.
       */
      @Override
      public boolean isOpen() {
          return !mClosed.get();
      }
  
      /** @removed */
      @Deprecated
      public boolean isValid() {
          return isOpen();
      }
  
      /**
       * Close sensor direct channel.
       *
       * Stop all active sensor in the channel and free sensor system resource related to channel.
       * Shared memory used for creating the direct channel need to be closed or freed separately.
       *
       * @see SensorManager#createDirectChannel(MemoryFile)
       * @see SensorManager#createDirectChannel(HardwareBuffer)
       */
      @Override
      public void close() {
          if (mClosed.compareAndSet(false, true)) {
              mCloseGuard.close();
              // actual close action
              mManager.destroyDirectChannel(this);
          }
      }
  
      /**
       * Configure sensor rate or stop sensor report.
       *
       * To start event report of a sensor, or change rate of existing report, call this function with
       * rateLevel other than {@link android.hardware.SensorDirectChannel#RATE_STOP}. Sensor events
       * will be added into a queue formed by the shared memory used in creation of direction channel.
       * Each element of the queue has size of 104 bytes and represents a sensor event. Data
       * structure of an element (all fields in little-endian):
       *
       * <pre>
       *   offset   type                    name
       * ------------------------------------------------------------------------
       *   0x0000   int32_t                 size (always 104)
       *   0x0004   int32_t                 sensor report token
       *   0x0008   int32_t                 type (see SensorType)
       *   0x000C   uint32_t                atomic counter
       *   0x0010   int64_t                 timestamp (see Event)
       *   0x0018   float[16]/int64_t[8]    data (data type depends on sensor type)
       *   0x0058   int32_t[4]              reserved (set to zero)
       * </pre>
       *
       * There are no head or tail pointers. The sequence and frontier of new sensor events is
       * determined by the atomic counter, which counts from 1 after creation of direct channel and
       * increments 1 for each new event. Atomic counter will wrap back to 1 after it reaches
       * UINT32_MAX, skipping value 0 to avoid confusion with uninitialized memory. The writer in
       * sensor system will wrap around from the start of shared memory region when it reaches the
       * end. If size of memory region is not a multiple of size of element (104 bytes), the residual
       * is not used at the end.  Function returns a positive sensor report token on success. This
       * token can be used to differentiate sensor events from multiple sensor of the same type. For
       * example, if there are two accelerometers in the system A and B, it is guaranteed different
       * report tokens will be returned when starting sensor A and B.
       *
       * To stop a sensor, call this function with rateLevel equal {@link
       * android.hardware.SensorDirectChannel#RATE_STOP}. If the sensor parameter is left to be null,
       * this will stop all active sensor report associated with the direct channel specified.
       * Function return 1 on success or 0 on failure.
       *
       * @param sensor A {@link android.hardware.Sensor} object to denote sensor to be operated.
       * @param rateLevel rate level defined in {@link android.hardware.SensorDirectChannel}.
       * @return * starting report or changing rate: positive sensor report token on success,
       *                                             0 on failure;
       *         * stopping report: 1 on success, 0 on failure.
       * @throws NullPointerException when channel is null.
       */
      public int configure(Sensor sensor, @RateLevel int rateLevel) {
          return mManager.configureDirectChannelImpl(this, sensor, rateLevel);
      }
  
      /** @hide */
      SensorDirectChannel(SensorManager manager, int id, int type, long size) {
          mManager = manager;
          mNativeHandle = id;
          mType = type;
          mSize = size;
          mCloseGuard.open("SensorDirectChannel");
      }
  
      /** @hide */
      int getNativeHandle() {
          return mNativeHandle;
      }
  
      /**
       * This function encode handle information in {@link android.os.MemoryFile} into a long array to
       * be passed down to native methods.
       *
       * @hide */
      static long[] encodeData(MemoryFile ashmem) {
          int fd;
          try {
              fd = ashmem.getFileDescriptor().getInt$();
          } catch (IOException e) {
              fd = -1;
          }
          return new long[] { 1 /*numFds*/, 0 /*numInts*/, fd };
      }
  
      @Override
      protected void finalize() throws Throwable {
          try {
              if (mCloseGuard != null) {
                  mCloseGuard.warnIfOpen();
              }
  
              close();
          } finally {
              super.finalize();
          }
      }
  
      private final AtomicBoolean mClosed = new AtomicBoolean();
      private final CloseGuard mCloseGuard = CloseGuard.get();
      private final SensorManager mManager;
      private final int mNativeHandle;
      private final long mSize;
      private final int mType;
  }
  