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

Commit 690fdd26 authored by Ivo Kay's avatar Ivo Kay Committed by Android (Google) Code Review
Browse files

Merge "Wait for GnssPowerStats to be updated before populating atom" into main

parents 678b3bc8 b5ab4bab
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1181,7 +1181,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
                        GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX));
            }
        } else if ("request_power_stats".equals(command)) {
            mGnssNative.requestPowerStats();
            mGnssNative.requestPowerStats(Runnable::run, powerStats -> {});
        } else {
            Log.w(TAG, "sendExtraCommand: unknown command " + command);
        }
+2 −2
Original line number Diff line number Diff line
@@ -314,9 +314,9 @@ public class GnssManagerService {
            ipw.decreaseIndent();
        }

        GnssPowerStats powerStats = mGnssNative.getPowerStats();
        GnssPowerStats powerStats = mGnssNative.getLastKnownPowerStats();
        if (powerStats != null) {
            ipw.println("Last Power Stats:");
            ipw.println("Last Known Power Stats:");
            ipw.increaseIndent();
            powerStats.dump(fd, ipw, args, mGnssNative.getCapabilities());
            ipw.decreaseIndent();
+65 −57
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.location.gnss;

import android.annotation.NonNull;
import android.app.StatsManager;
import android.content.Context;
import android.location.GnssSignalQuality;
@@ -60,7 +61,6 @@ public class GnssMetrics {
    private static final double L5_CARRIER_FREQ_RANGE_LOW_HZ = 1164 * 1e6;
    private static final double L5_CARRIER_FREQ_RANGE_HIGH_HZ = 1189 * 1e6;


    private long mLogStartInElapsedRealtimeMs;

    GnssPowerMetrics mGnssPowerMetrics;
@@ -608,8 +608,19 @@ public class GnssMetrics {
        }

        @Override
        public int onPullAtom(int atomTag, List<StatsEvent> data) {
            if (atomTag == FrameworkStatsLog.GNSS_STATS) {
        public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
            switch (atomTag) {
                case FrameworkStatsLog.GNSS_STATS:
                    return pullGnssStats(atomTag, data);
                case FrameworkStatsLog.GNSS_POWER_STATS:
                    return pullGnssPowerStats(atomTag, data);
                default:
                    throw new UnsupportedOperationException("Unknown tagId = " + atomTag);
            }
        }
    }

    private int pullGnssStats(int atomTag, List<StatsEvent> data) {
        data.add(FrameworkStatsLog.buildStatsEvent(atomTag,
                mLocationFailureReportsStatistics.getCount(),
                mLocationFailureReportsStatistics.getLongSum(),
@@ -623,34 +634,36 @@ public class GnssMetrics {
                mL5TopFourAverageCn0DbmHzReportsStatistics.getLongSum(), mSvStatusReports,
                mSvStatusReportsUsedInFix, mL5SvStatusReports,
                mL5SvStatusReportsUsedInFix));
            } else if (atomTag == FrameworkStatsLog.GNSS_POWER_STATS) {
                mGnssNative.requestPowerStats();
                GnssPowerStats gnssPowerStats = mGnssNative.getPowerStats();
                if (gnssPowerStats == null) {
        return StatsManager.PULL_SUCCESS;
    }

    private int pullGnssPowerStats(int atomTag, List<StatsEvent> data) {
        GnssPowerStats powerStats = mGnssNative.requestPowerStatsBlocking();
        if (powerStats == null) {
            return StatsManager.PULL_SKIP;
        } else {
            data.add(createPowerStatsEvent(atomTag, powerStats));
            return StatsManager.PULL_SUCCESS;
        }
    }

    private static StatsEvent createPowerStatsEvent(int atomTag,
            @NonNull GnssPowerStats powerStats) {
        double[] otherModesEnergyMilliJoule = new double[VENDOR_SPECIFIC_POWER_MODES_SIZE];
                double[] tempGnssPowerStatsOtherModes =
                        gnssPowerStats.getOtherModesEnergyMilliJoule();
                if (tempGnssPowerStatsOtherModes.length < VENDOR_SPECIFIC_POWER_MODES_SIZE) {
                    System.arraycopy(tempGnssPowerStatsOtherModes, 0,
                            otherModesEnergyMilliJoule, 0,
                            tempGnssPowerStatsOtherModes.length);
                } else {
        double[] tempGnssPowerStatsOtherModes = powerStats.getOtherModesEnergyMilliJoule();
        System.arraycopy(tempGnssPowerStatsOtherModes, 0,
                otherModesEnergyMilliJoule, 0,
                            VENDOR_SPECIFIC_POWER_MODES_SIZE);
                }
                data.add(FrameworkStatsLog.buildStatsEvent(atomTag,
                        (long) (gnssPowerStats.getElapsedRealtimeUncertaintyNanos()),
                        (long) (gnssPowerStats.getTotalEnergyMilliJoule() * CONVERT_MILLI_TO_MICRO),
                        (long) (gnssPowerStats.getSinglebandTrackingModeEnergyMilliJoule()
                Math.min(tempGnssPowerStatsOtherModes.length, VENDOR_SPECIFIC_POWER_MODES_SIZE));
        return FrameworkStatsLog.buildStatsEvent(atomTag,
                (long) (powerStats.getElapsedRealtimeUncertaintyNanos()),
                (long) (powerStats.getTotalEnergyMilliJoule() * CONVERT_MILLI_TO_MICRO),
                (long) (powerStats.getSinglebandTrackingModeEnergyMilliJoule()
                        * CONVERT_MILLI_TO_MICRO),
                        (long) (gnssPowerStats.getMultibandTrackingModeEnergyMilliJoule()
                (long) (powerStats.getMultibandTrackingModeEnergyMilliJoule()
                        * CONVERT_MILLI_TO_MICRO),
                        (long) (gnssPowerStats.getSinglebandAcquisitionModeEnergyMilliJoule()
                (long) (powerStats.getSinglebandAcquisitionModeEnergyMilliJoule()
                        * CONVERT_MILLI_TO_MICRO),
                        (long) (gnssPowerStats.getMultibandAcquisitionModeEnergyMilliJoule()
                (long) (powerStats.getMultibandAcquisitionModeEnergyMilliJoule()
                        * CONVERT_MILLI_TO_MICRO),
                (long) (otherModesEnergyMilliJoule[0] * CONVERT_MILLI_TO_MICRO),
                (long) (otherModesEnergyMilliJoule[1] * CONVERT_MILLI_TO_MICRO),
@@ -661,11 +674,6 @@ public class GnssMetrics {
                (long) (otherModesEnergyMilliJoule[6] * CONVERT_MILLI_TO_MICRO),
                (long) (otherModesEnergyMilliJoule[7] * CONVERT_MILLI_TO_MICRO),
                (long) (otherModesEnergyMilliJoule[8] * CONVERT_MILLI_TO_MICRO),
                        (long) (otherModesEnergyMilliJoule[9] * CONVERT_MILLI_TO_MICRO)));
            } else {
                throw new UnsupportedOperationException("Unknown tagId = " + atomTag);
            }
            return StatsManager.PULL_SUCCESS;
        }
                (long) (otherModesEnergyMilliJoule[9] * CONVERT_MILLI_TO_MICRO));
    }
}
 No newline at end of file
+80 −6
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@ package com.android.server.location.gnss.hal;

import static com.android.server.location.gnss.GnssManagerService.TAG;

import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.location.GnssAntennaInfo;
import android.location.GnssCapabilities;
@@ -29,6 +31,7 @@ import android.location.GnssSignalType;
import android.location.GnssStatus;
import android.location.Location;
import android.os.Binder;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;

@@ -46,9 +49,13 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Entry point for most GNSS HAL commands and callbacks.
@@ -140,6 +147,8 @@ public class GnssNative {
    public static final int AGPS_SETID_TYPE_IMSI = 1;
    public static final int AGPS_SETID_TYPE_MSISDN = 2;

    private static final int POWER_STATS_REQUEST_TIMEOUT_MILLIS = 100;

    @IntDef(prefix = "AGPS_SETID_TYPE_", value = {AGPS_SETID_TYPE_NONE, AGPS_SETID_TYPE_IMSI,
            AGPS_SETID_TYPE_MSISDN})
    @Retention(RetentionPolicy.SOURCE)
@@ -289,6 +298,15 @@ public class GnssNative {
                byte responseType, boolean inEmergencyMode, boolean isCachedLocation);
    }

    /** Callback for reporting {@link GnssPowerStats} */
    public interface PowerStatsCallback {
        /**
         * Called when power stats are reported.
         * @param powerStats non-null value when power stats are available, {@code null} otherwise.
         */
        void onReportPowerStats(@Nullable GnssPowerStats powerStats);
    }

    // set lower than the current ITAR limit of 600m/s to allow this to trigger even if GPS HAL
    // stops output right at 600m/s, depriving this of the information of a device that reaches
    // greater than 600m/s, and higher than the speed of sound to avoid impacting most use cases.
@@ -311,6 +329,8 @@ public class GnssNative {
    @GuardedBy("GnssNative.class")
    private static GnssNative sInstance;

    private final Handler mHandler;

    /**
     * Sets GnssHal instance to use for testing.
     */
@@ -367,6 +387,14 @@ public class GnssNative {
    private NavigationMessageCallbacks[] mNavigationMessageCallbacks =
            new NavigationMessageCallbacks[0];

    private @Nullable GnssPowerStats mLastKnownPowerStats = null;
    private final Object mPowerStatsLock = new Object();
    private final Runnable mPowerStatsTimeoutCallback = () -> {
        Log.d(TAG, "Request for power stats timed out");
        reportGnssPowerStats(null);
    };
    private final List<PowerStatsCallback> mPendingPowerStatsCallbacks = new ArrayList<>();

    // these callbacks may only have a single implementation
    private GeofenceCallbacks mGeofenceCallbacks;
    private TimeCallbacks mTimeCallbacks;
@@ -381,7 +409,6 @@ public class GnssNative {

    private GnssCapabilities mCapabilities = new GnssCapabilities.Builder().build();
    private @GnssCapabilities.TopHalCapabilityFlags int mTopFlags;
    private @Nullable GnssPowerStats mPowerStats = null;
    private int mHardwareYear = 0;
    private @Nullable String mHardwareModelName = null;
    private long mStartRealtimeMs = 0;
@@ -391,6 +418,7 @@ public class GnssNative {
        mGnssHal = Objects.requireNonNull(gnssHal);
        mEmergencyHelper = injector.getEmergencyHelper();
        mConfiguration = configuration;
        mHandler = FgThread.getHandler();
    }

    public void addBaseCallbacks(BaseCallbacks callbacks) {
@@ -532,8 +560,8 @@ public class GnssNative {
    /**
     * Returns the latest power stats from the GNSS HAL.
     */
    public @Nullable GnssPowerStats getPowerStats() {
        return mPowerStats;
    public @Nullable GnssPowerStats getLastKnownPowerStats() {
        return mLastKnownPowerStats;
    }

    /**
@@ -931,10 +959,49 @@ public class GnssNative {

    /**
     * Request an eventual update of GNSS power statistics.
     *
     * @param executor Executor that will run {@code callback}
     * @param callback Called with non-null power stats if they were obtained in time, called with
     *                 {@code null} if stats could not be obtained in time.
     */
    public void requestPowerStats() {
    public void requestPowerStats(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull PowerStatsCallback callback) {
        Preconditions.checkState(mRegistered);
        synchronized (mPowerStatsLock) {
            mPendingPowerStatsCallbacks.add(powerStats -> {
                Binder.withCleanCallingIdentity(
                        () -> executor.execute(() -> callback.onReportPowerStats(powerStats)));
            });
            if (mPendingPowerStatsCallbacks.size() == 1) {
                mGnssHal.requestPowerStats();
                mHandler.postDelayed(mPowerStatsTimeoutCallback,
                        POWER_STATS_REQUEST_TIMEOUT_MILLIS);
            }
        }
    }

    /**
     * Request GNSS power statistics and blocks for a short time waiting for the result.
     *
     * @return non-null power stats, or {@code null} if stats could not be obtained in time.
     */
    public @Nullable GnssPowerStats requestPowerStatsBlocking() {
        AtomicReference<GnssPowerStats> statsWrapper = new AtomicReference<>();
        CountDownLatch latch = new CountDownLatch(1);
        requestPowerStats(Runnable::run, powerStats -> {
            statsWrapper.set(powerStats);
            latch.countDown();
        });

        try {
            latch.await(POWER_STATS_REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            Log.d(TAG, "Interrupted while waiting for power stats");
            Thread.currentThread().interrupt();
        }

        return statsWrapper.get();
    }

    /**
@@ -1167,7 +1234,14 @@ public class GnssNative {

    @NativeEntryPoint
    void reportGnssPowerStats(GnssPowerStats powerStats) {
        mPowerStats = powerStats;
        synchronized (mPowerStatsLock) {
            mHandler.removeCallbacks(mPowerStatsTimeoutCallback);
            if (powerStats != null) {
                mLastKnownPowerStats = powerStats;
            }
            mPendingPowerStatsCallbacks.forEach(cb -> cb.onReportPowerStats(powerStats));
            mPendingPowerStatsCallbacks.clear();
        }
    }

    @NativeEntryPoint
+129 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.gnss.hal;

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

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import android.content.Context;
import android.platform.test.annotations.Presubmit;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.server.location.gnss.GnssConfiguration;
import com.android.server.location.gnss.GnssPowerStats;
import com.android.server.location.injector.Injector;
import com.android.server.location.injector.TestInjector;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.Objects;
import java.util.concurrent.Executor;

@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class GnssNativeTest {

    private @Mock Context mContext;
    private @Mock GnssConfiguration mMockConfiguration;
    private FakeGnssHal mFakeGnssHal;
    private GnssNative mGnssNative;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        mFakeGnssHal = new FakeGnssHal();
        GnssNative.setGnssHalForTest(mFakeGnssHal);
        Injector injector = new TestInjector(mContext);
        mGnssNative = spy(Objects.requireNonNull(GnssNative.create(injector, mMockConfiguration)));
        mGnssNative.register();
    }

    @Test
    public void testRequestPowerStats_onNull_executesCallbackWithNull() {
        mFakeGnssHal.setPowerStats(null);
        Executor executor = spy(Runnable::run);
        GnssNative.PowerStatsCallback callback = spy(stats -> {});

        mGnssNative.requestPowerStats(executor, callback);

        verify(executor).execute(any());
        verify(callback).onReportPowerStats(null);
    }

    @Test
    public void testRequestPowerStats_onPowerStats_executesCallbackWithStats() {
        GnssPowerStats powerStats = new GnssPowerStats(1, 2, 3, 4, 5, 6, 7, 8, new double[]{9, 10});
        mFakeGnssHal.setPowerStats(powerStats);
        Executor executor = spy(Runnable::run);
        GnssNative.PowerStatsCallback callback = spy(stats -> {});

        mGnssNative.requestPowerStats(executor, callback);

        verify(executor).execute(any());
        verify(callback).onReportPowerStats(powerStats);
    }

    @Test
    public void testRequestPowerStatsBlocking_onNull_returnsNull() {
        mFakeGnssHal.setPowerStats(null);

        assertThat(mGnssNative.requestPowerStatsBlocking()).isNull();
    }

    @Test
    public void testRequestPowerStatsBlocking_onPowerStats_returnsStats() {
        GnssPowerStats powerStats = new GnssPowerStats(1, 2, 3, 4, 5, 6, 7, 8, new double[]{9, 10});
        mFakeGnssHal.setPowerStats(powerStats);

        assertThat(mGnssNative.requestPowerStatsBlocking()).isEqualTo(powerStats);
    }

    @Test
    public void testGetLastKnownPowerStats_onNull_preservesLastKnownPowerStats() {
        GnssPowerStats powerStats = new GnssPowerStats(1, 2, 3, 4, 5, 6, 7, 8, new double[]{9, 10});

        mGnssNative.reportGnssPowerStats(powerStats);
        assertThat(mGnssNative.getLastKnownPowerStats()).isEqualTo(powerStats);

        mGnssNative.reportGnssPowerStats(null);
        assertThat(mGnssNative.getLastKnownPowerStats()).isEqualTo(powerStats);
    }

    @Test
    public void testGetLastKnownPowerStats_onPowerStats_updatesLastKnownPowerStats() {
        GnssPowerStats powerStats1 = new GnssPowerStats(1, 2, 3, 4, 5, 6, 7, 8, new double[]{9, 0});
        GnssPowerStats powerStats2 = new GnssPowerStats(2, 3, 4, 5, 6, 7, 8, 9, new double[]{0, 9});

        mGnssNative.reportGnssPowerStats(powerStats1);
        assertThat(mGnssNative.getLastKnownPowerStats()).isEqualTo(powerStats1);

        mGnssNative.reportGnssPowerStats(powerStats2);
        assertThat(mGnssNative.getLastKnownPowerStats()).isEqualTo(powerStats2);
    }

}