• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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