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

Commit d9c8a85f authored by destradaa's avatar destradaa Committed by Android (Google) Code Review
Browse files

Merge "Reduce memory usage of GpsStatus objects."

parents a3a8b313 6bde4683
Loading
Loading
Loading
Loading
+11 −7
Original line number Diff line number Diff line
@@ -40,6 +40,9 @@ public final class GpsSatellite {
     * cached GpsStatus instance to the client's copy.
     */
    void setStatus(GpsSatellite satellite) {
        if (satellite == null) {
            mValid = false;
        } else {
            mValid = satellite.mValid;
            mHasEphemeris = satellite.mHasEphemeris;
            mHasAlmanac = satellite.mHasAlmanac;
@@ -48,6 +51,7 @@ public final class GpsSatellite {
            mElevation = satellite.mElevation;
            mAzimuth = satellite.mAzimuth;
        }
    }

    /**
     * Returns the PRN (pseudo-random number) for the satellite.
+68 −29
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.location;

import android.util.SparseArray;

import java.util.Iterator;
import java.util.NoSuchElementException;

@@ -29,20 +31,24 @@ public final class GpsStatus {

    /* These package private values are modified by the LocationManager class */
    private int mTimeToFirstFix;
    private GpsSatellite mSatellites[] = new GpsSatellite[NUM_SATELLITES];
    private final SparseArray<GpsSatellite> mSatellites = new SparseArray<>();

    private final class SatelliteIterator implements Iterator<GpsSatellite> {

        private GpsSatellite[] mSatellites;
        int mIndex = 0;
        private final SparseArray<GpsSatellite> mSatellites;
        private final int mSatellitesCount;

        private int mIndex = 0;

        SatelliteIterator(GpsSatellite[] satellites) {
        SatelliteIterator(SparseArray<GpsSatellite> satellites) {
            mSatellites = satellites;
            mSatellitesCount = satellites.size();
        }

        public boolean hasNext() {
            for (int i = mIndex; i < mSatellites.length; i++) {
                if (mSatellites[i].mValid) {
            for (; mIndex < mSatellitesCount; ++mIndex) {
                GpsSatellite satellite = mSatellites.valueAt(mIndex);
                if (satellite.mValid) {
                    return true;
                }
            }
@@ -50,8 +56,9 @@ public final class GpsStatus {
        }

        public GpsSatellite next() {
            while (mIndex < mSatellites.length) {
                GpsSatellite satellite = mSatellites[mIndex++];
            while (mIndex < mSatellitesCount) {
                GpsSatellite satellite = mSatellites.valueAt(mIndex);
                ++mIndex;
                if (satellite.mValid) {
                    return satellite;
                }
@@ -127,11 +134,8 @@ public final class GpsStatus {
        void onNmeaReceived(long timestamp, String nmea);
    }

    GpsStatus() {
        for (int i = 0; i < mSatellites.length; i++) {
            mSatellites[i] = new GpsSatellite(i + 1);
        }
    }
    // For API-compat a public ctor() is not available
    GpsStatus() {}

    /**
     * Used internally within {@link LocationManager} to copy GPS status
@@ -141,18 +145,17 @@ public final class GpsStatus {
    synchronized void setStatus(int svCount, int[] prns, float[] snrs,
            float[] elevations, float[] azimuths, int ephemerisMask,
            int almanacMask, int usedInFixMask) {
        int i;

        for (i = 0; i < mSatellites.length; i++) {
            mSatellites[i].mValid = false;
        clearSatellites();
        for (int i = 0; i < svCount; i++) {
            int prn = prns[i];
            int prnShift = (1 << (prn - 1));
            if (prn > 0 && prn <= NUM_SATELLITES) {
                GpsSatellite satellite = mSatellites.get(prn);
                if (satellite == null) {
                    satellite = new GpsSatellite(prn);
                    mSatellites.put(prn, satellite);
                }

        for (i = 0; i < svCount; i++) {
            int prn = prns[i] - 1;
            int prnShift = (1 << prn);
            if (prn >= 0 && prn < mSatellites.length) {
                GpsSatellite satellite = mSatellites[prn];
    
                satellite.mValid = true;
                satellite.mSnr = snrs[i];
                satellite.mElevation = elevations[i];
@@ -172,9 +175,37 @@ public final class GpsStatus {
     */
    void setStatus(GpsStatus status) {
        mTimeToFirstFix = status.getTimeToFirstFix();
        clearSatellites();

        SparseArray<GpsSatellite> otherSatellites = status.mSatellites;
        int otherSatellitesCount = otherSatellites.size();
        int satelliteIndex = 0;
        // merge both sparse arrays, note that we have already invalidated the elements in the
        // receiver array
        for (int i = 0; i < otherSatellitesCount; ++i) {
            GpsSatellite otherSatellite = otherSatellites.valueAt(i);
            int otherSatellitePrn = otherSatellite.getPrn();

        for (int i = 0; i < mSatellites.length; i++) {
            mSatellites[i].setStatus(status.mSatellites[i]);
            int satellitesCount = mSatellites.size();
            while (satelliteIndex < satellitesCount
                    && mSatellites.valueAt(satelliteIndex).getPrn() < otherSatellitePrn) {
                ++satelliteIndex;
            }

            if (satelliteIndex < mSatellites.size()) {
                GpsSatellite satellite = mSatellites.valueAt(satelliteIndex);
                if (satellite.getPrn() == otherSatellitePrn) {
                    satellite.setStatus(otherSatellite);
                } else {
                    satellite = new GpsSatellite(otherSatellitePrn);
                    satellite.setStatus(otherSatellite);
                    mSatellites.put(otherSatellitePrn, satellite);
                }
            } else {
                GpsSatellite satellite = new GpsSatellite(otherSatellitePrn);
                satellite.setStatus(otherSatellite);
                mSatellites.append(otherSatellitePrn, satellite);
            }
        }
    }

@@ -211,4 +242,12 @@ public final class GpsStatus {
    public int getMaxSatellites() {
        return NUM_SATELLITES;
    }

    private void clearSatellites() {
        int satellitesCount = mSatellites.size();
        for (int i = 0; i < satellitesCount; i++) {
            GpsSatellite satellite = mSatellites.valueAt(i);
            satellite.mValid = false;
        }
    }
}
+356 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.location;

import junit.framework.TestCase;

import android.test.suitebuilder.annotation.SmallTest;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;

/**
 * Unit tests for {@link GpsStatus}.
 */
@SmallTest
public class GpsStatusTest extends TestCase {

    private static final int MAX_VALUE = 250;

    private final Random mRandom = new Random();

    private GpsStatus mStatus;
    private int mCount;
    private int[] mPrns;
    private float[] mSnrs;
    private float[] mElevations;
    private float[] mAzimuth;
    private int mEphemerisMask;
    private int mAlmanacMask;
    private int mUsedInFixMask;

    public void setUp() throws Exception {
        super.setUp();
        mStatus = createGpsStatus();
        generateSatellitesData(generateInt());
    }

    public void testEmptyGpsStatus() throws Exception {
        verifyIsEmpty(mStatus);
    }

    public void testGpsStatusIterator() throws Exception {
        generateSatellitesData(2);
        setSatellites(mStatus);
        Iterator<GpsSatellite> iterator = mStatus.getSatellites().iterator();
        assertTrue("hasNext(1)", iterator.hasNext());
        assertTrue("hasNext(1) does not overflow", iterator.hasNext());
        GpsSatellite satellite1 = iterator.next();
        assertNotNull("satellite", satellite1);
        assertTrue("hasNext(2)", iterator.hasNext());
        assertTrue("hasNext(2) does not overflow", iterator.hasNext());
        GpsSatellite satellite2 = iterator.next();
        assertNotNull("satellite", satellite2);
        assertFalse("hasNext() no elements", iterator.hasNext());
    }

    public void testTtff() throws Exception {
        int testTtff = generateInt();
        set(mStatus, testTtff);
        verifyTtff(mStatus, testTtff);
    }

    public void testCopyTtff() throws Exception {
        int testTtff = generateInt();
        verifyTtff(mStatus, 0);

        GpsStatus otherStatus = createGpsStatus();
        set(otherStatus, testTtff);
        verifyTtff(otherStatus, testTtff);

        set(mStatus, otherStatus);
        verifyTtff(mStatus, testTtff);
    }

    public void testSetSatellites() throws Exception {
        setSatellites(mStatus);
        verifySatellites(mStatus);
    }

    public void testCopySatellites() throws Exception {
        verifyIsEmpty(mStatus);

        GpsStatus otherStatus = createGpsStatus();
        setSatellites(otherStatus);
        verifySatellites(otherStatus);

        set(mStatus, otherStatus);
        verifySatellites(mStatus);
    }

    public void testOverrideSatellites() throws Exception {
        setSatellites(mStatus);
        verifySatellites(mStatus);

        GpsStatus otherStatus = createGpsStatus();
        generateSatellitesData(mCount, true /* reusePrns */);
        setSatellites(otherStatus);
        verifySatellites(otherStatus);

        set(mStatus, otherStatus);
        verifySatellites(mStatus);
    }

    public void testAddSatellites() throws Exception {
        int count = 10;
        generateSatellitesData(count);
        setSatellites(mStatus);
        verifySatellites(mStatus);

        GpsStatus otherStatus = createGpsStatus();
        generateSatellitesData(count);
        setSatellites(otherStatus);
        verifySatellites(otherStatus);

        set(mStatus, otherStatus);
        verifySatellites(mStatus);
    }

    public void testAddMoreSatellites() throws Exception {
        int count = 25;
        generateSatellitesData(count);
        setSatellites(mStatus);
        verifySatellites(mStatus);

        GpsStatus otherStatus = createGpsStatus();
        generateSatellitesData(count * 2);
        setSatellites(otherStatus);
        verifySatellites(otherStatus);

        set(mStatus, otherStatus);
        verifySatellites(mStatus);
    }

    public void testAddLessSatellites() throws Exception {
        int count = 25;
        generateSatellitesData(count * 2);
        setSatellites(mStatus);
        verifySatellites(mStatus);

        GpsStatus otherStatus = createGpsStatus();
        generateSatellitesData(count);
        setSatellites(otherStatus);
        verifySatellites(otherStatus);

        set(mStatus, otherStatus);
        verifySatellites(mStatus);
    }

    private static void verifyIsEmpty(GpsStatus status) {
        verifySatelliteCount(status, 0);
        verifyTtff(status, 0);
    }

    private static void verifySatelliteCount(GpsStatus status, int expectedCount) {
        int satellites = 0;
        for (GpsSatellite s : status.getSatellites()) {
            ++satellites;
        }
        assertEquals("GpsStatus::SatelliteCount", expectedCount, satellites);
    }

    private void verifySatellites(GpsStatus status) {
        verifySatelliteCount(status, mCount);
        verifySatellites(status, mCount, mPrns, mSnrs, mElevations, mAzimuth, mEphemerisMask,
                mAlmanacMask, mUsedInFixMask);
    }

    private static void verifySatellites(
            GpsStatus status,
            int count,
            int[] prns,
            float[] snrs,
            float[] elevations,
            float[] azimuth,
            int ephemerisMask,
            int almanacMask,
            int usedInFixMask) {
        for (int i = 0; i < count; ++i) {
            int prn = prns[i];
            GpsSatellite satellite = getSatellite(status, prn);
            assertNotNull(getSatelliteAssertInfo(i, prn, "non-null"), satellite);
            assertEquals(getSatelliteAssertInfo(i, prn, "Snr"), snrs[i], satellite.getSnr());
            assertEquals(
                    getSatelliteAssertInfo(i, prn, "Elevation"),
                    elevations[i],
                    satellite.getElevation());
            assertEquals(
                    getSatelliteAssertInfo(i, prn, "Azimuth"),
                    azimuth[i],
                    satellite.getAzimuth());
            int prnShift = 1 << (prn - 1);
            assertEquals(
                    getSatelliteAssertInfo(i, prn, "ephemeris"),
                    (ephemerisMask & prnShift) != 0,
                    satellite.hasEphemeris());
            assertEquals(
                    getSatelliteAssertInfo(i, prn, "almanac"),
                    (almanacMask & prnShift) != 0,
                    satellite.hasAlmanac());
            assertEquals(
                    getSatelliteAssertInfo(i, prn, "usedInFix"),
                    (usedInFixMask & prnShift) != 0,
                    satellite.usedInFix());
        }
    }

    private static void verifyTtff(GpsStatus status, int expectedTtff) {
        assertEquals("GpsStatus::TTFF", expectedTtff, status.getTimeToFirstFix());
    }

    private static GpsStatus createGpsStatus() throws Exception {
        Constructor<GpsStatus>  ctor = GpsStatus.class.getDeclaredConstructor();
        ctor.setAccessible(true);
        return ctor.newInstance();
    }

    private static void set(GpsStatus status, int ttff) throws Exception {
        Class<?> statusClass = status.getClass();
        Method setTtff = statusClass.getDeclaredMethod("setTimeToFirstFix", Integer.TYPE);
        setTtff.setAccessible(true);
        setTtff.invoke(status, ttff);
    }

    private static void set(GpsStatus status, GpsStatus statusToSet) throws Exception {
        Class<?> statusClass = status.getClass();
        Method setStatus = statusClass.getDeclaredMethod("setStatus", statusClass);
        setStatus.setAccessible(true);
        setStatus.invoke(status, statusToSet);
    }

    private void setSatellites(GpsStatus status) throws Exception {
        set(status, mCount, mPrns, mSnrs, mElevations, mAzimuth, mEphemerisMask, mAlmanacMask,
                mUsedInFixMask);
    }

    private static void set(
            GpsStatus status,
            int count,
            int[] prns,
            float[] snrs,
            float[] elevations,
            float[] azimuth,
            int ephemerisMask,
            int almanacMask,
            int usedInFixMask) throws Exception {
        Class<?> statusClass = status.getClass();
        Class<?> intClass = Integer.TYPE;
        Class<?> floatArrayClass = Class.forName("[F");
        Method setStatus = statusClass.getDeclaredMethod(
                "setStatus",
                intClass,
                Class.forName("[I"),
                floatArrayClass,
                floatArrayClass,
                floatArrayClass,
                intClass,
                intClass,
                intClass);
        setStatus.setAccessible(true);
        setStatus.invoke(
                status,
                count,
                prns,
                snrs,
                elevations,
                azimuth,
                ephemerisMask,
                almanacMask,
                usedInFixMask);
    }

    private int generateInt() {
        return mRandom.nextInt(MAX_VALUE) + 1;
    }

    private int[] generateIntArray(int count) {
        Set<Integer> generatedPrns = new HashSet<>();
        int[] array = new int[count];
        for(int i = 0; i < count; ++i) {
            int generated;
            do {
                generated = generateInt();
            } while (generatedPrns.contains(generated));
            array[i] = generated;
            generatedPrns.add(generated);
        }
        return array;
    }

    private float[] generateFloatArray(int count) {
        float[] array = new float[count];
        for(int i = 0; i < count; ++i) {
            array[i] = generateInt();
        }
        return array;
    }

    private int generateMask(int[] prns) {
        int mask = 0;
        int prnsLength = prns.length;
        for (int i = 0; i < prnsLength; ++i) {
            if (mRandom.nextBoolean()) {
                mask |= 1 << (prns[i] - 1);
            }
        }
        return mask;
    }

    private void generateSatellitesData(int count) {
        generateSatellitesData(count, false /* reusePrns */);
    }

    private void generateSatellitesData(int count, boolean reusePrns) {
        mCount = count;
        if (!reusePrns) {
            mPrns = generateIntArray(count);
        }
        mSnrs = generateFloatArray(count);
        mElevations = generateFloatArray(count);
        mAzimuth = generateFloatArray(count);
        mEphemerisMask = generateMask(mPrns);
        mAlmanacMask = generateMask(mPrns);
        mUsedInFixMask = generateMask(mPrns);
    }

    private static GpsSatellite getSatellite(GpsStatus status, int prn) {
        for (GpsSatellite satellite : status.getSatellites()) {
            if (satellite.getPrn() == prn) {
                return satellite;
            }
        }
        return null;
    }

    private static String getSatelliteAssertInfo(int index, int prn, String param) {
        return String.format("Satellite::%s [i=%d, prn=%d]", param, index, prn);
    }
}