private static extern int GvrShimUnity_getControllerState(int device, ref GvrShimUnityControllerState state);
        /// Reads the controller's current state and stores it in outState.
        public void ReadState(ControllerState outState, int controller_id)
        {
            int retval = 0;

            if (lastUpdateFrame != Time.frameCount)
            {
                lastUpdateFrame = Time.frameCount;

                retval = GvrShimUnity_updateState();
                if (retval != GVR_SHIM_OK)
                {
                    Debug.LogError("GvrShimUnity_updateState returned error.");
                    return;
                }
            }

            int connStatus = 0;

            retval = GvrShimUnity_getControllerConnectionStatus(controller_id, ref connStatus);
            if (retval != GVR_SHIM_OK)
            {
                Debug.LogError("GvrShimUnity_getControllerConnectionStatus returned error.");
                return;
            }
            outState.connectionState = ConvertConnectionState(connStatus);

            // Early out if not connected.  No other state is relevant.
            if (outState.connectionState != GvrConnectionState.Connected)
            {
                return;
            }

            GvrShimUnityControllerState aState = new GvrShimUnityControllerState();

            retval = GvrShimUnity_getControllerState(controller_id, ref aState);
            if (retval != GVR_SHIM_OK)
            {
                Debug.LogError("GvrShimUnity_getControllerState returned error.");
                return;
            }

            // Convert GVR API orientation (right-handed) into Unity axis system (left-handed).
            pose3d.Set(aState.position, aState.orientation);
            pose3d.SetRightHanded(pose3d.Matrix);
            outState.orientation = pose3d.Orientation;
            outState.position    = pose3d.Position;

            // For accelerometer, we have to flip Z because the GVR API has Z pointing backwards
            // and Unity has Z pointing forward.
            outState.accel    = aState.acceleration;
            outState.accel.z *= -1;

            // Gyro in GVR represents a right-handed angular velocity about each axis (positive means
            // clockwise when sighting along axis). Since Unity uses a left-handed system, we flip the
            // signs to adjust the sign of the rotational velocity (so that positive means
            // counter-clockwise). In addition, since in Unity the Z axis points forward while GVR
            // has Z pointing backwards, we flip the Z axis sign again. So the result is that
            // we should use -X, -Y, +Z:
            outState.gyro = aState.gyro;
            outState.gyro.Scale(new Vector3(-1, -1, 1));

            outState.touchPos = aState.touchPos;
            // Shim outputs centered touchpos coordinates, but the ControllerState struct is
            // top-left coordinates. Convert back to top-left.
            outState.touchPos.x = (outState.touchPos.x / 2.0f) + 0.5f;
            outState.touchPos.y = (-outState.touchPos.y / 2.0f) + 0.5f;

            outState.is6DoF = (aState.trackedDataAvailable &
                               (int)GvrShimTrackedDataAvailableFlags.PositionAvailable) != 0;
            outState.buttonsState = (GvrControllerButton)aState.buttonState;
            // Derive button up/down state from Unity perspective, as it can miss
            // up/downs from platform, like on pause/resume.
            if (controller_id >= 0 && controller_id < lastButtonStates.Length)
            {
                outState.SetButtonsUpDownFromPrevious(lastButtonStates[controller_id]);
                lastButtonStates[controller_id] = outState.buttonsState;
            }
            outState.recentered   = (aState.recentered != 0);
            outState.isCharging   = (aState.batteryCharging != 0);
            outState.batteryLevel = (GvrControllerBatteryLevel)aState.batteryLevel;
        }