17b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala/*
27b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala * Copyright (C) 2018 The Android Open Source Project
37b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala *
47b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala * Licensed under the Apache License, Version 2.0 (the "License");
57b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala * you may not use this file except in compliance with the License.
67b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala * You may obtain a copy of the License at
77b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala *
87b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala *      http://www.apache.org/licenses/LICENSE-2.0
97b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala *
107b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala * Unless required by applicable law or agreed to in writing, software
117b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala * distributed under the License is distributed on an "AS IS" BASIS,
127b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala * See the License for the specific language governing permissions and
147b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala * limitations under the License.
157b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala */
167b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
177b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala#ifndef ANDROID_SERVERS_DISTORTIONMAPPER_H
187b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala#define ANDROID_SERVERS_DISTORTIONMAPPER_H
197b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
207b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala#include <utils/Errors.h>
217b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala#include <array>
227b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala#include <mutex>
237b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
247b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala#include "camera/CameraMetadata.h"
257b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
267b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvalanamespace android {
277b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
287b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvalanamespace camera3 {
297b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
307b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala/**
317b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala * Utilities to transform between raw (distorted) and warped (corrected) coordinate systems
327b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala * for cameras that support geometric distortion
337b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala */
347b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvalaclass DistortionMapper {
357b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala  public:
367b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    DistortionMapper();
377b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
387b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    /**
397b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Check whether distortion correction is supported by the camera HAL
407b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     */
417b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    static bool isDistortionSupported(const CameraMetadata &deviceInfo);
427b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
437b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    /**
447b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Update static lens calibration info from camera characteristics
457b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     */
467b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    status_t setupStaticInfo(const CameraMetadata &deviceInfo);
477b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
487b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    /**
497b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Return whether distortion correction can be applied currently
507b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     */
517b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    bool calibrationValid() const;
527b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
537b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    /**
547b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Correct capture request if distortion correction is enabled
557b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     */
567b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    status_t correctCaptureRequest(CameraMetadata *request);
577b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
587b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    /**
597b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Correct capture result if distortion correction is enabled
607b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     */
617b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    status_t correctCaptureResult(CameraMetadata *request);
627b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
637b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
647b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala  public: // Visible for testing. Not guarded by mutex; do not use concurrently
657b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    /**
667b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Update lens calibration from capture results or equivalent
677b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     */
687b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    status_t updateCalibration(const CameraMetadata &result);
697b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
707b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    /**
717b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Transform from distorted (original) to corrected (warped) coordinates.
727b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Coordinates are transformed in-place
737b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     *
747b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     *   coordPairs: A pointer to an array of consecutive (x,y) points
757b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     *   coordCount: Number of (x,y) pairs to transform
767b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     */
777b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    status_t mapRawToCorrected(int32_t *coordPairs, int coordCount);
787b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
797b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    /**
807b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Transform from distorted (original) to corrected (warped) coordinates.
817b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Coordinates are transformed in-place
827b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     *
837b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     *   rects: A pointer to an array of consecutive (x,y, w, h) rectangles
847b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     *   rectCount: Number of rectangles to transform
857b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     */
867b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    status_t mapRawRectToCorrected(int32_t *rects, int rectCount);
877b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
887b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    /**
897b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Transform from corrected (warped) to distorted (original) coordinates.
907b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Coordinates are transformed in-place
917b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     *
927b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     *   coordPairs: A pointer to an array of consecutive (x,y) points
937b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     *   coordCount: Number of (x,y) pairs to transform
947b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     */
957b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    template<typename T>
967b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    status_t mapCorrectedToRaw(T* coordPairs, int coordCount) const;
977b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
987b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    /**
997b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Transform from corrected (warped) to distorted (original) coordinates.
1007b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     * Coordinates are transformed in-place
1017b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     *
1027b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     *   rects: A pointer to an array of consecutive (x,y, w, h) rectangles
1037b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     *   rectCount: Number of rectangles to transform
1047b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala     */
1057b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount) const;
1067b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1077b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    struct GridQuad {
1087b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala        // Source grid quad, or null
1097b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala        const GridQuad *src;
1107b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala        // x,y coordinates of corners, in
1117b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala        // clockwise order
1127b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala        std::array<float, 8> coords;
1137b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    };
1147b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1157b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // Find which grid quad encloses the point; returns null if none do
1167b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    static const GridQuad* findEnclosingQuad(
1177b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala            const int32_t pt[2], const std::vector<GridQuad>& grid);
1187b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1197b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // Calculate 'horizontal' interpolation coordinate for the point and the quad
1207b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // Assumes the point P is within the quad Q.
1217b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // Given quad with points P1-P4, and edges E12-E41, and considering the edge segments as
1227b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // functions of U: E12(u), where E12(0) = P1 and E12(1) = P2, then we want to find a u
1237b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // such that the edge E12(u) -> E43(u) contains point P.
1247b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // This can be determined by checking if the cross product of vector [E12(u)-E43(u)] and
1257b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // vector [E12(u)-P] is zero. Solving the equation
1267b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // [E12(u)-E43(u)] x [E12(u)-P] = 0 gives a quadratic equation in u; the solution in the range
1277b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // 0 to 1 is the one chosen.
1287b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // If calculateU is true, then an interpolation coordinate for edges E12 and E43 is found;
1297b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // if it is false, then an interpolation coordinate for edges E14 and E23 is found.
1307b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    static float calculateUorV(const int32_t pt[2], const GridQuad& quad, bool calculateU);
1317b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1327b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala  private:
1337b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    mutable std::mutex mMutex;
1347b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1357b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // Number of quads in each dimension of the mapping grids
1367b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    constexpr static size_t kGridSize = 15;
1377b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // Margin to expand the grid by to ensure it doesn't clip the domain
1387b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    constexpr static float kGridMargin = 0.05f;
1397b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // Fuzziness for float inequality tests
1407b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    constexpr static float kFloatFuzz = 1e-4;
1417b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1427b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // Metadata key lists to correct
1437b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1447b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // Both capture request and result
1457b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    static const std::array<uint32_t, 3> kMeteringRegionsToCorrect;
1467b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1477b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // Only capture request
1487b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    static const std::array<uint32_t, 1> kRequestRectsToCorrect;
1497b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1507b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // Only capture result
151cc073aac88a30dded339bc8809297fb78cc1b43aEino-Ville Talvala    static const std::array<uint32_t, 1> kResultRectsToCorrect;
1527b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1537b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // Only for capture results
154cc073aac88a30dded339bc8809297fb78cc1b43aEino-Ville Talvala    static const std::array<uint32_t, 2> kResultPointsToCorrect;
1557b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1567b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // Utility to create reverse mapping grids
1577b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    status_t buildGrids();
1587b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1597b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1607b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    bool mValidMapping;
1617b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    bool mValidGrids;
1627b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1637b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // intrisic parameters, in pixels
1647b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    float mFx, mFy, mCx, mCy, mS;
1657b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // pre-calculated inverses for speed
1667b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    float mInvFx, mInvFy;
1677b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // radial/tangential distortion parameters
1687b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    float mK[5];
1697b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1707b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    // pre-correction active array dimensions
1717b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    int mArrayWidth, mArrayHeight;
172c3462a1b892911f35ba2ff53e0f7aaec38fc2e4dYin-Chia Yeh    // active array dimensions
173c3462a1b892911f35ba2ff53e0f7aaec38fc2e4dYin-Chia Yeh    int mActiveWidth, mActiveHeight;
1747b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1757b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    std::vector<GridQuad> mCorrectedGrid;
1767b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala    std::vector<GridQuad> mDistortedGrid;
1777b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1787b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala}; // class DistortionMapper
1797b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1807b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala} // namespace camera3
1817b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1827b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala} // namespace android
1837b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala
1847b8a1fd27d12d1b3ea711b0edca6ff5b07f5beb1Eino-Ville Talvala#endif
185