1 package com.android.server.location; 2 3 import android.location.IGpsGeofenceHardware; 4 import android.os.Handler; 5 import android.os.IBinder; 6 import android.os.Looper; 7 import android.util.Log; 8 import android.util.SparseArray; 9 10 import com.android.internal.annotations.VisibleForTesting; 11 12 import java.util.concurrent.Callable; 13 import java.util.concurrent.ExecutionException; 14 import java.util.concurrent.FutureTask; 15 16 /** 17 * Manages GNSS Geofence operations. 18 */ 19 class GnssGeofenceProvider extends IGpsGeofenceHardware.Stub { 20 21 private static final String TAG = "GnssGeofenceProvider"; 22 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 23 24 /** Holds the parameters of a geofence. */ 25 private static class GeofenceEntry { 26 public int geofenceId; 27 public double latitude; 28 public double longitude; 29 public double radius; 30 public int lastTransition; 31 public int monitorTransitions; 32 public int notificationResponsiveness; 33 public int unknownTimer; 34 public boolean paused; 35 } 36 37 private final GnssGeofenceProviderNative mNative; 38 private final SparseArray<GeofenceEntry> mGeofenceEntries = new SparseArray<>(); 39 private final Handler mHandler; 40 GnssGeofenceProvider(Looper looper)41 GnssGeofenceProvider(Looper looper) { 42 this(looper, new GnssGeofenceProviderNative()); 43 } 44 45 @VisibleForTesting GnssGeofenceProvider(Looper looper, GnssGeofenceProviderNative gnssGeofenceProviderNative)46 GnssGeofenceProvider(Looper looper, GnssGeofenceProviderNative gnssGeofenceProviderNative) { 47 mHandler = new Handler(looper); 48 mNative = gnssGeofenceProviderNative; 49 } 50 51 // TODO(b/37460011): use this method in HAL death recovery. resumeIfStarted()52 void resumeIfStarted() { 53 if (DEBUG) { 54 Log.d(TAG, "resumeIfStarted"); 55 } 56 mHandler.post(() -> { 57 for (int i = 0; i < mGeofenceEntries.size(); i++) { 58 GeofenceEntry entry = mGeofenceEntries.valueAt(i); 59 boolean added = mNative.addGeofence(entry.geofenceId, entry.latitude, 60 entry.longitude, 61 entry.radius, 62 entry.lastTransition, entry.monitorTransitions, 63 entry.notificationResponsiveness, entry.unknownTimer); 64 if (added && entry.paused) { 65 mNative.pauseGeofence(entry.geofenceId); 66 } 67 } 68 }); 69 } 70 runOnHandlerThread(Callable<Boolean> callable)71 private boolean runOnHandlerThread(Callable<Boolean> callable) { 72 FutureTask<Boolean> futureTask = new FutureTask<>(callable); 73 mHandler.post(futureTask); 74 try { 75 return futureTask.get(); 76 } catch (InterruptedException | ExecutionException e) { 77 Log.e(TAG, "Failed running callable.", e); 78 } 79 return false; 80 } 81 82 @Override isHardwareGeofenceSupported()83 public boolean isHardwareGeofenceSupported() { 84 return runOnHandlerThread(mNative::isGeofenceSupported); 85 } 86 87 @Override addCircularHardwareGeofence(int geofenceId, double latitude, double longitude, double radius, int lastTransition, int monitorTransitions, int notificationResponsiveness, int unknownTimer)88 public boolean addCircularHardwareGeofence(int geofenceId, double latitude, 89 double longitude, double radius, int lastTransition, int monitorTransitions, 90 int notificationResponsiveness, int unknownTimer) { 91 return runOnHandlerThread(() -> { 92 boolean added = mNative.addGeofence(geofenceId, latitude, longitude, radius, 93 lastTransition, monitorTransitions, notificationResponsiveness, 94 unknownTimer); 95 if (added) { 96 GeofenceEntry entry = new GeofenceEntry(); 97 entry.geofenceId = geofenceId; 98 entry.latitude = latitude; 99 entry.longitude = longitude; 100 entry.radius = radius; 101 entry.lastTransition = lastTransition; 102 entry.monitorTransitions = monitorTransitions; 103 entry.notificationResponsiveness = notificationResponsiveness; 104 entry.unknownTimer = unknownTimer; 105 mGeofenceEntries.put(geofenceId, entry); 106 } 107 return added; 108 }); 109 } 110 111 @Override removeHardwareGeofence(int geofenceId)112 public boolean removeHardwareGeofence(int geofenceId) { 113 return runOnHandlerThread(() -> { 114 boolean removed = mNative.removeGeofence(geofenceId); 115 if (removed) { 116 mGeofenceEntries.remove(geofenceId); 117 } 118 return removed; 119 }); 120 } 121 122 @Override 123 public boolean pauseHardwareGeofence(int geofenceId) { 124 return runOnHandlerThread(() -> { 125 boolean paused = mNative.pauseGeofence(geofenceId); 126 if (paused) { 127 GeofenceEntry entry = mGeofenceEntries.get(geofenceId); 128 if (entry != null) { 129 entry.paused = true; 130 } 131 } 132 return paused; 133 }); 134 } 135 136 @Override 137 public boolean resumeHardwareGeofence(int geofenceId, int monitorTransitions) { 138 return runOnHandlerThread(() -> { 139 boolean resumed = mNative.resumeGeofence(geofenceId, monitorTransitions); 140 if (resumed) { 141 GeofenceEntry entry = mGeofenceEntries.get(geofenceId); 142 if (entry != null) { 143 entry.paused = false; 144 entry.monitorTransitions = monitorTransitions; 145 } 146 } 147 return resumed; 148 }); 149 } 150 151 @VisibleForTesting 152 static class GnssGeofenceProviderNative { 153 public boolean isGeofenceSupported() { 154 return native_is_geofence_supported(); 155 } 156 157 public boolean addGeofence(int geofenceId, double latitude, double longitude, double radius, 158 int lastTransition, int monitorTransitions, int notificationResponsiveness, 159 int unknownTimer) { 160 return native_add_geofence(geofenceId, latitude, longitude, radius, lastTransition, 161 monitorTransitions, notificationResponsiveness, unknownTimer); 162 } 163 164 public boolean removeGeofence(int geofenceId) { 165 return native_remove_geofence(geofenceId); 166 } 167 168 public boolean resumeGeofence(int geofenceId, int transitions) { 169 return native_resume_geofence(geofenceId, transitions); 170 } 171 172 public boolean pauseGeofence(int geofenceId) { 173 return native_pause_geofence(geofenceId); 174 } 175 } 176 177 private static native boolean native_is_geofence_supported(); 178 179 private static native boolean native_add_geofence(int geofenceId, double latitude, 180 double longitude, double radius, int lastTransition, int monitorTransitions, 181 int notificationResponsivenes, int unknownTimer); 182 183 private static native boolean native_remove_geofence(int geofenceId); 184 185 private static native boolean native_resume_geofence(int geofenceId, int transitions); 186 187 private static native boolean native_pause_geofence(int geofenceId); 188 } 189