Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 82d65fbd authored by Brian Julian's avatar Brian Julian
Browse files

Adds hidden AltitudeConverter.addMslAltitudeToLocation(Location) that does not...

Adds hidden AltitudeConverter.addMslAltitudeToLocation(Location) that does not load data from raw assets.

Relnote: N/A
Bug: 231327615
Test: atest FrameworksMockingServicesTests:AltitudeConverterTest
Change-Id: Idb699d03035c7ec56a07f3c648f49119b014363f
parent 0a061936
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -169,4 +169,28 @@ public final class AltitudeConverter {
        double[] geoidHeightsMeters = mGeoidHeightMap.readGeoidHeights(params, context, s2CellIds);
        addMslAltitude(params, s2CellIds, geoidHeightsMeters, location);
    }

    /**
     * Same as {@link #addMslAltitudeToLocation(Context, Location)} except that data will not be
     * loaded from raw assets. Returns true if a Mean Sea Level altitude is added to the
     * {@code location}; otherwise, returns false and leaves the {@code location} unchanged.
     *
     * @hide
     */
    public boolean addMslAltitudeToLocation(@NonNull Location location) {
        validate(location);
        MapParamsProto params = GeoidHeightMap.getParams();
        if (params == null) {
            return false;
        }

        long[] s2CellIds = findMapSquare(params, location);
        double[] geoidHeightsMeters = mGeoidHeightMap.readGeoidHeights(params, s2CellIds);
        if (geoidHeightsMeters == null) {
            return false;
        }

        addMslAltitude(params, s2CellIds, geoidHeightsMeters, location);
        return true;
    }
}
+46 −16
Original line number Diff line number Diff line
@@ -76,6 +76,17 @@ public final class GeoidHeightMap {
        }
    }

    /**
     * Same as {@link #getParams(Context)} except that null is returned if the singleton parameter
     * instance is not yet initialized.
     */
    @Nullable
    public static MapParamsProto getParams() {
        synchronized (sLock) {
            return sParams;
        }
    }

    private static long getCacheKey(@NonNull MapParamsProto params, long s2CellId) {
        return S2CellIdUtils.getParent(s2CellId, params.cacheTileS2Level);
    }
@@ -99,7 +110,8 @@ public final class GeoidHeightMap {
        S2TileProto[] tiles = new S2TileProto[len];
        for (int i = 0; i < len; i++) {
            if (s2CellIds[i] != 0) {
                tiles[i] = tileFunction.getTile(s2CellIds[i]);
                long cacheKey = getCacheKey(params, s2CellIds[i]);
                tiles[i] = tileFunction.getTile(cacheKey);
            }
            values[i] = Double.NaN;
        }
@@ -208,19 +220,26 @@ public final class GeoidHeightMap {
    }

    /**
     * Returns the geoid heights in meters associated with the map cells identified by
     * {@code s2CellIds}. Throws an {@link IOException} if a geoid height cannot be calculated for a
     * non-zero ID.
     * Throws an {@link IllegalArgumentException} if the {@code s2CellIds} has an invalid length or
     * ID.
     */
    @NonNull
    public double[] readGeoidHeights(@NonNull MapParamsProto params, @NonNull Context context,
            @NonNull long[] s2CellIds) throws IOException {
    private static void validate(@NonNull MapParamsProto params, @NonNull long[] s2CellIds) {
        Preconditions.checkArgument(s2CellIds.length == 4);
        for (long s2CellId : s2CellIds) {
            Preconditions.checkArgument(
                    s2CellId == 0 || S2CellIdUtils.getLevel(s2CellId) == params.mapS2Level);
        }
    }

    /**
     * Returns the geoid heights in meters associated with the map cells identified by
     * {@code s2CellIds}. Throws an {@link IOException} if a geoid height cannot be calculated for a
     * non-zero ID.
     */
    @NonNull
    public double[] readGeoidHeights(@NonNull MapParamsProto params, @NonNull Context context,
            @NonNull long[] s2CellIds) throws IOException {
        validate(params, s2CellIds);
        double[] heightsMeters = new double[s2CellIds.length];
        if (getGeoidHeights(params, mCacheTiles::get, s2CellIds, heightsMeters)) {
            return heightsMeters;
@@ -233,6 +252,21 @@ public final class GeoidHeightMap {
        throw new IOException("Unable to calculate geoid heights from raw assets.");
    }

    /**
     * Same as {@link #readGeoidHeights(MapParamsProto, Context, long[])} except that data will not
     * be loaded from raw assets. Returns the heights if present for all non-zero IDs; otherwise,
     * returns null.
     */
    @Nullable
    public double[] readGeoidHeights(@NonNull MapParamsProto params, @NonNull long[] s2CellIds) {
        validate(params, s2CellIds);
        double[] heightsMeters = new double[s2CellIds.length];
        if (getGeoidHeights(params, mCacheTiles::get, s2CellIds, heightsMeters)) {
            return heightsMeters;
        }
        return null;
    }

    /**
     * Adds to {@code heightsMeters} the geoid heights in meters associated with the map cells
     * identified by {@code s2CellIds}. Returns true if heights are present for all non-zero IDs;
@@ -297,11 +331,7 @@ public final class GeoidHeightMap {
            mergeFromDiskTile(params, tile, cacheKeys, diskTokens, i, loadedTiles);
        }

        return s2CellId -> {
            if (s2CellId == 0) {
                return null;
            }
            long cacheKey = getCacheKey(params, s2CellId);
        return cacheKey -> {
            for (int i = 0; i < cacheKeys.length; i++) {
                if (cacheKeys[i] == cacheKey) {
                    return loadedTiles[i];
@@ -321,8 +351,8 @@ public final class GeoidHeightMap {
        long[] s2CellIds = new long[numMapCellsPerCacheTile];
        double[] values = new double[numMapCellsPerCacheTile];

        // Each cache key identifies a different sub-tile of the disk tile.
        TileFunction diskTileFunction = s2CellId -> diskTile;
        // Each cache key identifies a different sub-tile of the same disk tile.
        TileFunction diskTileFunction = cacheKey -> diskTile;
        for (int i = diskTokenIndex; i < len; i++) {
            if (!Objects.equals(diskTokens[i], diskTokens[diskTokenIndex])
                    || loadedTiles[i] != null) {
@@ -358,10 +388,10 @@ public final class GeoidHeightMap {
        }
    }

    /** Defines a function-like object to retrieve tiles for map cells. */
    /** Defines a function-like object to retrieve tiles for cache keys. */
    private interface TileFunction {

        @Nullable
        S2TileProto getTile(long s2CellId);
        S2TileProto getTile(long cacheKey);
    }
}
+172 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.altitude;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertThrows;

import android.content.Context;
import android.location.Location;
import android.location.altitude.AltitudeConverter;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.IOException;

@MediumTest
@RunWith(AndroidJUnit4.class)
public class AltitudeConverterTest {

    private AltitudeConverter mAltitudeConverter;
    private Context mContext;

    @Before
    public void setUp() {
        mAltitudeConverter = new AltitudeConverter();
        mContext = ApplicationProvider.getApplicationContext();
    }

    @Test
    public void testAddMslAltitudeToLocation_expectedBehavior() throws IOException {
        // Interpolates between bffffc, 955554, and 000004.
        Location location = new Location("");
        location.setLatitude(-35.246789);
        location.setLongitude(-44.962683);
        location.setAltitude(-1);
        location.setVerticalAccuracyMeters(1);
        // Requires data to be loaded from raw assets.
        assertThat(mAltitudeConverter.addMslAltitudeToLocation(location)).isFalse();
        assertThat(location.hasMslAltitude()).isFalse();
        assertThat(location.hasMslAltitudeAccuracy()).isFalse();
        // Loads data from raw assets.
        mAltitudeConverter.addMslAltitudeToLocation(mContext, location);
        assertThat(location.getMslAltitudeMeters()).isWithin(2).of(5.1076);
        assertThat(location.getMslAltitudeAccuracyMeters()).isGreaterThan(1f);
        assertThat(location.getMslAltitudeAccuracyMeters()).isLessThan(1.1f);

        // Again interpolates between bffffc, 955554, and 000004.
        location = new Location("");
        location.setLatitude(-35.246789);
        location.setLongitude(-44.962683);
        location.setAltitude(-1);
        location.setVerticalAccuracyMeters(1);
        // Requires no data to be loaded from raw assets.
        assertThat(mAltitudeConverter.addMslAltitudeToLocation(location)).isTrue();
        assertThat(location.getMslAltitudeMeters()).isWithin(2).of(5.1076);
        assertThat(location.getMslAltitudeAccuracyMeters()).isGreaterThan(1f);
        assertThat(location.getMslAltitudeAccuracyMeters()).isLessThan(1.1f);
        // Results in same outcome.
        mAltitudeConverter.addMslAltitudeToLocation(mContext, location);
        assertThat(location.getMslAltitudeMeters()).isWithin(2).of(5.1076);
        assertThat(location.getMslAltitudeAccuracyMeters()).isGreaterThan(1f);
        assertThat(location.getMslAltitudeAccuracyMeters()).isLessThan(1.1f);

        // Interpolate between 955554, 000004, 00000c, and 95554c - no vertical accuracy.
        location = new Location("");
        location.setLatitude(-35.176383);
        location.setLongitude(-44.962683);
        location.setAltitude(-1);
        location.setVerticalAccuracyMeters(-1); // Invalid vertical accuracy
        // Requires no data to be loaded from raw assets.
        assertThat(mAltitudeConverter.addMslAltitudeToLocation(location)).isTrue();
        assertThat(location.getMslAltitudeMeters()).isWithin(2).of(5.1919);
        assertThat(location.hasMslAltitudeAccuracy()).isFalse();
        // Results in same outcome.
        mAltitudeConverter.addMslAltitudeToLocation(mContext, location);
        assertThat(location.getMslAltitudeMeters()).isWithin(2).of(5.1919);
        assertThat(location.hasMslAltitudeAccuracy()).isFalse();

        // Interpolates somewhere else more interesting, i.e., Hawaii.
        location = new Location("");
        location.setLatitude(19.545519);
        location.setLongitude(-155.998774);
        location.setAltitude(-1);
        location.setVerticalAccuracyMeters(1);
        // Requires data to be loaded from raw assets.
        assertThat(mAltitudeConverter.addMslAltitudeToLocation(location)).isFalse();
        assertThat(location.hasMslAltitude()).isFalse();
        assertThat(location.hasMslAltitudeAccuracy()).isFalse();
        // Loads data from raw assets.
        mAltitudeConverter.addMslAltitudeToLocation(mContext, location);
        assertThat(location.getMslAltitudeMeters()).isWithin(2).of(-19.2359);
        assertThat(location.getMslAltitudeAccuracyMeters()).isGreaterThan(1f);
        assertThat(location.getMslAltitudeAccuracyMeters()).isLessThan(1.1f);
    }

    @Test
    public void testAddMslAltitudeToLocation_invalidLatitudeThrows() {
        Location location = new Location("");
        location.setLongitude(-44.962683);
        location.setAltitude(-1);

        location.setLatitude(Double.NaN);
        assertThrows(IllegalArgumentException.class,
                () -> mAltitudeConverter.addMslAltitudeToLocation(location));

        location.setLatitude(91);
        assertThrows(IllegalArgumentException.class,
                () -> mAltitudeConverter.addMslAltitudeToLocation(location));

        location.setLatitude(-91);
        assertThrows(IllegalArgumentException.class,
                () -> mAltitudeConverter.addMslAltitudeToLocation(location));
    }

    @Test
    public void testAddMslAltitudeToLocation_invalidLongitudeThrows() {
        Location location = new Location("");
        location.setLatitude(-35.246789);
        location.setAltitude(-1);

        location.setLongitude(Double.NaN);
        assertThrows(IllegalArgumentException.class,
                () -> mAltitudeConverter.addMslAltitudeToLocation(location));

        location.setLongitude(181);
        assertThrows(IllegalArgumentException.class,
                () -> mAltitudeConverter.addMslAltitudeToLocation(location));

        location.setLongitude(-181);
        assertThrows(IllegalArgumentException.class,
                () -> mAltitudeConverter.addMslAltitudeToLocation(location));
    }

    @Test
    public void testAddMslAltitudeToLocation_invalidAltitudeThrows() {
        Location location = new Location("");
        location.setLatitude(-35.246789);
        location.setLongitude(-44.962683);

        assertThrows(IllegalArgumentException.class,
                () -> mAltitudeConverter.addMslAltitudeToLocation(location));

        location.setAltitude(Double.NaN);
        assertThrows(IllegalArgumentException.class,
                () -> mAltitudeConverter.addMslAltitudeToLocation(location));

        location.setAltitude(Double.POSITIVE_INFINITY);
        assertThrows(IllegalArgumentException.class,
                () -> mAltitudeConverter.addMslAltitudeToLocation(location));
    }
}