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