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

Commit 3600c831 authored by Brian Julian's avatar Brian Julian Committed by Android (Google) Code Review
Browse files

Merge "Applies AltitudeConverter to LocationManagerProvider such that Mean Sea...

Merge "Applies AltitudeConverter to LocationManagerProvider such that Mean Sea Level altitude is automatically added to applicable locations being processed."
parents 98cc6695 a09411a7
Loading
Loading
Loading
Loading
+75 −24
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import android.location.LocationManagerInternal;
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
import android.location.altitude.AltitudeConverter;
import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
@@ -81,6 +82,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.DeviceConfig;
import android.stats.location.LocationStatsEnums;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -94,6 +96,7 @@ import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.location.LocationPermissions;
import com.android.server.location.LocationPermissions.PermissionLevel;
@@ -122,6 +125,7 @@ import com.android.server.location.settings.LocationSettings;
import com.android.server.location.settings.LocationUserSettings;

import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -1441,6 +1445,10 @@ public class LocationProviderManager extends
    @GuardedBy("mMultiplexerLock")
    @Nullable private StateChangedListener mStateChangedListener;

    /** Enables missing MSL altitudes to be added on behalf of the provider. */
    private final AltitudeConverter mAltitudeConverter = new AltitudeConverter();
    private volatile boolean mIsAltitudeConverterIdle = true;

    public LocationProviderManager(Context context, Injector injector,
            String name, @Nullable PassiveLocationProviderManager passiveManager) {
        this(context, injector, name, passiveManager, Collections.emptyList());
@@ -2512,33 +2520,18 @@ public class LocationProviderManager extends
    @GuardedBy("mMultiplexerLock")
    @Override
    public void onReportLocation(LocationResult locationResult) {
        LocationResult filtered;
        LocationResult processed;
        if (mPassiveManager != null) {
            filtered = locationResult.filter(location -> {
                if (!location.isMock()) {
                    if (location.getLatitude() == 0 && location.getLongitude() == 0) {
                        Log.e(TAG, "blocking 0,0 location from " + mName + " provider");
                        return false;
                    }
                }

                if (!location.isComplete()) {
                    Log.e(TAG, "blocking incomplete location from " + mName + " provider");
                    return false;
                }

                return true;
            });

            if (filtered == null) {
            processed = processReportedLocation(locationResult);
            if (processed == null) {
                return;
            }

            // don't log location received for passive provider because it's spammy
            EVENT_LOG.logProviderReceivedLocations(mName, filtered.size());
            EVENT_LOG.logProviderReceivedLocations(mName, processed.size());
        } else {
            // passive provider should get already filtered results as input
            filtered = locationResult;
            // passive provider should get already processed results as input
            processed = locationResult;
        }

        // check for non-monotonic locations if we're not the passive manager. the passive manager
@@ -2554,17 +2547,75 @@ public class LocationProviderManager extends
        }

        // update last location
        setLastLocation(filtered.getLastLocation(), UserHandle.USER_ALL);
        setLastLocation(processed.getLastLocation(), UserHandle.USER_ALL);

        // attempt listener delivery
        deliverToListeners(registration -> {
            return registration.acceptLocationChange(filtered);
            return registration.acceptLocationChange(processed);
        });

        // notify passive provider
        if (mPassiveManager != null) {
            mPassiveManager.updateLocation(filtered);
            mPassiveManager.updateLocation(processed);
        }
    }

    @GuardedBy("mMultiplexerLock")
    @Nullable
    private LocationResult processReportedLocation(LocationResult locationResult) {
        LocationResult processed = locationResult.filter(location -> {
            if (!location.isMock()) {
                if (location.getLatitude() == 0 && location.getLongitude() == 0) {
                    Log.e(TAG, "blocking 0,0 location from " + mName + " provider");
                    return false;
                }
            }

            if (!location.isComplete()) {
                Log.e(TAG, "blocking incomplete location from " + mName + " provider");
                return false;
            }

            return true;
        });
        if (processed == null) {
            return null;
        }

        // Attempt to add a missing MSL altitude on behalf of the provider.
        if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_LOCATION,
                "enable_location_provider_manager_msl", false)) {
            return processed.map(location -> {
                if (!location.hasMslAltitude() && location.hasAltitude()) {
                    try {
                        Location locationCopy = new Location(location);
                        if (mAltitudeConverter.addMslAltitudeToLocation(locationCopy)) {
                            return locationCopy;
                        }
                        // Only queue up one IO thread runnable.
                        if (mIsAltitudeConverterIdle) {
                            mIsAltitudeConverterIdle = false;
                            IoThread.getExecutor().execute(() -> {
                                try {
                                    // Results added to the location copy are essentially discarded.
                                    // We only rely on the side effect of loading altitude assets
                                    // into the converter's memory cache.
                                    mAltitudeConverter.addMslAltitudeToLocation(mContext,
                                            locationCopy);
                                } catch (IOException e) {
                                    Log.e(TAG, "not loading MSL altitude assets: " + e);
                                }
                                mIsAltitudeConverterIdle = true;
                            });
                        }
                    } catch (IllegalArgumentException e) {
                        Log.e(TAG, "not adding MSL altitude to location: " + e);
                    }
                }
                return location;
            });
        }
        return processed;
    }

    @GuardedBy("mMultiplexerLock")
+146 −0
Original line number Diff line number Diff line
@@ -80,8 +80,12 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.WorkSource;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.Log;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

@@ -174,6 +178,8 @@ public class LocationProviderManagerTest {
        doReturn(mResources).when(mContext).getResources();
        doReturn(mPackageManager).when(mContext).getPackageManager();
        doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
        doReturn(ApplicationProvider.getApplicationContext()).when(
                mContext).getApplicationContext();
        doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
        doReturn(PackageManager.PERMISSION_DENIED)
                .when(mContext)
@@ -210,6 +216,8 @@ public class LocationProviderManagerTest {

    @After
    public void tearDown() throws Exception {
        DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
                DeviceConfig.NAMESPACE_LOCATION);
        LocalServices.removeServiceForTest(LocationManagerInternal.class);

        // some test failures may leave the fg thread stuck, interrupt until we get out of it
@@ -1339,6 +1347,144 @@ public class LocationProviderManagerTest {
        assertThat(mManager.isVisibleToCaller()).isFalse();
    }

    @MediumTest
    @Test
    public void testEnableMsl_expectedBehavior() throws Exception {
        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LOCATION,
                "enable_location_provider_manager_msl", Boolean.toString(true), false);

        // Create a random location and set provider location to cache necessary MSL assets.
        Location loc = createLocation(NAME, mRandom);
        loc.setAltitude(mRandom.nextDouble());
        loc.setVerticalAccuracyMeters(mRandom.nextFloat());
        mProvider.setProviderLocation(LocationResult.wrap(loc));
        Thread.sleep(1000);

        // Register listener and reset provider location to capture.
        ILocationListener listener = createMockLocationListener();
        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
        mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
        mProvider.setProviderLocation(LocationResult.wrap(loc));
        ArgumentCaptor<List<Location>> captor = ArgumentCaptor.forClass(List.class);
        verify(listener).onLocationChanged(captor.capture(), nullable(IRemoteCallback.class));

        // Assert that MSL fields are populated.
        Location actual = captor.getValue().get(0);
        assertThat(actual.hasMslAltitude()).isTrue();
        assertThat(actual.hasMslAltitudeAccuracy()).isTrue();
    }

    @MediumTest
    @Test
    public void testEnableMsl_noVerticalAccuracy() throws Exception {
        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LOCATION,
                "enable_location_provider_manager_msl", Boolean.toString(true), false);

        // Create a random location and set provider location to cache necessary MSL assets.
        Location loc = createLocation(NAME, mRandom);
        loc.setAltitude(mRandom.nextDouble());
        loc.setVerticalAccuracyMeters(mRandom.nextFloat());
        mProvider.setProviderLocation(LocationResult.wrap(loc));
        Thread.sleep(1000);

        // Register listener and reset provider location with no vertical accuracy to capture.
        ILocationListener listener = createMockLocationListener();
        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
        mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
        loc.removeVerticalAccuracy();
        mProvider.setProviderLocation(LocationResult.wrap(loc));
        ArgumentCaptor<List<Location>> captor = ArgumentCaptor.forClass(List.class);
        verify(listener).onLocationChanged(captor.capture(), nullable(IRemoteCallback.class));

        // Assert that only the MSL accuracy field is populated.
        Location actual = captor.getValue().get(0);
        assertThat(actual.hasMslAltitude()).isTrue();
        assertThat(actual.hasMslAltitudeAccuracy()).isFalse();
    }

    @MediumTest
    @Test
    public void testEnableMsl_noAltitude() throws Exception {
        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LOCATION,
                "enable_location_provider_manager_msl", Boolean.toString(true), false);

        // Create a random location and set provider location to cache necessary MSL assets.
        Location loc = createLocation(NAME, mRandom);
        loc.setAltitude(mRandom.nextDouble());
        loc.setVerticalAccuracyMeters(mRandom.nextFloat());
        mProvider.setProviderLocation(LocationResult.wrap(loc));
        Thread.sleep(1000);

        // Register listener and reset provider location with no altitude to capture.
        ILocationListener listener = createMockLocationListener();
        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
        mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
        loc.removeAltitude();
        mProvider.setProviderLocation(LocationResult.wrap(loc));
        ArgumentCaptor<List<Location>> captor = ArgumentCaptor.forClass(List.class);
        verify(listener).onLocationChanged(captor.capture(), nullable(IRemoteCallback.class));

        // Assert that no MSL fields are populated.
        Location actual = captor.getValue().get(0);
        assertThat(actual.hasMslAltitude()).isFalse();
        assertThat(actual.hasMslAltitudeAccuracy()).isFalse();
    }

    @MediumTest
    @Test
    public void testEnableMsl_invalidAltitude() throws Exception {
        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LOCATION,
                "enable_location_provider_manager_msl", Boolean.toString(true), false);

        // Create a random location and set provider location to cache necessary MSL assets.
        Location loc = createLocation(NAME, mRandom);
        loc.setAltitude(mRandom.nextDouble());
        loc.setVerticalAccuracyMeters(mRandom.nextFloat());
        mProvider.setProviderLocation(LocationResult.wrap(loc));
        Thread.sleep(1000);

        // Register listener and reset provider location with invalid altitude to capture.
        ILocationListener listener = createMockLocationListener();
        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
        mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
        loc.setAltitude(Double.POSITIVE_INFINITY);
        mProvider.setProviderLocation(LocationResult.wrap(loc));
        ArgumentCaptor<List<Location>> captor = ArgumentCaptor.forClass(List.class);
        verify(listener).onLocationChanged(captor.capture(), nullable(IRemoteCallback.class));

        // Assert that no MSL fields are populated.
        Location actual = captor.getValue().get(0);
        assertThat(actual.hasMslAltitude()).isFalse();
        assertThat(actual.hasMslAltitudeAccuracy()).isFalse();
    }

    @MediumTest
    @Test
    public void testDisableMsl_expectedBehavior() throws Exception {
        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LOCATION,
                "enable_location_provider_manager_msl", Boolean.toString(false), false);

        // Create a random location and set provider location to cache necessary MSL assets.
        Location loc = createLocation(NAME, mRandom);
        loc.setAltitude(mRandom.nextDouble());
        loc.setVerticalAccuracyMeters(mRandom.nextFloat());
        mProvider.setProviderLocation(LocationResult.wrap(loc));
        Thread.sleep(1000);

        // Register listener and reset provider location to capture.
        ILocationListener listener = createMockLocationListener();
        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
        mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
        mProvider.setProviderLocation(LocationResult.wrap(loc));
        ArgumentCaptor<List<Location>> captor = ArgumentCaptor.forClass(List.class);
        verify(listener).onLocationChanged(captor.capture(), nullable(IRemoteCallback.class));

        // Assert that no MSL fields are populated.
        Location actual = captor.getValue().get(0);
        assertThat(actual.hasMslAltitude()).isFalse();
        assertThat(actual.hasMslAltitudeAccuracy()).isFalse();
    }

    private ILocationListener createMockLocationListener() {
        return spy(new ILocationListener.Stub() {
            @Override