• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.location.provider;
18 
19 import static android.location.Location.EXTRA_NO_GPS_LOCATION;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SuppressLint;
24 import android.annotation.SystemApi;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.location.Location;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.Looper;
32 import android.os.RemoteException;
33 import android.util.Log;
34 
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.Objects;
38 
39 /**
40  * Base class for location providers outside the system server.
41  *
42  * Location providers should be wrapped in a non-exported service which returns the result of
43  * {@link #getBinder()} from the service's {@link android.app.Service#onBind(Intent)} method. The
44  * service should not be exported so that components other than the system server cannot bind to it.
45  * Alternatively, the service may be guarded by a permission that only system server can obtain. The
46  * service may specify metadata on its capabilities:
47  *
48  * <ul>
49  *     <li>
50  *         "serviceVersion": An integer version code to help tie break if multiple services are
51  *         capable of implementing the same location provider. All else equal, the service with the
52  *         highest version code will be chosen. Assumed to be 0 if not specified.
53  *     </li>
54  *     <li>
55  *         "serviceIsMultiuser": A boolean property, indicating if the service wishes to take
56  *         responsibility for handling changes to the current user on the device. If true, the
57  *         service will always be bound from the system user. If false, the service will always be
58  *         bound from the current user. If the current user changes, the old binding will be
59  *         released, and a new binding established under the new user. Assumed to be false if not
60  *         specified.
61  *     </li>
62  * </ul>
63  *
64  * <p>The service should have an intent filter in place for the location provider it wishes to
65  * implements. Defaults for some providers are specified as constants in this class.
66  *
67  * <p>Location providers are identified by their UID / package name / attribution tag. Based on this
68  * identity, location providers may be given some special privileges (such as making special
69  * requests to other location providers).
70  *
71  * @hide
72  */
73 @SystemApi
74 public abstract class LocationProviderBase {
75 
76     /**
77      * Callback to be invoked when a flush operation is complete and all flushed locations have been
78      * reported.
79      */
80     public interface OnFlushCompleteCallback {
81 
82         /**
83          * Should be invoked once the flush is complete.
84          */
onFlushComplete()85         void onFlushComplete();
86     }
87 
88     /**
89      * The action the wrapping service should have in its intent filter to implement the
90      * {@link android.location.LocationManager#NETWORK_PROVIDER}.
91      */
92     @SuppressLint("ActionValue")
93     public static final String ACTION_NETWORK_PROVIDER =
94             "com.android.location.service.v3.NetworkLocationProvider";
95 
96     /**
97      * The action the wrapping service should have in its intent filter to implement the
98      * {@link android.location.LocationManager#FUSED_PROVIDER}.
99      */
100     @SuppressLint("ActionValue")
101     public static final String ACTION_FUSED_PROVIDER =
102             "com.android.location.service.FusedLocationProvider";
103 
104     final String mTag;
105     final @Nullable String mAttributionTag;
106     final IBinder mBinder;
107 
108     // write locked on mBinder, read lock is optional depending on atomicity requirements
109     volatile @Nullable ILocationProviderManager mManager;
110     volatile ProviderProperties mProperties;
111     volatile boolean mAllowed;
112 
LocationProviderBase(@onNull Context context, @NonNull String tag, @NonNull ProviderProperties properties)113     public LocationProviderBase(@NonNull Context context, @NonNull String tag,
114             @NonNull ProviderProperties properties) {
115         mTag = tag;
116         mAttributionTag = context.getAttributionTag();
117         mBinder = new Service();
118 
119         mManager = null;
120         mProperties = Objects.requireNonNull(properties);
121         mAllowed = true;
122     }
123 
124     /**
125      * Returns the IBinder instance that should be returned from the
126      * {@link android.app.Service#onBind(Intent)} method of the wrapping service.
127      */
getBinder()128     public final @Nullable IBinder getBinder() {
129         return mBinder;
130     }
131 
132     /**
133      * Sets whether this provider is currently allowed or not. Note that this is specific to the
134      * provider only, and is unrelated to global location settings. This is a hint to the location
135      * manager that this provider will be unable to fulfill incoming requests. Setting a provider
136      * as not allowed will result in the provider being disabled. Setting a provider as allowed
137      * means that the provider may be in either the enabled or disabled state.
138      *
139      * <p>Some guidelines: providers should set their own allowed/disallowed status based only on
140      * state "owned" by that provider. For instance, providers should not take into account the
141      * state of the location master setting when setting themselves allowed or disallowed, as this
142      * state is not owned by a particular provider. If a provider requires some additional user
143      * consent that is particular to the provider, this should be use to set the allowed/disallowed
144      * state. If the provider proxies to another provider, the child provider's allowed/disallowed
145      * state should be taken into account in the parent's allowed state. For most providers, it is
146      * expected that they will be always allowed.
147      */
setAllowed(boolean allowed)148     public void setAllowed(boolean allowed) {
149         synchronized (mBinder) {
150             if (mAllowed == allowed) {
151                 return;
152             }
153 
154             mAllowed = allowed;
155         }
156 
157         ILocationProviderManager manager = mManager;
158         if (manager != null) {
159             try {
160                 manager.onSetAllowed(mAllowed);
161             } catch (RemoteException e) {
162                 throw e.rethrowFromSystemServer();
163             } catch (RuntimeException e) {
164                 Log.w(mTag, e);
165             }
166         }
167     }
168 
169     /**
170      * Returns true if this provider is allowed. Providers start as allowed on construction.
171      */
isAllowed()172     public boolean isAllowed() {
173         return mAllowed;
174     }
175 
176     /**
177      * Sets the provider properties that may be queried by clients. Generally speaking, providers
178      * should try to avoid changing their properties after construction.
179      */
setProperties(@onNull ProviderProperties properties)180     public void setProperties(@NonNull ProviderProperties properties) {
181         synchronized (mBinder) {
182             mProperties = Objects.requireNonNull(properties);
183         }
184 
185         ILocationProviderManager manager = mManager;
186         if (manager != null) {
187             try {
188                 manager.onSetProperties(mProperties);
189             } catch (RemoteException e) {
190                 throw e.rethrowFromSystemServer();
191             } catch (RuntimeException e) {
192                 Log.w(mTag, e);
193             }
194         }
195     }
196 
197     /**
198      * Returns the currently set properties of the provider.
199      */
getProperties()200     public @NonNull ProviderProperties getProperties() {
201         return mProperties;
202     }
203 
204     /**
205      * Reports a new location from this provider.
206      */
reportLocation(@onNull Location location)207     public void reportLocation(@NonNull Location location) {
208         ILocationProviderManager manager = mManager;
209         if (manager != null) {
210             try {
211                 manager.onReportLocation(stripExtras(location));
212             } catch (RemoteException e) {
213                 throw e.rethrowFromSystemServer();
214             } catch (RuntimeException e) {
215                 Log.w(mTag, e);
216             }
217         }
218     }
219 
220     /**
221      * Reports a new batch of locations from this provider. Locations must be ordered in the list
222      * from earliest first to latest last.
223      */
reportLocations(@onNull List<Location> locations)224     public void reportLocations(@NonNull List<Location> locations) {
225         ILocationProviderManager manager = mManager;
226         if (manager != null) {
227 
228 
229             try {
230                 manager.onReportLocations(stripExtras(locations));
231             } catch (RemoteException e) {
232                 throw e.rethrowFromSystemServer();
233             } catch (RuntimeException e) {
234                 Log.w(mTag, e);
235             }
236         }
237     }
238 
239     /**
240      * Set the current {@link ProviderRequest} for this provider. Each call to this method overrides
241      * any prior ProviderRequests. The provider should immediately attempt to provide locations (or
242      * not provide locations) according to the parameters of the provider request.
243      */
onSetRequest(@onNull ProviderRequest request)244     public abstract void onSetRequest(@NonNull ProviderRequest request);
245 
246     /**
247      * Requests a flush of any pending batched locations. The callback must always be invoked once
248      * per invocation, and should be invoked after {@link #reportLocation(Location)} or
249      * {@link #reportLocations(List)} has been invoked with any flushed locations. The callback may
250      * be invoked immediately if no locations are flushed.
251      */
onFlush(@onNull OnFlushCompleteCallback callback)252     public abstract void onFlush(@NonNull OnFlushCompleteCallback callback);
253 
254     /**
255      * Implements optional custom commands.
256      */
onSendExtraCommand(@onNull String command, @SuppressLint("NullableCollection") @Nullable Bundle extras)257     public abstract void onSendExtraCommand(@NonNull String command,
258             @SuppressLint("NullableCollection")
259             @Nullable Bundle extras);
260 
stripExtras(Location location)261     private static Location stripExtras(Location location) {
262         Bundle extras = location.getExtras();
263         if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION)
264                 || extras.containsKey("indoorProbability")
265                 || extras.containsKey("coarseLocation"))) {
266             location = new Location(location);
267             extras = location.getExtras();
268             extras.remove(EXTRA_NO_GPS_LOCATION);
269             extras.remove("indoorProbability");
270             extras.remove("coarseLocation");
271             if (extras.isEmpty()) {
272                 location.setExtras(null);
273             }
274         }
275         return location;
276     }
277 
stripExtras(List<Location> locations)278     private static List<Location> stripExtras(List<Location> locations) {
279         List<Location> mapped = locations;
280         final int size = locations.size();
281         int i = 0;
282         for (Location location : locations) {
283             Location newLocation = stripExtras(location);
284             if (mapped != locations) {
285                 mapped.add(newLocation);
286             } else if (newLocation != location) {
287                 mapped = new ArrayList<>(size);
288                 int j = 0;
289                 for (Location copiedLocation : locations) {
290                     if (j >= i) {
291                         break;
292                     }
293                     mapped.add(copiedLocation);
294                     j++;
295                 }
296                 mapped.add(newLocation);
297             }
298             i++;
299         }
300 
301         return mapped;
302     }
303 
304     private final class Service extends ILocationProvider.Stub {
305 
Service()306         Service() {}
307 
308         @Override
setLocationProviderManager(ILocationProviderManager manager)309         public void setLocationProviderManager(ILocationProviderManager manager) {
310             synchronized (mBinder) {
311                 try {
312                     manager.onInitialize(mAllowed, mProperties, mAttributionTag);
313                 } catch (RemoteException | RuntimeException e) {
314                     Log.w(mTag, e);
315                 }
316 
317                 mManager = manager;
318             }
319         }
320 
321         @Override
setRequest(ProviderRequest request)322         public void setRequest(ProviderRequest request) {
323             try {
324                 onSetRequest(request);
325             } catch (RuntimeException e) {
326                 // exceptions on one-way binder threads are dropped - move to a different thread
327                 Log.w(mTag, e);
328                 new Handler(Looper.getMainLooper()).post(() -> {
329                     throw new AssertionError(e);
330                 });
331             }
332         }
333 
334         @Override
flush()335         public void flush() {
336             try {
337                 onFlush(this::onFlushComplete);
338             } catch (RuntimeException e) {
339                 // exceptions on one-way binder threads are dropped - move to a different thread
340                 Log.w(mTag, e);
341                 new Handler(Looper.getMainLooper()).post(() -> {
342                     throw new AssertionError(e);
343                 });
344             }
345         }
346 
onFlushComplete()347         private void onFlushComplete() {
348             ILocationProviderManager manager = mManager;
349             if (manager != null) {
350                 try {
351                     manager.onFlushComplete();
352                 } catch (RemoteException | RuntimeException e) {
353                     Log.w(mTag, e);
354                 }
355             }
356         }
357 
358         @Override
sendExtraCommand(String command, Bundle extras)359         public void sendExtraCommand(String command, Bundle extras) {
360             try {
361                 onSendExtraCommand(command, extras);
362             } catch (RuntimeException e) {
363                 // exceptions on one-way binder threads are dropped - move to a different thread
364                 Log.w(mTag, e);
365                 new Handler(Looper.getMainLooper()).post(() -> {
366                     throw new AssertionError(e);
367                 });
368             }
369         }
370     }
371 }
372