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

Commit 31f6a261 authored by Yu-Han Yang's avatar Yu-Han Yang Committed by android-build-merger
Browse files

Merge "Refactor GnssGeofenceProvider" into pi-dev am: 728cb519

am: 87bbfe05

Change-Id: I18a1dc4af712e41c958a63bbcefccb59579d76b0
parents 03d6b03b 87bbfe05
Loading
Loading
Loading
Loading
+188 −0
Original line number Diff line number Diff line
package com.android.server.location;

import android.location.IGpsGeofenceHardware;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * Manages GNSS Geofence operations.
 */
class GnssGeofenceProvider extends IGpsGeofenceHardware.Stub {

    private static final String TAG = "GnssGeofenceProvider";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    /** Holds the parameters of a geofence. */
    private static class GeofenceEntry {
        public int geofenceId;
        public double latitude;
        public double longitude;
        public double radius;
        public int lastTransition;
        public int monitorTransitions;
        public int notificationResponsiveness;
        public int unknownTimer;
        public boolean paused;
    }

    private final GnssGeofenceProviderNative mNative;
    private final SparseArray<GeofenceEntry> mGeofenceEntries = new SparseArray<>();
    private final Handler mHandler;

    GnssGeofenceProvider(Looper looper) {
        this(looper, new GnssGeofenceProviderNative());
    }

    @VisibleForTesting
    GnssGeofenceProvider(Looper looper, GnssGeofenceProviderNative gnssGeofenceProviderNative) {
        mHandler = new Handler(looper);
        mNative = gnssGeofenceProviderNative;
    }

    // TODO(b/37460011): use this method in HAL death recovery.
    void resumeIfStarted() {
        if (DEBUG) {
            Log.d(TAG, "resumeIfStarted");
        }
        mHandler.post(() -> {
            for (int i = 0; i < mGeofenceEntries.size(); i++) {
                GeofenceEntry entry = mGeofenceEntries.valueAt(i);
                boolean added = mNative.addGeofence(entry.geofenceId, entry.latitude,
                        entry.longitude,
                        entry.radius,
                        entry.lastTransition, entry.monitorTransitions,
                        entry.notificationResponsiveness, entry.unknownTimer);
                if (added && entry.paused) {
                    mNative.pauseGeofence(entry.geofenceId);
                }
            }
        });
    }

    private boolean runOnHandlerThread(Callable<Boolean> callable) {
        FutureTask<Boolean> futureTask = new FutureTask<>(callable);
        mHandler.post(futureTask);
        try {
            return futureTask.get();
        } catch (InterruptedException | ExecutionException e) {
            Log.e(TAG, "Failed running callable.", e);
        }
        return false;
    }

    @Override
    public boolean isHardwareGeofenceSupported() {
        return runOnHandlerThread(mNative::isGeofenceSupported);
    }

    @Override
    public boolean addCircularHardwareGeofence(int geofenceId, double latitude,
            double longitude, double radius, int lastTransition, int monitorTransitions,
            int notificationResponsiveness, int unknownTimer) {
        return runOnHandlerThread(() -> {
            boolean added = mNative.addGeofence(geofenceId, latitude, longitude, radius,
                    lastTransition, monitorTransitions, notificationResponsiveness,
                    unknownTimer);
            if (added) {
                GeofenceEntry entry = new GeofenceEntry();
                entry.geofenceId = geofenceId;
                entry.latitude = latitude;
                entry.longitude = longitude;
                entry.radius = radius;
                entry.lastTransition = lastTransition;
                entry.monitorTransitions = monitorTransitions;
                entry.notificationResponsiveness = notificationResponsiveness;
                entry.unknownTimer = unknownTimer;
                mGeofenceEntries.put(geofenceId, entry);
            }
            return added;
        });
    }

    @Override
    public boolean removeHardwareGeofence(int geofenceId) {
        return runOnHandlerThread(() -> {
            boolean removed = mNative.removeGeofence(geofenceId);
            if (removed) {
                mGeofenceEntries.remove(geofenceId);
            }
            return removed;
        });
    }

    @Override
    public boolean pauseHardwareGeofence(int geofenceId) {
        return runOnHandlerThread(() -> {
            boolean paused = mNative.pauseGeofence(geofenceId);
            if (paused) {
                GeofenceEntry entry = mGeofenceEntries.get(geofenceId);
                if (entry != null) {
                    entry.paused = true;
                }
            }
            return paused;
        });
    }

    @Override
    public boolean resumeHardwareGeofence(int geofenceId, int monitorTransitions) {
        return runOnHandlerThread(() -> {
            boolean resumed = mNative.resumeGeofence(geofenceId, monitorTransitions);
            if (resumed) {
                GeofenceEntry entry = mGeofenceEntries.get(geofenceId);
                if (entry != null) {
                    entry.paused = false;
                    entry.monitorTransitions = monitorTransitions;
                }
            }
            return resumed;
        });
    }

    @VisibleForTesting
    static class GnssGeofenceProviderNative {
        public boolean isGeofenceSupported() {
            return native_is_geofence_supported();
        }

        public boolean addGeofence(int geofenceId, double latitude, double longitude, double radius,
                int lastTransition, int monitorTransitions, int notificationResponsiveness,
                int unknownTimer) {
            return native_add_geofence(geofenceId, latitude, longitude, radius, lastTransition,
                    monitorTransitions, notificationResponsiveness, unknownTimer);
        }

        public boolean removeGeofence(int geofenceId) {
            return native_remove_geofence(geofenceId);
        }

        public boolean resumeGeofence(int geofenceId, int transitions) {
            return native_resume_geofence(geofenceId, transitions);
        }

        public boolean pauseGeofence(int geofenceId) {
            return native_pause_geofence(geofenceId);
        }
    }

    private static native boolean native_is_geofence_supported();

    private static native boolean native_add_geofence(int geofenceId, double latitude,
            double longitude, double radius, int lastTransition, int monitorTransitions,
            int notificationResponsivenes, int unknownTimer);

    private static native boolean native_remove_geofence(int geofenceId);

    private static native boolean native_resume_geofence(int geofenceId, int transitions);

    private static native boolean native_pause_geofence(int geofenceId);
}
+3 −39
Original line number Diff line number Diff line
@@ -419,6 +419,7 @@ public class GnssLocationProvider implements LocationProviderInterface, InjectNt
    private final LocationChangeListener mFusedLocationListener = new FusedLocationListener();
    private final NtpTimeHelper mNtpTimeHelper;
    private final GnssBatchingProvider mGnssBatchingProvider;
    private final GnssGeofenceProvider mGnssGeofenceProvider;

    // Handler for processing events
    private Handler mHandler;
@@ -493,7 +494,7 @@ public class GnssLocationProvider implements LocationProviderInterface, InjectNt
    }

    public IGpsGeofenceHardware getGpsGeofenceProxy() {
        return mGpsGeofenceBinder;
        return mGnssGeofenceProvider;
    }

    public GnssMeasurementsProvider getGnssMeasurementsProvider() {
@@ -887,6 +888,7 @@ public class GnssLocationProvider implements LocationProviderInterface, InjectNt
                looper, this);
        mHandler.post(mGnssSatelliteBlacklistHelper::updateSatelliteBlacklist);
        mGnssBatchingProvider = new GnssBatchingProvider();
        mGnssGeofenceProvider = new GnssGeofenceProvider(looper);
    }

    /**
@@ -1501,31 +1503,6 @@ public class GnssLocationProvider implements LocationProviderInterface, InjectNt
        }
    }

    private IGpsGeofenceHardware mGpsGeofenceBinder = new IGpsGeofenceHardware.Stub() {
        public boolean isHardwareGeofenceSupported() {
            return native_is_geofence_supported();
        }

        public boolean addCircularHardwareGeofence(int geofenceId, double latitude,
                double longitude, double radius, int lastTransition, int monitorTransitions,
                int notificationResponsiveness, int unknownTimer) {
            return native_add_geofence(geofenceId, latitude, longitude, radius,
                    lastTransition, monitorTransitions, notificationResponsiveness, unknownTimer);
        }

        public boolean removeHardwareGeofence(int geofenceId) {
            return native_remove_geofence(geofenceId);
        }

        public boolean pauseHardwareGeofence(int geofenceId) {
            return native_pause_geofence(geofenceId);
        }

        public boolean resumeHardwareGeofence(int geofenceId, int monitorTransition) {
            return native_resume_geofence(geofenceId, monitorTransition);
        }
    };

    private boolean deleteAidingData(Bundle extras) {
        int flags;

@@ -2813,19 +2790,6 @@ public class GnssLocationProvider implements LocationProviderInterface, InjectNt
    private native void native_update_network_state(boolean connected, int type,
            boolean roaming, boolean available, String extraInfo, String defaultAPN);

    // Hardware Geofence support.
    private static native boolean native_is_geofence_supported();

    private static native boolean native_add_geofence(int geofenceId, double latitude,
            double longitude, double radius, int lastTransition, int monitorTransitions,
            int notificationResponsivenes, int unknownTimer);

    private static native boolean native_remove_geofence(int geofenceId);

    private static native boolean native_resume_geofence(int geofenceId, int transitions);

    private static native boolean native_pause_geofence(int geofenceId);

    // Gps Hal measurements support.
    private static native boolean native_is_measurement_supported();

+28 −19
Original line number Diff line number Diff line
@@ -1745,12 +1745,12 @@ static void android_location_GnssLocationProvider_update_network_state(JNIEnv* e
    }
}

static jboolean android_location_GnssLocationProvider_is_geofence_supported(
static jboolean android_location_GnssGeofenceProvider_is_geofence_supported(
        JNIEnv* /* env */, jobject /* obj */) {
    return (gnssGeofencingIface != nullptr) ? JNI_TRUE : JNI_FALSE;
}

static jboolean android_location_GnssLocationProvider_add_geofence(JNIEnv* /* env */,
static jboolean android_location_GnssGeofenceProvider_add_geofence(JNIEnv* /* env */,
        jobject /* obj */, jint geofenceId, jdouble latitude, jdouble longitude, jdouble radius,
        jint last_transition, jint monitor_transition, jint notification_responsiveness,
        jint unknown_timer) {
@@ -1766,7 +1766,7 @@ static jboolean android_location_GnssLocationProvider_add_geofence(JNIEnv* /* en
    return JNI_FALSE;
}

static jboolean android_location_GnssLocationProvider_remove_geofence(JNIEnv* /* env */,
static jboolean android_location_GnssGeofenceProvider_remove_geofence(JNIEnv* /* env */,
        jobject /* obj */, jint geofenceId) {
    if (gnssGeofencingIface != nullptr) {
        auto result = gnssGeofencingIface->removeGeofence(geofenceId);
@@ -1777,7 +1777,7 @@ static jboolean android_location_GnssLocationProvider_remove_geofence(JNIEnv* /*
    return JNI_FALSE;
}

static jboolean android_location_GnssLocationProvider_pause_geofence(JNIEnv* /* env */,
static jboolean android_location_GnssGeofenceProvider_pause_geofence(JNIEnv* /* env */,
        jobject /* obj */, jint geofenceId) {
    if (gnssGeofencingIface != nullptr) {
        auto result = gnssGeofencingIface->pauseGeofence(geofenceId);
@@ -1788,7 +1788,7 @@ static jboolean android_location_GnssLocationProvider_pause_geofence(JNIEnv* /*
    return JNI_FALSE;
}

static jboolean android_location_GnssLocationProvider_resume_geofence(JNIEnv* /* env */,
static jboolean android_location_GnssGeofenceProvider_resume_geofence(JNIEnv* /* env */,
        jobject /* obj */, jint geofenceId, jint monitor_transition) {
    if (gnssGeofencingIface != nullptr) {
        auto result = gnssGeofencingIface->resumeGeofence(geofenceId, monitor_transition);
@@ -2178,20 +2178,6 @@ static const JNINativeMethod sMethods[] = {
    {"native_update_network_state",
            "(ZIZZLjava/lang/String;Ljava/lang/String;)V",
            reinterpret_cast<void *>(android_location_GnssLocationProvider_update_network_state)},
    {"native_is_geofence_supported",
            "()Z",
            reinterpret_cast<void *>(android_location_GnssLocationProvider_is_geofence_supported)},
    {"native_add_geofence",
            "(IDDDIIII)Z",
            reinterpret_cast<void *>(android_location_GnssLocationProvider_add_geofence)},
    {"native_remove_geofence",
            "(I)Z",
            reinterpret_cast<void *>(android_location_GnssLocationProvider_remove_geofence)},
    {"native_pause_geofence", "(I)Z", reinterpret_cast<void *>(
            android_location_GnssLocationProvider_pause_geofence)},
    {"native_resume_geofence",
            "(II)Z",
            reinterpret_cast<void *>(android_location_GnssLocationProvider_resume_geofence)},
    {"native_is_measurement_supported",
            "()Z",
            reinterpret_cast<void *>(
@@ -2265,12 +2251,35 @@ static const JNINativeMethod sMethodsBatching[] = {
            reinterpret_cast<void *>(android_location_GnssBatchingProvider_cleanup_batching)},
};

static const JNINativeMethod sGeofenceMethods[] = {
     /* name, signature, funcPtr */
    {"native_is_geofence_supported",
            "()Z",
            reinterpret_cast<void *>(android_location_GnssGeofenceProvider_is_geofence_supported)},
    {"native_add_geofence",
            "(IDDDIIII)Z",
            reinterpret_cast<void *>(android_location_GnssGeofenceProvider_add_geofence)},
    {"native_remove_geofence",
            "(I)Z",
            reinterpret_cast<void *>(android_location_GnssGeofenceProvider_remove_geofence)},
    {"native_pause_geofence", "(I)Z", reinterpret_cast<void *>(
            android_location_GnssGeofenceProvider_pause_geofence)},
    {"native_resume_geofence",
            "(II)Z",
            reinterpret_cast<void *>(android_location_GnssGeofenceProvider_resume_geofence)},
};

int register_android_server_location_GnssLocationProvider(JNIEnv* env) {
    jniRegisterNativeMethods(
            env,
            "com/android/server/location/GnssBatchingProvider",
            sMethodsBatching,
            NELEM(sMethodsBatching));
    jniRegisterNativeMethods(
            env,
            "com/android/server/location/GnssGeofenceProvider",
            sGeofenceMethods,
            NELEM(sGeofenceMethods));
    return jniRegisterNativeMethods(
            env,
            "com/android/server/location/GnssLocationProvider",
+121 −0
Original line number Diff line number Diff line
package com.android.server.location;

import static org.mockito.Matchers.anyDouble;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.os.Looper;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;

import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.SystemLoaderPackages;

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

/**
 * Unit tests for {@link GnssGeofenceProvider}.
 */
@RunWith(FrameworkRobolectricTestRunner.class)
@Config(
        manifest = Config.NONE,
        sdk = 27
)
@SystemLoaderPackages({"com.android.server.location"})
@Presubmit
public class GnssGeofenceProviderTest {
    private static final int GEOFENCE_ID = 12345;
    private static final double LATITUDE = 10.0;
    private static final double LONGITUDE = 20.0;
    private static final double RADIUS = 5.0;
    private static final int LAST_TRANSITION = 0;
    private static final int MONITOR_TRANSITIONS = 0;
    private static final int NOTIFICATION_RESPONSIVENESS = 0;
    private static final int UNKNOWN_TIMER = 0;
    @Mock
    private GnssGeofenceProvider.GnssGeofenceProviderNative mMockNative;
    private GnssGeofenceProvider mTestProvider;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        when(mMockNative.addGeofence(anyInt(), anyDouble(), anyDouble(), anyDouble(), anyInt(),
                anyInt(), anyInt(), anyInt())).thenReturn(true);
        when(mMockNative.pauseGeofence(anyInt())).thenReturn(true);
        when(mMockNative.removeGeofence(anyInt())).thenReturn(true);
        when(mMockNative.resumeGeofence(anyInt(), anyInt())).thenReturn(true);
        mTestProvider = new GnssGeofenceProvider(Looper.myLooper(), mMockNative);
        mTestProvider.addCircularHardwareGeofence(GEOFENCE_ID, LATITUDE,
                LONGITUDE, RADIUS, LAST_TRANSITION, MONITOR_TRANSITIONS,
                NOTIFICATION_RESPONSIVENESS,
                UNKNOWN_TIMER);
    }

    @Test
    public void addGeofence_nativeAdded() {
        verify(mMockNative).addGeofence(eq(GEOFENCE_ID), eq(LATITUDE), eq(LONGITUDE),
                eq(RADIUS), eq(LAST_TRANSITION), eq(MONITOR_TRANSITIONS),
                eq(NOTIFICATION_RESPONSIVENESS),
                eq(UNKNOWN_TIMER));
    }

    @Test
    public void pauseGeofence_nativePaused() {
        mTestProvider.pauseHardwareGeofence(GEOFENCE_ID);
        verify(mMockNative).pauseGeofence(eq(GEOFENCE_ID));
    }

    @Test
    public void removeGeofence_nativeRemoved() {
        mTestProvider.removeHardwareGeofence(GEOFENCE_ID);
        verify(mMockNative).removeGeofence(eq(GEOFENCE_ID));
    }

    @Test
    public void resumeGeofence_nativeResumed() {
        mTestProvider.pauseHardwareGeofence(GEOFENCE_ID);
        mTestProvider.resumeHardwareGeofence(GEOFENCE_ID, MONITOR_TRANSITIONS);
        verify(mMockNative).resumeGeofence(eq(GEOFENCE_ID), eq(MONITOR_TRANSITIONS));
    }

    @Test
    public void addGeofence_restart_added() throws RemoteException {
        mTestProvider.resumeIfStarted();

        verify(mMockNative, times(2)).addGeofence(eq(GEOFENCE_ID), eq(LATITUDE), eq(LONGITUDE),
                eq(RADIUS), eq(LAST_TRANSITION), eq(MONITOR_TRANSITIONS),
                eq(NOTIFICATION_RESPONSIVENESS),
                eq(UNKNOWN_TIMER));
    }

    @Test
    public void removeGeofence_restart_notAdded() throws RemoteException {
        mTestProvider.removeHardwareGeofence(GEOFENCE_ID);
        mTestProvider.resumeIfStarted();

        verify(mMockNative, times(1)).addGeofence(eq(GEOFENCE_ID), eq(LATITUDE), eq(LONGITUDE),
                eq(RADIUS), eq(LAST_TRANSITION), eq(MONITOR_TRANSITIONS),
                eq(NOTIFICATION_RESPONSIVENESS),
                eq(UNKNOWN_TIMER));
    }

    @Test
    public void pauseGeofence_restart_paused() throws RemoteException {
        mTestProvider.pauseHardwareGeofence(GEOFENCE_ID);
        mTestProvider.resumeIfStarted();

        verify(mMockNative, times(2)).addGeofence(eq(GEOFENCE_ID), eq(LATITUDE), eq(LONGITUDE),
                eq(RADIUS), eq(LAST_TRANSITION), eq(MONITOR_TRANSITIONS),
                eq(NOTIFICATION_RESPONSIVENESS),
                eq(UNKNOWN_TIMER));
        verify(mMockNative, times(2)).pauseGeofence(eq(GEOFENCE_ID));
    }
}