context: //-----------------------------------------------------------------------
  // <copyright file="TangoApplication.cs" company="Google">
  //
  // Copyright 2016 Google Inc. All Rights Reserved.
  //
  // 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.
  //
  // </copyright>
  //-----------------------------------------------------------------------
  
  namespace Tango
  {
      using System;
      using System.Collections;
      using System.Collections.Generic;
      using System.IO;
      using System.Linq;
      using System.Runtime.InteropServices;
      using UnityEngine;
      using UnityEngine.Serialization;
  
      /// <summary>
      /// Delegate for permission callbacks.
      /// </summary>
      /// <param name="permissionsGranted"><c>true</c> if permissions were granted, otherwise <c>false</c>.</param>
      public delegate void PermissionsEvent(bool permissionsGranted);
  
      /// <summary>
      /// Delegate for service connection.
      /// </summary>
      public delegate void OnTangoConnectEventHandler();
  
      /// <summary>
      /// Delegate for service disconnection.
      /// </summary>
      public delegate void OnTangoDisconnectEventHandler();
  
      /// <summary>
      /// Delegate for service disconnection.
      /// </summary>
      /// <param name="displayRotation">Rotation of current display. Index enum is same same as Android screen
      /// rotation standard.</param>
      /// <param name="colorCameraRotation">Rotation of current color camera sensor. Index enum is same as Android
      /// camera rotation standard.</param>
      public delegate void OnDisplayChangedEventHandler(OrientationManager.Rotation displayRotation,
                                                        OrientationManager.Rotation colorCameraRotation);
  
      /// <summary>
      /// Main entry point for the Tango Service.
      /// 
      /// This component handles nearly all communication with the underlying TangoService.  You must have one of these
      /// in your scene for Tango to work.  Customization of the Tango connection can be done in the Unity editor or by
      /// programmatically setting the member flags.
      /// 
      /// This sends out events to Components that derive from the ITangoPose, ITangoDepth, etc. interfaces and
      /// register themselves via Register.  This also sends out events to callbacks passed in through 
      /// RegisterOnTangoConnect, RegisterOnTangoDisconnect, and RegisterPermissionsCallback.
      /// 
      /// Note: To connect to the Tango Service, you should call <c>InitApplication</c> after properly registering 
      /// everything.
      /// </summary>
      public class TangoApplication : MonoBehaviour
      {
          public bool m_autoConnectToService = false;
  
          public bool m_allowOutOfDateTangoAPI = false;
  #if UNITY_EDITOR
          /// <summary>
          /// Whether to show performance-related options for
          /// TangoApplication via the TangoInspector custom editor GUI.
          /// </summary>
          public bool m_showPerformanceOptionsInInspector = true;
  
          /// <summary>
          /// Whether to show emulation options for TangoApplication
          /// via the TangoInspector custom editor GUI.
          /// </summary>
          public bool m_showEmulationOptionsInInspector = false;
  
          /// <summary>
          /// Mesh used in emulating the physical world 
          /// (e.g. depth and color camera data).
          /// </summary>
          public Mesh m_emulationEnvironment;
  
          /// <summary>
          /// Optional texture to be applied to emulation environment mesh.
          /// </summary>
          public Texture m_emulationEnvironmentTexture;
  
          /// <summary>
          /// If true, use the more performance-demanding aspects of tango emulation
          /// (Currently, depth and color camera).
          /// </summary>
          public bool m_doSlowEmulation;
  
          /// <summary>
          /// Whether the emulation environment mesh should be lit
          /// (For example, a scanned/generated mesh of a room with color data
          /// should not be virtually lit, because it already represents real
          /// lighting).
          /// </summary>
          public bool m_emulationVideoOverlaySimpleLighting;
  
          public Vector3 m_emulatedAreaDescriptionStartOffset;
  #endif
  
          public bool m_enableMotionTracking = true;
          public bool m_motionTrackingAutoReset = true;
  
          [FormerlySerializedAs("m_enableADFLoading")]
          public bool m_enableAreaDescriptions = false;
          [FormerlySerializedAs("m_enableAreaLearning")]
          public bool m_areaDescriptionLearningMode = false;
  
          /// <summary>
          /// Toggle for experimental drift correction.
          /// 
          /// Drift-corrected frames come through the Area Description reference
          /// frame and currently cannot be used when an Area Description is loaded
          /// or when Area Learning is enabled.
          /// 
          /// There will be a period after Startup during which drift-corrected frames
          /// are not available.
          /// 
          /// Behavior is likely to change in the future.
          /// </summary>
          public bool m_enableDriftCorrection = false;
  
          public bool m_enableDepth = true;
  
          public bool m_enableAreaLearning = false;
          public bool m_enableADFLoading = false;
  
          public bool m_enable3DReconstruction = false;
          public float m_3drResolutionMeters = 0.1f;
          public bool m_3drGenerateColor = false;
          public bool m_3drGenerateNormal = false;
          public bool m_3drGenerateTexCoord = false;
          public bool m_3drSpaceClearing = false;
          public bool m_3drUseAreaDescriptionPose = false;
          public int m_3drMinNumVertices = 20;
          public Tango3DReconstruction.UpdateMethod m_3drUpdateMethod = Tango3DReconstruction.UpdateMethod.PROJECTIVE;
  
          public bool m_enableVideoOverlay = false;
          public bool m_videoOverlayUseTextureMethod = true;
  
          /// <summary>
          /// DEPRECATED. Will be removed in a future SDK.
          /// </summary>
          public bool m_videoOverlayUseYUVTextureIdMethod = false;
  
          public bool m_videoOverlayUseByteBufferMethod = false;
          
          /// <summary>
          /// Whether to keep screen always awake throughout the whole application session.
          /// </summary>
          public bool m_keepScreenAwake = false;
  
          /// <summary>
          /// Whether to adjust the size of the application's main render buffer
          /// for performance reasons (Some Tango devices have very high-resolution
          /// displays, so this option exists as a hint that this may be necessary).
          /// </summary>
          public bool m_adjustScreenResolution = false;
  
          /// <summary>
          /// Target resolution to reduce resolution to when m_adjustScreenResolution is
          /// enabled. Specifies the lesser of the two dimensions (i.e. height in landscape
          /// or width in portrait mode).
          /// </summary>
          public int m_targetResolution = 1080;
  
          /// <summary>
          /// If true, resolution adjustment will allow adjusting to a resolution
          /// larger than the display of the current device.
          /// 
          /// This is generally discouraged.
          /// </summary>
          public bool m_allowOversizedScreenResolutions = false;
  
          /// <summary>
          /// Inspector value for initial max point cloud size.
          /// To set point cloud max size at runtime, use SetMaxDepthPoints().
          /// 
          /// A value of 0 disables the feature.
          /// </summary>
          public int m_initialPointCloudMaxPoints = 0;
  
          /// <summary>
          /// Event triggered when display is rotated.
          /// </summary>
          public OnDisplayChangedEventHandler OnDisplayChanged;
  
          internal bool m_enableCloudADF = false;
  
          private const string CLASS_NAME = "TangoApplication";
          private static string m_tangoServiceVersion = string.Empty;
  
          /// <summary>
          /// Ratio of the screen's larger dimension (e.g. Screen.Width in landscape)
          /// to the screen's smaller dimension. Measured once, on the first time 
          /// TangoApplication awakes.
          /// </summary>
          private static float m_screenLandscapeAspectRatio = -1;
  
          /// <summary>
          /// If RequestPermissions() has been called automatically.
          /// 
          /// This only matters if m_autoConnectToService is set.
          /// </summary>
          private bool m_autoConnectRequestedPermissions = false;
  
          /// <summary>
          /// The connection to the Tango3DReconstruction library.
          /// </summary>
          private Tango3DReconstruction m_tango3DReconstruction;
  
          private PermissionsTypes m_requiredPermissions = 0;
          private IntPtr m_callbackContext = IntPtr.Zero;
  
          /// <summary>
          /// If true, the device has a compatible version of Tango Core. Otherwise false.
          /// </summary>
          private bool m_isTangoUpToDate = false;
  
          /// <summary>
          /// If true, the TangoService is running and providing data to client, e.g motion tracking 
          /// and point cloud data. Otherwise false.
          /// </summary>
          private bool m_isTangoStarted = false;
  
          /// <summary>
          /// If true, the TangoApplication instance will request permission when application is resumed.
          /// Otherwise false. This is used to handle Tango Service connection when application resumed from
          /// pause.
          /// </summary>
          private bool m_shouldReconnectService = false;
  
          /// <summary>
          /// The flag to transfer permission event from Android main thread to Unity main thread
          /// (Update function).
          /// </summary>
          private bool m_permissionsSuccessful = false;
  
          /// <summary>
          /// The last-known depth camera rate.
          /// </summary>
          private int m_appDepthCameraRate = 5;
  
          private YUVTexture m_yuvTexture;
          private TangoConfig m_tangoConfig;
          private TangoConfig m_tangoRuntimeConfig;
  
          /// <summary>
          /// Boolean value to check if application is currently paused in background.
          /// </summary>
          private bool m_isApplicationPaused = false;
  
          /// <summary>
          /// Lock for m_androidMessageQueue.
          /// </summary>
          private object m_messageQueueLock = new object();
  
          /// <summary>
          /// A message queue to pipe Android native callbacks (e.g OnResume, OnPause) to Unity main thread.
          /// </summary>
          private Queue<AndroidMessage> m_androidMessageQueue = new Queue<AndroidMessage>();
  
          /// <summary>
          /// Lock to protect Tango connect and disconnect.
          /// </summary>
          private object m_tangoLifecycleLock = new object();
  
          /// <summary>
          /// Occurs when permission event.
          /// </summary>
          private event PermissionsEvent PermissionEvent;
  
          /// <summary>
          /// Occurs when on tango connect.
          /// </summary>
          private event OnTangoConnectEventHandler OnTangoConnect;
  
          /// <summary>
          /// Occurs when on tango disconnect.
          /// </summary>
          private event OnTangoDisconnectEventHandler OnTangoDisconnect;
  
          /// <summary>
          /// Type of Android messages. This mainly mirrors the callback from Android activity.
          /// </summary>
          private enum AndroidMessageType
          {
              NONE,
              ON_PAUSE,
              ON_RESUME,
              ON_ACTIVITY_RESULT,
              ON_TANGO_SERVICE_CONNECTED,
              ON_TANGO_SERVICE_DISCONNECTED,
              ON_REQUEST_PERMISSION_RESULT,
              ON_DISPLAY_CHANGED
          }
  
          /// <summary>
          /// Permission types used by Tango applications.
          /// </summary>
          [Flags]
          private enum PermissionsTypes
          {
              // All entries must be a power of two for
              // use in a bit field as flags.
              NONE = 0,
              AREA_LEARNING = 0x1,
              ANDROID_CAMERA = 0x2,
              SERVICE_BOUND = 0x4,
          }
  
          /// <summary>
          /// Gets a value indicating whether this is connected to a Tango service.
          /// </summary>
          /// <value><c>true</c> if connected to a Tango service; otherwise, <c>false</c>.</value>
          public bool IsServiceConnected
          {
              get { return m_isTangoStarted; }
          }
  
          /// <summary>
          /// Gets the Tango config.  Useful for debugging.
          /// </summary>
          /// <value>The config.</value>
          internal TangoConfig Config
          {
              get { return m_tangoConfig; }
          }
  
          /// <summary>
          /// Gets the current Tango runtime config.  Useful for debugging.
          /// </summary>
          /// <value>The current runtime config.</value>
          internal TangoConfig RuntimeConfig
          {
              get { return m_tangoRuntimeConfig; }
          }
  
          /// <summary>
          /// Get the Tango service version name.
          /// </summary>
          /// <returns>String for the version name.</returns>
          public static string GetTangoServiceVersion()
          {
              if (m_tangoServiceVersion == string.Empty)
              {
                  m_tangoServiceVersion = AndroidHelper.GetTangoCoreVersionName();
              }
              
              return m_tangoServiceVersion;
          }
  
          /// <summary>
          /// DEPRECATED: Get the video overlay texture.
          /// </summary>
          /// <returns>The video overlay texture.</returns>
          public YUVTexture GetVideoOverlayTextureYUV()
          {
              return m_yuvTexture;
          }
  
          /// <summary>
          /// Register to get Tango callbacks.
          /// 
          /// The object should derive from one of ITangoDepth, ITangoEvent, ITangoPose, ITangoVideoOverlay, or
          /// ITangoExperimentalTangoVideoOverlay.  You will get callback during Update until you unregister.
          /// </summary>
          /// <param name="tangoObject">Object to get Tango callbacks from.</param>
          public void Register(object tangoObject)
          {
              ITangoAreaDescriptionEvent areaDescriptionEvent = tangoObject as ITangoAreaDescriptionEvent;
              if (areaDescriptionEvent != null)
              {
                  AreaDescriptionEventListener.Register(areaDescriptionEvent.OnAreaDescriptionImported,
                                                        areaDescriptionEvent.OnAreaDescriptionExported);
              }
  
              ITangoEvent tangoEvent = tangoObject as ITangoEvent;
              if (tangoEvent != null)
              {
                  TangoEventListener.RegisterOnTangoEventAvailable(tangoEvent.OnTangoEventAvailableEventHandler);
              }
  
              ITangoEventMultithreaded tangoEventMultithreaded = tangoObject as ITangoEventMultithreaded;
  
              if (tangoEventMultithreaded != null)
              {
                  TangoEventListener.RegisterOnTangoEventMultithreadedAvailable(
                      tangoEventMultithreaded.OnTangoEventMultithreadedAvailableEventHandler);
              }
  
              ITangoLifecycle tangoLifecycle = tangoObject as ITangoLifecycle;
              if (tangoLifecycle != null)
              {
                  _RegisterPermissionsCallback(tangoLifecycle.OnTangoPermissions);
                  _RegisterOnTangoConnect(tangoLifecycle.OnTangoServiceConnected);
                  _RegisterOnTangoDisconnect(tangoLifecycle.OnTangoServiceDisconnected);
              }
  
              if (m_enableMotionTracking)
              {
                  ITangoPose poseHandler = tangoObject as ITangoPose;
                  if (poseHandler != null)
                  {
                      PoseListener.RegisterTangoPoseAvailable(poseHandler.OnTangoPoseAvailable);
                  }
              }
  
              if (m_enableDepth)
              {
                  ITangoPointCloud pointCloudHandler = tangoObject as ITangoPointCloud;
                  if (pointCloudHandler != null)
                  {
                      DepthListener.RegisterOnPointCloudAvailable(pointCloudHandler.OnTangoPointCloudAvailable);
                  }
  
                  ITangoPointCloudMultithreaded pointCloudMultithreadedHandler
                      = tangoObject as ITangoPointCloudMultithreaded;
                  if (pointCloudMultithreadedHandler != null)
                  {
                      DepthListener.RegisterOnPointCloudMultithreadedAvailable(
                          pointCloudMultithreadedHandler.OnTangoPointCloudMultithreadedAvailable);
                  }
  
                  ITangoDepth depthHandler = tangoObject as ITangoDepth;
                  if (depthHandler != null)
                  {
                      DepthListener.RegisterOnTangoDepthAvailable(depthHandler.OnTangoDepthAvailable);
                  }
  
                  ITangoDepthMultithreaded depthMultithreadedHandler = tangoObject as ITangoDepthMultithreaded;
                  if (depthMultithreadedHandler != null)
                  {
                      DepthListener.RegisterOnTangoDepthMultithreadedAvailable(
                          depthMultithreadedHandler.OnTangoDepthMultithreadedAvailable);
                  }
              }
              
              if (m_enableVideoOverlay)
              {
                  if (m_videoOverlayUseTextureMethod)
                  {
                      ITangoCameraTexture handler = tangoObject as ITangoCameraTexture;
                      if (handler != null)
                      {
                          VideoOverlayListener.RegisterOnTangoCameraTextureAvailable(handler.OnTangoCameraTextureAvailable);
                      }
                  }
  
                  if (m_videoOverlayUseYUVTextureIdMethod)
                  {
                      IExperimentalTangoVideoOverlay handler = tangoObject as IExperimentalTangoVideoOverlay;
                      if (handler != null)
                      {
                          VideoOverlayListener.RegisterOnTangoYUVTextureAvailable(handler.OnExperimentalTangoImageAvailable);
                      }
                  }
  
                  if (m_videoOverlayUseByteBufferMethod)
                  {
                      ITangoVideoOverlay handler = tangoObject as ITangoVideoOverlay;
                      if (handler != null)
                      {
                          VideoOverlayListener.RegisterOnTangoImageAvailable(handler.OnTangoImageAvailableEventHandler);
                      }
  
                      ITangoVideoOverlayMultithreaded multithreadedHandler = tangoObject as ITangoVideoOverlayMultithreaded;
                      if (multithreadedHandler != null)
                      {
                          VideoOverlayListener.RegisterOnTangoImageMultithreadedAvailable(multithreadedHandler.OnTangoImageMultithreadedAvailable);
                      }
                  }
              }
  
              if (m_enable3DReconstruction)
              {
                  ITango3DReconstruction t3drHandler = tangoObject as ITango3DReconstruction;
                  if (t3drHandler != null && m_tango3DReconstruction != null)
                  {
                      m_tango3DReconstruction.RegisterGridIndicesDirty(t3drHandler.OnTango3DReconstructionGridIndicesDirty);
                  }
              }
          }
  
          /// <summary>
          /// Unregister from Tango callbacks.
          /// 
          /// See TangoApplication.Register for more details.
          /// </summary>
          /// <param name="tangoObject">Object to stop getting Tango callbacks from.</param>
          public void Unregister(System.Object tangoObject)
          {
              ITangoAreaDescriptionEvent areaDescriptionEvent = tangoObject as ITangoAreaDescriptionEvent;
              if (areaDescriptionEvent != null)
              {
                  AreaDescriptionEventListener.Unregister(areaDescriptionEvent.OnAreaDescriptionImported,
                                                          areaDescriptionEvent.OnAreaDescriptionExported);
              }
  
              ITangoEvent tangoEvent = tangoObject as ITangoEvent;
              if (tangoEvent != null)
              {
                  TangoEventListener.UnregisterOnTangoEventAvailable(tangoEvent.OnTangoEventAvailableEventHandler);
              }
  
              ITangoEventMultithreaded tangoEventMultithreaded = tangoObject as ITangoEventMultithreaded;
              if (tangoEventMultithreaded != null)
              {
                  TangoEventListener.UnregisterOnTangoEventMultithreadedAvailable(
                      tangoEventMultithreaded.OnTangoEventMultithreadedAvailableEventHandler);
              }
  
              ITangoLifecycle tangoLifecycle = tangoObject as ITangoLifecycle;
              if (tangoLifecycle != null)
              {
                  _UnregisterPermissionsCallback(tangoLifecycle.OnTangoPermissions);
                  _UnregisterOnTangoConnect(tangoLifecycle.OnTangoServiceConnected);
                  _UnregisterOnTangoDisconnect(tangoLifecycle.OnTangoServiceDisconnected);
              }
  
              if (m_enableMotionTracking)
              {
                  ITangoPose poseHandler = tangoObject as ITangoPose;
                  if (poseHandler != null)
                  {
                      PoseListener.UnregisterTangoPoseAvailable(poseHandler.OnTangoPoseAvailable);
                  }
              }
  
              if (m_enableDepth)
              {
                  ITangoPointCloud pointCloudHandler = tangoObject as ITangoPointCloud;
                  if (pointCloudHandler != null)
                  {
                      DepthListener.UnregisterOnPointCloudAvailable(pointCloudHandler.OnTangoPointCloudAvailable);
                  }
  
                  ITangoPointCloudMultithreaded pointCloudMultithreaded = tangoObject as ITangoPointCloudMultithreaded;
                  if (pointCloudMultithreaded != null)
                  {
                      DepthListener.UnregisterOnPointCloudMultithreadedAvailable(
                          pointCloudMultithreaded.OnTangoPointCloudMultithreadedAvailable);
                  }
  
                  ITangoDepth depthHandler = tangoObject as ITangoDepth;
                  if (depthHandler != null)
                  {
                      DepthListener.UnregisterOnTangoDepthAvailable(depthHandler.OnTangoDepthAvailable);
                  }
  
                  ITangoDepthMultithreaded depthMultithreadedHandler = tangoObject as ITangoDepthMultithreaded;
                  if (depthMultithreadedHandler != null)
                  {
                      DepthListener.UnregisterOnTangoDepthMultithreadedAvailable(
                          depthMultithreadedHandler.OnTangoDepthMultithreadedAvailable);
                  }
              }
  
              if (m_enableVideoOverlay)
              {
                  if (m_videoOverlayUseTextureMethod)
                  {
                      ITangoCameraTexture handler = tangoObject as ITangoCameraTexture;
                      if (handler != null)
                      {
                          VideoOverlayListener.UnregisterOnTangoCameraTextureAvailable(handler.OnTangoCameraTextureAvailable);
                      }
                  }
  
                  if (m_videoOverlayUseYUVTextureIdMethod)
                  {
                      IExperimentalTangoVideoOverlay handler = tangoObject as IExperimentalTangoVideoOverlay;
                      if (handler != null)
                      {
                          VideoOverlayListener.UnregisterOnTangoYUVTextureAvailable(handler.OnExperimentalTangoImageAvailable);
                      }
                  }
  
                  if (m_videoOverlayUseByteBufferMethod)
                  {
                      ITangoVideoOverlay handler = tangoObject as ITangoVideoOverlay;
                      if (handler != null)
                      {
                          VideoOverlayListener.UnregisterOnTangoImageAvailable(handler.OnTangoImageAvailableEventHandler);
                      }
  
                      ITangoVideoOverlayMultithreaded multithreadedHandler = tangoObject as ITangoVideoOverlayMultithreaded;
                      if (multithreadedHandler != null)
                      {
                          VideoOverlayListener.UnregisterOnTangoImageMultithreadedAvailable(multithreadedHandler.OnTangoImageMultithreadedAvailable);
                      }
                  }
              }
  
              if (m_enable3DReconstruction)
              {
                  ITango3DReconstruction t3drHandler = tangoObject as ITango3DReconstruction;
                  if (t3drHandler != null && m_tango3DReconstruction != null)
                  {
                      m_tango3DReconstruction.UnregisterGridIndicesDirty(t3drHandler.OnTango3DReconstructionGridIndicesDirty);
                  }
              }
          }
  
          /// <summary>
          /// Check if all requested permissions have been granted.
          /// </summary>
          /// <returns><c>true</c> if all requested permissions were granted; otherwise, <c>false</c>.</returns>
          public bool HasRequestedPermissions()
          {
              return m_requiredPermissions == PermissionsTypes.NONE;
          }
  
          /// <summary>
          /// Manual initialization step 1: Call this to request Tango permissions.
          /// 
          /// To know the result of the permissions request, implement the interface ITangoLifecycle and register
          /// yourself before calling this.
          /// 
          /// Once all permissions have been granted, you can call TangoApplication.Startup, optionally passing in the
          /// AreaDescription to load.  You can get the list of AreaDescriptions once the appropriate permission is
          /// granted.
          /// </summary>
          public void RequestPermissions()
          {
  #if UNITY_EDITOR
              m_requiredPermissions = PermissionsTypes.NONE;
  #else
              if (m_requiredPermissions == PermissionsTypes.NONE)
              {
                  if (m_enableVideoOverlay || m_enableDepth)
                  {
                      if (!AndroidHelper.CheckPermission(Common.ANDROID_CAMERA_PERMISSION))
                      {
                          m_requiredPermissions |= PermissionsTypes.ANDROID_CAMERA;
                      }
                  }
  
                  if (m_enableAreaDescriptions && !m_enableDriftCorrection)
                  {
                      if (!AndroidHelper.ApplicationHasTangoPermissions(Common.TANGO_ADF_LOAD_SAVE_PERMISSIONS))
                      {
                          m_requiredPermissions |= PermissionsTypes.AREA_LEARNING;
                      }
                  }
              }
  
              // It is always required to rebind to the service.
              m_requiredPermissions |= PermissionsTypes.SERVICE_BOUND;
  #endif
              // If no permissions are needed, send all permission granted event.
              // Ohterwise, start request permission.
              if (m_requiredPermissions == PermissionsTypes.NONE)
              {
                  _SendPermissionEvent(true);
              }
              else
              {
                  _RequestNextPermission();
              }
          }
  
          /// <summary>
          /// Manual initialization step 2: Call this to connect to the Tango service.
          /// 
          /// After connecting to the Tango service, you will get updates for Motion Tracking, Depth Sensing, and Area
          /// Learning.  If you have a specific Area Description you want to localize too, pass that Area Description in
          /// here.
          /// </summary>
          /// <param name="areaDescription">If not null, the Area Description to localize to.</param>
          public void Startup(AreaDescription areaDescription)
          {
              // Make sure all required permissions have been granted.
              if (m_requiredPermissions != PermissionsTypes.NONE)
              {
                  Debug.Log(CLASS_NAME + ".Startup() -- ERROR: Not all required permissions were accepted yet. Needed " +
                            "permission: " + m_requiredPermissions);
                  return;
              }
  
              if (!_CheckTangoVersion())
              {
                  // Error logged in _CheckTangoVersion function.
                  return;
              }
  
              // Setup configs.
              m_tangoConfig = new TangoConfig(TangoEnums.TangoConfigType.TANGO_CONFIG_DEFAULT);
              m_tangoRuntimeConfig = new TangoConfig(TangoEnums.TangoConfigType.TANGO_CONFIG_RUNTIME);
  
              if (m_enableVideoOverlay && m_videoOverlayUseYUVTextureIdMethod)
              {
                  int yTextureWidth = 0;
                  int yTextureHeight = 0;
                  int uvTextureWidth = 0;
                  int uvTextureHeight = 0;
                  
                  m_tangoConfig.GetInt32(TangoConfig.Keys.EXPERIMENTAL_Y_TEXTURE_WIDTH, ref yTextureWidth);
                  m_tangoConfig.GetInt32(TangoConfig.Keys.EXPERIMENTAL_Y_TEXTURE_HEIGHT, ref yTextureHeight);
                  m_tangoConfig.GetInt32(TangoConfig.Keys.EXPERIMENTAL_UV_TEXTURE_WIDTH, ref uvTextureWidth);
                  m_tangoConfig.GetInt32(TangoConfig.Keys.EXPERIMENTAL_UV_TEXTURE_HEIGHT, ref uvTextureHeight);
                  
                  if (yTextureWidth == 0 || yTextureHeight == 0 || uvTextureWidth == 0 || uvTextureHeight == 0)
                  {
                      Debug.Log("Video overlay texture sizes were not set properly");
                  }
                  
                  m_yuvTexture.ResizeAll(yTextureWidth, yTextureHeight, uvTextureWidth, uvTextureHeight);
              }
  
              if (areaDescription != null)
              {
                  _InitializeMotionTracking(areaDescription.m_uuid);
              }
              else
              {
                  _InitializeMotionTracking(null);
              }
  
              if (m_tangoConfig.SetBool(TangoConfig.Keys.ENABLE_DEPTH_PERCEPTION_BOOL, m_enableDepth) 
                  && m_tangoConfig.SetInt32(TangoConfig.Keys.DEPTH_MODE, (int)TangoConfig.DepthMode.XYZC)
                  && m_enableDepth)
              {
                  DepthListener.SetCallback();
              }
  
              if (m_tangoConfig.SetBool(TangoConfig.Keys.ENABLE_COLOR_CAMERA_BOOL, m_enableVideoOverlay) && 
                  m_enableVideoOverlay)
              {
                  _SetVideoOverlayCallbacks();
              }
  
              TangoEventListener.SetCallback();
  
              if (m_enable3DReconstruction)
              {
                  Register(m_tango3DReconstruction);
              }
  
              _TangoConnect();
          }
  
          /// <summary>
          /// Disconnect from the Tango service.
          /// 
          /// This is called automatically when the TangoApplication goes away.  You only need 
          /// to call this to disconnect from the Tango service before the TangoApplication goes
          /// away.
          /// </summary>
          public void Shutdown()
          {
              Debug.Log("Tango Shutdown");
              _TangoDisconnect();
          }
  
          /// <summary>
          /// Set the frame rate of the depth camera.
          /// 
          /// Disabling or reducing the frame rate of the depth camera when it is running can save a significant amount
          /// of battery.
          /// </summary>
          /// <param name="rate">The rate in frames per second, for the depth camera to run at.</param>
          public void SetDepthCameraRate(int rate)
          {
              _SetDepthCameraRate(rate);
              m_appDepthCameraRate = rate;
          }
  
          /// <summary>
          /// Set the frame rate of the depth camera.
          /// 
          /// Disabling or reducing the frame rate of the depth camera when it is running can save a significant amount
          /// of battery.
          /// </summary>
          /// <param name="rate">A special rate to set the depth camera to.</param>
          public void SetDepthCameraRate(TangoEnums.TangoDepthCameraRate rate)
          {
              switch (rate)
              {
              case TangoEnums.TangoDepthCameraRate.DISABLED:
                  SetDepthCameraRate(0);
                  break;
                  
              case TangoEnums.TangoDepthCameraRate.MAXIMUM:
                  // Set the depth frame rate to a sufficiently high number, it will get rounded down.  There is no 
                  // way to actually get the maximum value to pass in.
                  SetDepthCameraRate(5);
                  break;
              }
          }
  
          /// <summary>
          /// A cap on the number of points allowable in a point cloud; the point cloud will be reduced to this size if
          /// it exceeds it. 
          /// 
          /// Reducing the point cloud density will not reduce the time it takes for the native Tango process to
          /// produce depth data (and may in fact incur a small penalty). Performance gains come strictly from operations 
          /// downstream of point cloud creation (e.g. converting the depth cloud points into Unity coordinates, 
          /// point cloud rendering, plane-finding, etc).
          /// </summary>
          /// <param name="maxDepthPoints">Maximum number of depth points. A value of 0 means no limit.</param>
          public void SetMaxDepthPoints(int maxDepthPoints)
          {
              DepthListener.SetPointCloudLimit(maxDepthPoints);
          }
  
          /// <summary>
          /// Clear the 3D Reconstruction data.  The reconstruction will start fresh.
          /// </summary>
          public void Tango3DRClear()
          {
              if (m_tango3DReconstruction != null)
              {
                  m_tango3DReconstruction.Clear();
              }
          }
  
          /// <summary>
          /// Extract a single grid cell's mesh.
          /// </summary>
          /// <returns>Status of the extraction.</returns>
          /// <param name="gridIndex">Grid index to extract.</param>
          /// <param name="vertices">Filled out with extracted vertices.</param>
          /// <param name="normals">Filled out with extracted normals.</param>
          /// <param name="colors">Filled out with extracted colors.</param>
          /// <param name="triangles">Filled out with extracted triangle indices.</param>
          /// <param name="numVertices">Filled out with the number of valid vertices.</param>
          /// <param name="numTriangles">Filled out with the number of valid triangles.</param>
          public Tango3DReconstruction.Status Tango3DRExtractMeshSegment(
              Tango3DReconstruction.GridIndex gridIndex, Vector3[] vertices, Vector3[] normals, Color32[] colors, 
              int[] triangles, out int numVertices, out int numTriangles)
          {
              if (m_tango3DReconstruction != null)
              {
                  return m_tango3DReconstruction.ExtractMeshSegment(gridIndex, vertices, normals, colors, triangles,
                                                                    out numVertices, out numTriangles);
              }
  
              numVertices = 0;
              numTriangles = 0;
              return Tango3DReconstruction.Status.INVALID;
          }
  
          /// <summary>
          /// Extract a single mesh for the entire 3D Reconstruction state.
          /// </summary>
          /// <returns>Status of the extraction.</returns>
          /// <param name="vertices">Filled out with extracted vertices.</param>
          /// <param name="normals">Filled out with extracted normals.</param>
          /// <param name="colors">Filled out with extracted colors.</param>
          /// <param name="triangles">Filled out with extracted triangle indices.</param>
          /// <param name="numVertices">Filled out with the number of valid vertices.</param>
          /// <param name="numTriangles">Filled out with the number of valid triangles.</param>
          public Tango3DReconstruction.Status Tango3DRExtractWholeMesh(
              Vector3[] vertices, Vector3[] normals, Color32[] colors, int[] triangles, out int numVertices,
              out int numTriangles)
          {
              if (m_tango3DReconstruction != null)
              {
                  return m_tango3DReconstruction.ExtractWholeMesh(vertices, normals, colors, triangles, out numVertices,
                                                                  out numTriangles);
              }
  
              numVertices = 0;
              numTriangles = 0;
              return Tango3DReconstruction.Status.INVALID;
          }
  
          /// <summary>
          /// Extract an array of <c>SignedDistanceVoxel</c> objects.
          /// </summary>
          /// <returns>
          /// Returns Status.SUCCESS if the voxels are fully extracted and stared in the array.  In this case, <c>numVoxels</c>
          /// will say how many voxels are used, the rest of the array is untouched.
          /// 
          /// Returns Status.INVALID if the array length does not exactly equal the number of voxels in a single grid
          /// index.  By default, the number of voxels in a grid index is 16*16*16.
          /// 
          /// Returns Status.INVALID if some other error occurs.
          /// </returns>
          /// <param name="gridIndex">Grid index to extract.</param>
          /// <param name="voxels">
          /// On successful extraction this will get filled out with the signed distance voxels.
          /// </param>
          /// <param name="numVoxels">Number of voxels filled out.</param>
          public Tango3DReconstruction.Status Tango3DRExtractSignedDistanceVoxel(
              Tango3DReconstruction.GridIndex gridIndex, Tango3DReconstruction.SignedDistanceVoxel[] voxels,
              out int numVoxels)
          {
              if (m_tango3DReconstruction != null)
              {
                  return m_tango3DReconstruction.ExtractSignedDistanceVoxel(gridIndex, voxels, out numVoxels);
              }
  
              numVoxels = 0;
              return Tango3DReconstruction.Status.INVALID;
          }
  
          /// <summary>
          /// Enable or disable the 3D Reconstruction.
          /// </summary>
          /// <param name="enabled">If set to <c>true</c> enabled.</param>
          public void Set3DReconstructionEnabled(bool enabled)
          {
              m_tango3DReconstruction.SetEnabled(enabled);
          }
  
          /// <summary>
          /// Register to get an event callback when all permissions are granted.
          /// 
          /// The passed event will get called once all Tango permissions have been granted.  Registering 
          /// after all permissions have already been granted will cause the event to never fire.
          /// </summary>
          /// <param name="permissionsEventHandler">Event to call.</param>
          private void _RegisterPermissionsCallback(PermissionsEvent permissionsEventHandler)
          {
              if (permissionsEventHandler != null)
              {
                  PermissionEvent += permissionsEventHandler;
              }
          }
  
          /// <summary>
          /// Unregister from the permission callbacks.
          /// 
          /// See TangoApplication.RegisterPermissionsCallback for more details.
          /// </summary>
          /// <param name="permissionsEventHandler">Event to remove.</param>
          private void _UnregisterPermissionsCallback(PermissionsEvent permissionsEventHandler)
          {
              if (permissionsEventHandler != null)
              {
                  PermissionEvent -= permissionsEventHandler;
              }
          }
  
          /// <summary>
          /// Register to get an event callback when connected to the Tango service.
          /// 
          /// The passed event will get called once connected to the Tango service.  Registering 
          /// after already connected will cause the event to not fire until disconnected and then
          /// connecting again.
          /// </summary>
          /// <param name="handler">Event to call.</param>
          private void _RegisterOnTangoConnect(OnTangoConnectEventHandler handler)
          {
              if (handler != null)
              {
                  OnTangoConnect += handler;
              }
          }
  
          /// <summary>
          /// Unregister from the callback when connected to the Tango service.
          /// 
          /// See TangoApplication.RegisterOnTangoConnect for more details.
          /// </summary>
          /// <param name="handler">Event to remove.</param>
          private void _UnregisterOnTangoConnect(OnTangoConnectEventHandler handler)
          {
              if (handler != null)
              {
                  OnTangoConnect -= handler;
              }
          }
  
          /// <summary>
          /// Register to get an event callback when disconnected from the Tango service.
          /// 
          /// The passed event will get called when disconnected from the Tango service.
          /// </summary>
          /// <param name="handler">Event to remove.</param>
          private void _RegisterOnTangoDisconnect(OnTangoDisconnectEventHandler handler)
          {
              if (handler != null)
              {
                  OnTangoDisconnect += handler;
              }
          }
  
          /// <summary>
          /// Unregister from the callback when disconnected from the Tango service.
          /// 
          /// See TangoApplication.RegisterOnTangoDisconnect for more details.
          /// </summary>
          /// <param name="handler">Event to remove.</param>
          private void _UnregisterOnTangoDisconnect(OnTangoDisconnectEventHandler handler)
          {
              if (handler != null)
              {
                  OnTangoDisconnect -= handler;
              }
          }
  
          /// <summary>
          /// Set callbacks for all VideoOverlayListener objects.
          /// </summary>
          private void _SetVideoOverlayCallbacks()
          {
              Debug.Log("TangoApplication._SetVideoOverlayCallbacks()");
  
              if (m_videoOverlayUseTextureMethod)
              {
                  VideoOverlayListener.SetCallbackTextureMethod();
              }
  
              if (m_videoOverlayUseYUVTextureIdMethod)
              {
                  VideoOverlayListener.SetCallbackYUVTextureIdMethod(m_yuvTexture);
              }
  
              if (m_videoOverlayUseByteBufferMethod)
              {
                  VideoOverlayListener.SetCallbackByteBufferMethod();
              }
          }
          
          /// <summary>
          /// Initialize motion tracking.
          /// </summary>
          /// <param name="uuid">ADF UUID to load.</param>
          private void _InitializeMotionTracking(string uuid)
          {
              Debug.Log("TangoApplication._InitializeMotionTracking(" + uuid + ")");
  
              System.Collections.Generic.List<TangoCoordinateFramePair> framePairs = new System.Collections.Generic.List<TangoCoordinateFramePair>();
  
              bool usedUUID = false;
              if (m_tangoConfig.SetBool(TangoConfig.Keys.ENABLE_MOTION_TRACKING_BOOL, m_enableMotionTracking) && m_enableMotionTracking)
              {
                  TangoCoordinateFramePair motionTracking;
                  motionTracking.baseFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_START_OF_SERVICE;
                  motionTracking.targetFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_DEVICE;
                  framePairs.Add(motionTracking);
  
                  if (m_enableAreaDescriptions)
                  {
                      if (!m_enableDriftCorrection)
                      {
                          m_tangoConfig.SetBool(TangoConfig.Keys.ENABLE_AREA_LEARNING_BOOL, m_areaDescriptionLearningMode);
  
                          if (!string.IsNullOrEmpty(uuid))
                          {
                              if (m_tangoConfig.SetString(TangoConfig.Keys.LOAD_AREA_DESCRIPTION_UUID_STRING, uuid))
                              {
                                  usedUUID = true;
                              }
                          }
  
                          if (m_enableCloudADF)
                          {
                              m_tangoConfig.SetString(TangoConfig.Keys.LOAD_AREA_DESCRIPTION_UUID_STRING, string.Empty);
                              m_tangoConfig.SetBool(TangoConfig.Keys.ENABLE_CLOUD_ADF_BOOL, true);
                              Debug.Log("Local AreaDescription cannot be loaded when cloud ADF is enabled, Tango is starting" +
                                        "with cloud Area Description only." + Environment.StackTrace);
                          }
                      }
                      else
                      {
                          m_tangoConfig.SetBool(TangoConfig.Keys.EXPERIMENTAL_ENABLE_DRIFT_CORRECTION_BOOL,
                                                m_enableDriftCorrection);
                      }
  
                      TangoCoordinateFramePair areaDescription;
                      areaDescription.baseFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_AREA_DESCRIPTION;
                      areaDescription.targetFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_DEVICE;
                      
                      TangoCoordinateFramePair startToADF;
                      startToADF.baseFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_AREA_DESCRIPTION;
                      startToADF.targetFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_START_OF_SERVICE;
                      
                      framePairs.Add(areaDescription);
                      framePairs.Add(startToADF);
                  }
              }
  
              if (framePairs.Count > 0)
              {
                  PoseListener.SetCallback(framePairs.ToArray());
              }
  
              // The C API does not default this to on, but it is locked down.
              m_tangoConfig.SetBool(TangoConfig.Keys.ENABLE_LOW_LATENCY_IMU_INTEGRATION, true);
  
              m_tangoConfig.SetBool(TangoConfig.Keys.ENABLE_MOTION_TRACKING_AUTO_RECOVERY_BOOL, m_motionTrackingAutoReset);
  
              // Check if the UUID passed in was actually used.
              if (!usedUUID && !string.IsNullOrEmpty(uuid))
              {
                  Debug.Log("An AreaDescription UUID was passed in, but motion tracking and area descriptions are not "
                            + "both enabled." + Environment.StackTrace);
              }
  
  #if UNITY_EDITOR
              EmulatedAreaDescriptionHelper.InitEmulationForUUID(uuid, m_enableAreaDescriptions, m_areaDescriptionLearningMode,
                                                                 m_enableDriftCorrection, m_emulatedAreaDescriptionStartOffset);
  #endif
          }
  
          /// <summary>
          /// Validate the TangoService version is supported.
          /// </summary>
          /// <returns>Returns <c>true</c> if Tango version is compatible, otherwise not.</returns>
          private bool _CheckTangoVersion()
          {
              if (!AndroidHelper.IsTangoCoreUpToDate())
              {
                  Debug.Log(string.Format(CLASS_NAME + ".Initialize() Invalid API version. Please update Project Tango Core to at least {0}.", AndroidHelper.TANGO_MINIMUM_VERSION_CODE));
                  if (!m_allowOutOfDateTangoAPI)
                  {
                      TangoUx ux = GetComponent<TangoUx>();
  
                      if (ux != null && ux.isActiveAndEnabled)
                      {
                          ux.ShowTangoOutOfDate();
                      }
  
                      return false;
                  }
              }
  
              m_isTangoUpToDate = true;
              Debug.Log(CLASS_NAME + ".Initialize() Tango was initialized!");
              return true;
          }
  
          /// <summary>
          /// Set the frame rate of the depth camera.
          /// Unlike public SetDepthCameraRate function. This function doesn't set m_appDepthCameraRate.
          ///
          /// Disabling or reducing the frame rate of the depth camera when it is running can save a significant amount
          /// of battery.
          /// </summary>
          /// <param name="rate">The rate in frames per second, for the depth camera to run at.</param>
          private void _SetDepthCameraRate(int rate)
          {
              if (rate < 0)
              {
                  Debug.Log("Invalid rate passed to SetDepthCameraRate");
                  return;
              }
  
              m_tangoRuntimeConfig.SetInt32(TangoConfig.Keys.RUNTIME_DEPTH_FRAMERATE, rate);
              m_tangoRuntimeConfig.SetRuntimeConfig();
          }
          
          /// <summary>
          /// Connect to the Tango Service.
          /// </summary>
          private void _TangoConnect()
          {
              Debug.Log("TangoApplication._TangoConnect()");
  
              if (!m_isTangoUpToDate)
              {
                  return;
              }
  
              lock (m_tangoLifecycleLock)
              {
                  if (!m_isTangoStarted && !m_isApplicationPaused)
                  {
                      m_isTangoStarted = true;
  
                      AndroidHelper.PerformanceLog("Unity _TangoConnect start");
                      if (API.TangoService_connect(m_callbackContext, m_tangoConfig.GetHandle()) != Common.ErrorType.TANGO_SUCCESS)
                      {
                          AndroidHelper.ShowAndroidToastMessage("Failed to connect to Tango Service.");
                          Debug.Log(CLASS_NAME + ".Connect() Could not connect to the Tango Service!");
                      }
                      else
                      {
                          AndroidHelper.PerformanceLog("Unity _TangoConnect end");
                          Debug.Log(CLASS_NAME + ".Connect() Tango client connected to service!");
                      }
  
                      if (OnTangoConnect != null)
                      {
                          OnTangoConnect();
                      }
                  }
              }
          }
          
          /// <summary>
          /// Disconnect from the Tango Service.
          /// </summary>
          private void _TangoDisconnect()
          {
              lock (m_tangoLifecycleLock)
              {
                  if (!m_isTangoStarted)
                  {
                      AndroidHelper.UnbindTangoService();
                      Debug.Log(CLASS_NAME + ".Disconnect() Not disconnecting from Tango Service "
  
gt:                               + "as this TangoApplication was not connected");