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     /**
105      * The action the wrapping service should have in its intent filter to implement the
106      * {@link android.location.LocationManager#GPS_PROVIDER}.
107      */
108     public static final String ACTION_GNSS_PROVIDER =
109             "android.location.provider.action.GNSS_PROVIDER";
110 
111     final String mTag;
112     final @Nullable String mAttributionTag;
113     final IBinder mBinder;
114 
115     // write locked on mBinder, read lock is optional depending on atomicity requirements
116     volatile @Nullable ILocationProviderManager mManager;
117     volatile ProviderProperties mProperties;
118     volatile boolean mAllowed;
119 
LocationProviderBase(@onNull Context context, @NonNull String tag, @NonNull ProviderProperties properties)120     public LocationProviderBase(@NonNull Context context, @NonNull String tag,
121             @NonNull ProviderProperties properties) {
122         mTag = tag;
123         mAttributionTag = context.getAttributionTag();
124         mBinder = new Service();
125 
126         mManager = null;
127         mProperties = Objects.requireNonNull(properties);
128         mAllowed = true;
129     }
130 
131     /**
132      * Returns the IBinder instance that should be returned from the
133      * {@link android.app.Service#onBind(Intent)} method of the wrapping service.
134      */
getBinder()135     public final @Nullable IBinder getBinder() {
136         return mBinder;
137     }
138 
139     /**
140      * Sets whether this provider is currently allowed or not. Note that this is specific to the
141      * provider only, and is unrelated to global location settings. This is a hint to the location
142      * manager that this provider will be unable to fulfill incoming requests. Setting a provider
143      * as not allowed will result in the provider being disabled. Setting a provider as allowed
144      * means that the provider may be in either the enabled or disabled state.
145      *
146      * <p>Some guidelines: providers should set their own allowed/disallowed status based only on
147      * state "owned" by that provider. For instance, providers should not take into account the
148      * state of the location master setting when setting themselves allowed or disallowed, as this
149      * state is not owned by a particular provider. If a provider requires some additional user
150      * consent that is particular to the provider, this should be use to set the allowed/disallowed
151      * state. If the provider proxies to another provider, the child provider's allowed/disallowed
152      * state should be taken into account in the parent's allowed state. For most providers, it is
153      * expected that they will be always allowed.
154      */
setAllowed(boolean allowed)155     public void setAllowed(boolean allowed) {
156         synchronized (mBinder) {
157             if (mAllowed == allowed) {
158                 return;
159             }
160 
161             mAllowed = allowed;
162         }
163 
164         ILocationProviderManager manager = mManager;
165         if (manager != null) {
166             try {
167                 manager.onSetAllowed(mAllowed);
168             } catch (RemoteException e) {
169                 throw e.rethrowFromSystemServer();
170             } catch (RuntimeException e) {
171                 Log.w(mTag, e);
172             }
173         }
174     }
175 
176     /**
177      * Returns true if this provider is allowed. Providers start as allowed on construction.
178      */
isAllowed()179     public boolean isAllowed() {
180         return mAllowed;
181     }
182 
183     /**
184      * Sets the provider properties that may be queried by clients. Generally speaking, providers
185      * should try to avoid changing their properties after construction.
186      */
setProperties(@onNull ProviderProperties properties)187     public void setProperties(@NonNull ProviderProperties properties) {
188         synchronized (mBinder) {
189             mProperties = Objects.requireNonNull(properties);
190         }
191 
192         ILocationProviderManager manager = mManager;
193         if (manager != null) {
194             try {
195                 manager.onSetProperties(mProperties);
196             } catch (RemoteException e) {
197                 throw e.rethrowFromSystemServer();
198             } catch (RuntimeException e) {
199                 Log.w(mTag, e);
200             }
201         }
202     }
203 
204     /**
205      * Returns the currently set properties of the provider.
206      */
getProperties()207     public @NonNull ProviderProperties getProperties() {
208         return mProperties;
209     }
210 
211     /**
212      * Reports a new location from this provider.
213      */
reportLocation(@onNull Location location)214     public void reportLocation(@NonNull Location location) {
215         ILocationProviderManager manager = mManager;
216         if (manager != null) {
217             try {
218                 manager.onReportLocation(stripExtras(location));
219             } catch (RemoteException e) {
220                 throw e.rethrowFromSystemServer();
221             } catch (RuntimeException e) {
222                 Log.w(mTag, e);
223             }
224         }
225     }
226 
227     /**
228      * Reports a new batch of locations from this provider. Locations must be ordered in the list
229      * from earliest first to latest last.
230      */
reportLocations(@onNull List<Location> locations)231     public void reportLocations(@NonNull List<Location> locations) {
232         ILocationProviderManager manager = mManager;
233         if (manager != null) {
234 
235 
236             try {
237                 manager.onReportLocations(stripExtras(locations));
238             } catch (RemoteException e) {
239                 throw e.rethrowFromSystemServer();
240             } catch (RuntimeException e) {
241                 Log.w(mTag, e);
242             }
243         }
244     }
245 
246     /**
247      * Set the current {@link ProviderRequest} for this provider. Each call to this method overrides
248      * any prior ProviderRequests. The provider should immediately attempt to provide locations (or
249      * not provide locations) according to the parameters of the provider request.
250      */
onSetRequest(@onNull ProviderRequest request)251     public abstract void onSetRequest(@NonNull ProviderRequest request);
252 
253     /**
254      * Requests a flush of any pending batched locations. The callback must always be invoked once
255      * per invocation, and should be invoked after {@link #reportLocation(Location)} or
256      * {@link #reportLocations(List)} has been invoked with any flushed locations. The callback may
257      * be invoked immediately if no locations are flushed.
258      */
onFlush(@onNull OnFlushCompleteCallback callback)259     public abstract void onFlush(@NonNull OnFlushCompleteCallback callback);
260 
261     /**
262      * Implements optional custom commands.
263      */
onSendExtraCommand(@onNull String command, @SuppressLint("NullableCollection") @Nullable Bundle extras)264     public abstract void onSendExtraCommand(@NonNull String command,
265             @SuppressLint("NullableCollection")
266             @Nullable Bundle extras);
267 
stripExtras(Location location)268     private static Location stripExtras(Location location) {
269         Bundle extras = location.getExtras();
270         if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION)
271                 || extras.containsKey("indoorProbability")
272                 || extras.containsKey("coarseLocation"))) {
273             location = new Location(location);
274             extras = location.getExtras();
275             extras.remove(EXTRA_NO_GPS_LOCATION);
276             extras.remove("indoorProbability");
277             extras.remove("coarseLocation");
278             if (extras.isEmpty()) {
279                 location.setExtras(null);
280             }
281         }
282         return location;
283     }
284 
stripExtras(List<Location> locations)285     private static List<Location> stripExtras(List<Location> locations) {
286         List<Location> mapped = locations;
287         final int size = locations.size();
288         int i = 0;
289         for (Location location : locations) {
290             Location newLocation = stripExtras(location);
291             if (mapped != locations) {
292                 mapped.add(newLocation);
293             } else if (newLocation != location) {
294                 mapped = new ArrayList<>(size);
295                 int j = 0;
296                 for (Location copiedLocation : locations) {
297                     if (j >= i) {
298                         break;
299                     }
300                     mapped.add(copiedLocation);
301                     j++;
302                 }
303                 mapped.add(newLocation);
304             }
305             i++;
306         }
307 
308         return mapped;
309     }
310 
311     private final class Service extends ILocationProvider.Stub {
312 
Service()313         Service() {}
314 
315         @Override
setLocationProviderManager(ILocationProviderManager manager)316         public void setLocationProviderManager(ILocationProviderManager manager) {
317             synchronized (mBinder) {
318                 try {
319                     manager.onInitialize(mAllowed, mProperties, mAttributionTag);
320                 } catch (RemoteException | RuntimeException e) {
321                     Log.w(mTag, e);
322                 }
323 
324                 mManager = manager;
325             }
326         }
327 
328         @Override
setRequest(ProviderRequest request)329         public void setRequest(ProviderRequest request) {
330             try {
331                 onSetRequest(request);
332             } catch (RuntimeException e) {
333                 // exceptions on one-way binder threads are dropped - move to a different thread
334                 Log.w(mTag, e);
335                 new Handler(Looper.getMainLooper()).post(() -> {
336                     throw new AssertionError(e);
337                 });
338             }
339         }
340 
341         @Override
flush()342         public void flush() {
343             try {
344                 onFlush(this::onFlushComplete);
345             } catch (RuntimeException e) {
346                 // exceptions on one-way binder threads are dropped - move to a different thread
347                 Log.w(mTag, e);
348                 new Handler(Looper.getMainLooper()).post(() -> {
349                     throw new AssertionError(e);
350                 });
351             }
352         }
353 
onFlushComplete()354         private void onFlushComplete() {
355             ILocationProviderManager manager = mManager;
356             if (manager != null) {
357                 try {
358                     manager.onFlushComplete();
359                 } catch (RemoteException | RuntimeException e) {
360                     Log.w(mTag, e);
361                 }
362             }
363         }
364 
365         @Override
sendExtraCommand(String command, Bundle extras)366         public void sendExtraCommand(String command, Bundle extras) {
367             try {
368                 onSendExtraCommand(command, extras);
369             } catch (RuntimeException e) {
370                 // exceptions on one-way binder threads are dropped - move to a different thread
371                 Log.w(mTag, e);
372                 new Handler(Looper.getMainLooper()).post(() -> {
373                     throw new AssertionError(e);
374                 });
375             }
376         }
377     }
378 }
379