1 /*
2  * Copyright (C) 2020 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 com.android.server.location.gnss;
18 
19 import static android.location.LocationManager.GPS_PROVIDER;
20 
21 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
22 import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
23 import static com.android.server.location.gnss.GnssManagerService.TAG;
24 
25 import android.annotation.Nullable;
26 import android.location.LocationManagerInternal;
27 import android.location.LocationManagerInternal.ProviderEnabledListener;
28 import android.location.util.identity.CallerIdentity;
29 import android.os.Binder;
30 import android.os.Build;
31 import android.os.IBinder;
32 import android.os.IInterface;
33 import android.os.Process;
34 import android.util.ArraySet;
35 
36 import com.android.internal.util.Preconditions;
37 import com.android.server.FgThread;
38 import com.android.server.LocalServices;
39 import com.android.server.location.injector.AppForegroundHelper;
40 import com.android.server.location.injector.Injector;
41 import com.android.server.location.injector.LocationPermissionsHelper;
42 import com.android.server.location.injector.PackageResetHelper;
43 import com.android.server.location.injector.SettingsHelper;
44 import com.android.server.location.injector.UserInfoHelper;
45 import com.android.server.location.injector.UserInfoHelper.UserListener;
46 import com.android.server.location.listeners.BinderListenerRegistration;
47 import com.android.server.location.listeners.ListenerMultiplexer;
48 
49 import java.util.Collection;
50 import java.util.Objects;
51 
52 /**
53  * Manager for all GNSS related listeners. This class handles deactivating listeners that do not
54  * belong to the current user, that do not have the appropriate permissions, or that are not
55  * currently in the foreground. It will also disable listeners if the GNSS provider is disabled.
56  * Listeners must be registered with the associated IBinder as the key, if the IBinder dies, the
57  * registration will automatically be removed.
58  *
59  * @param <TRequest>            request type
60  * @param <TListener>           listener type
61  * @param <TMergedRegistration> merged registration type
62  */
63 public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInterface,
64         TMergedRegistration> extends
65         ListenerMultiplexer<IBinder, TListener,
66                 GnssListenerMultiplexer<TRequest, TListener, TMergedRegistration>
67                         .GnssListenerRegistration, TMergedRegistration> {
68 
69     /**
70      * Registration object for GNSS listeners.
71      */
72     protected class GnssListenerRegistration extends
73             BinderListenerRegistration<IBinder, TListener> {
74 
75         private final TRequest mRequest;
76         private final CallerIdentity mIdentity;
77 
78         // we store these values because we don't trust the listeners not to give us dupes, not to
79         // spam us, and because checking the values may be more expensive
80         private boolean mForeground;
81         private boolean mPermitted;
82 
GnssListenerRegistration(TRequest request, CallerIdentity identity, TListener listener)83         protected GnssListenerRegistration(TRequest request, CallerIdentity identity,
84                 TListener listener) {
85             super(identity.isMyProcess() ? FgThread.getExecutor() : DIRECT_EXECUTOR, listener);
86             mRequest = request;
87             mIdentity = identity;
88         }
89 
getRequest()90         public final TRequest getRequest() {
91             return mRequest;
92         }
93 
getIdentity()94         public final CallerIdentity getIdentity() {
95             return mIdentity;
96         }
97 
98         @Override
getTag()99         public String getTag() {
100             return TAG;
101         }
102 
103         @Override
getOwner()104         protected GnssListenerMultiplexer<TRequest, TListener, TMergedRegistration> getOwner() {
105             return GnssListenerMultiplexer.this;
106         }
107 
108         @Override
getBinderFromKey(IBinder key)109         protected IBinder getBinderFromKey(IBinder key) {
110             return key;
111         }
112 
113         /**
114          * Returns true if this registration is currently in the foreground.
115          */
isForeground()116         public boolean isForeground() {
117             return mForeground;
118         }
119 
isPermitted()120         boolean isPermitted() {
121             return mPermitted;
122         }
123 
124         @Override
onRegister()125         protected void onRegister() {
126             super.onRegister();
127 
128             mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
129                     mIdentity);
130             mForeground = mAppForegroundHelper.isAppForeground(mIdentity.getUid());
131         }
132 
onLocationPermissionsChanged(@ullable String packageName)133         boolean onLocationPermissionsChanged(@Nullable String packageName) {
134             if (packageName == null || mIdentity.getPackageName().equals(packageName)) {
135                 return onLocationPermissionsChanged();
136             }
137 
138             return false;
139         }
140 
onLocationPermissionsChanged(int uid)141         boolean onLocationPermissionsChanged(int uid) {
142             if (mIdentity.getUid() == uid) {
143                 return onLocationPermissionsChanged();
144             }
145 
146             return false;
147         }
148 
onLocationPermissionsChanged()149         private boolean onLocationPermissionsChanged() {
150             boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
151                     mIdentity);
152             if (permitted != mPermitted) {
153                 mPermitted = permitted;
154                 return true;
155             }
156 
157             return false;
158         }
159 
onForegroundChanged(int uid, boolean foreground)160         boolean onForegroundChanged(int uid, boolean foreground) {
161             if (mIdentity.getUid() == uid && foreground != mForeground) {
162                 mForeground = foreground;
163                 return true;
164             }
165 
166             return false;
167         }
168 
169         @Override
toString()170         public String toString() {
171             StringBuilder builder = new StringBuilder();
172             builder.append(mIdentity);
173 
174             ArraySet<String> flags = new ArraySet<>(2);
175             if (!mForeground) {
176                 flags.add("bg");
177             }
178             if (!mPermitted) {
179                 flags.add("na");
180             }
181             if (!flags.isEmpty()) {
182                 builder.append(" ").append(flags);
183             }
184 
185             if (mRequest != null) {
186                 builder.append(" ").append(mRequest);
187             }
188             return builder.toString();
189         }
190     }
191 
192     protected final UserInfoHelper mUserInfoHelper;
193     protected final SettingsHelper mSettingsHelper;
194     protected final LocationPermissionsHelper mLocationPermissionsHelper;
195     protected final AppForegroundHelper mAppForegroundHelper;
196     protected final LocationManagerInternal mLocationManagerInternal;
197     private final PackageResetHelper mPackageResetHelper;
198 
199     private final UserListener mUserChangedListener = this::onUserChanged;
200     private final ProviderEnabledListener mProviderEnabledChangedListener =
201             this::onProviderEnabledChanged;
202     private final SettingsHelper.GlobalSettingChangedListener
203             mBackgroundThrottlePackageWhitelistChangedListener =
204             this::onBackgroundThrottlePackageAllowlistChanged;
205     private final SettingsHelper.UserSettingChangedListener
206             mLocationPackageBlacklistChangedListener =
207             this::onLocationPackageDenylistChanged;
208     private final LocationPermissionsHelper.LocationPermissionsListener
209             mLocationPermissionsListener =
210             new LocationPermissionsHelper.LocationPermissionsListener() {
211                 @Override
212                 public void onLocationPermissionsChanged(@Nullable String packageName) {
213                     GnssListenerMultiplexer.this.onLocationPermissionsChanged(packageName);
214                 }
215 
216                 @Override
217                 public void onLocationPermissionsChanged(int uid) {
218                     GnssListenerMultiplexer.this.onLocationPermissionsChanged(uid);
219                 }
220             };
221     private final AppForegroundHelper.AppForegroundListener mAppForegroundChangedListener =
222             this::onAppForegroundChanged;
223     private final PackageResetHelper.Responder mPackageResetResponder =
224             new PackageResetHelper.Responder() {
225                 @Override
226                 public void onPackageReset(String packageName) {
227                     GnssListenerMultiplexer.this.onPackageReset(packageName);
228                 }
229 
230                 @Override
231                 public boolean isResetableForPackage(String packageName) {
232                     return GnssListenerMultiplexer.this.isResetableForPackage(packageName);
233                 }
234             };
235 
GnssListenerMultiplexer(Injector injector)236     protected GnssListenerMultiplexer(Injector injector) {
237         mUserInfoHelper = injector.getUserInfoHelper();
238         mSettingsHelper = injector.getSettingsHelper();
239         mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
240         mAppForegroundHelper = injector.getAppForegroundHelper();
241         mPackageResetHelper = injector.getPackageResetHelper();
242         mLocationManagerInternal = Objects.requireNonNull(
243                 LocalServices.getService(LocationManagerInternal.class));
244     }
245 
246     /**
247      * May be overridden by subclasses to return whether the service is supported or not. This value
248      * should never change for the lifetime of the multiplexer. If the service is unsupported, all
249      * registrations will be treated as inactive and the backing service will never be registered.
250      *
251      */
isSupported()252     public boolean isSupported() {
253         return true;
254     }
255 
256     /**
257      * Adds a listener with the given identity.
258      */
addListener(CallerIdentity identity, TListener listener)259     protected void addListener(CallerIdentity identity, TListener listener) {
260         addListener(null, identity, listener);
261     }
262 
263     /**
264      * Adds a listener with the given identity and request.
265      */
addListener(TRequest request, CallerIdentity callerIdentity, TListener listener)266     protected void addListener(TRequest request, CallerIdentity callerIdentity,
267             TListener listener) {
268         final long identity = Binder.clearCallingIdentity();
269         try {
270             putRegistration(listener.asBinder(),
271                     createRegistration(request, callerIdentity, listener));
272         } finally {
273             Binder.restoreCallingIdentity(identity);
274         }
275     }
276 
277     /**
278      * May be overridden by subclasses to change the registration type.
279      */
createRegistration(TRequest request, CallerIdentity callerIdentity, TListener listener)280     protected GnssListenerRegistration createRegistration(TRequest request,
281             CallerIdentity callerIdentity, TListener listener) {
282         return new GnssListenerRegistration(request, callerIdentity, listener);
283     }
284 
285     /**
286      * Removes the given listener.
287      */
removeListener(TListener listener)288     public void removeListener(TListener listener) {
289         final long identity = Binder.clearCallingIdentity();
290         try {
291             removeRegistration(listener.asBinder());
292         } finally {
293             Binder.restoreCallingIdentity(identity);
294         }
295     }
296 
297     @Override
isActive(GnssListenerRegistration registration)298     protected boolean isActive(GnssListenerRegistration registration) {
299         if (!isSupported()) {
300             return false;
301         }
302 
303         CallerIdentity identity = registration.getIdentity();
304         return registration.isPermitted()
305                 && (registration.isForeground() || isBackgroundRestrictionExempt(identity))
306                 && isActive(identity);
307     }
308 
isActive(CallerIdentity identity)309     private boolean isActive(CallerIdentity identity) {
310         if (identity.isSystemServer()) {
311             if (!mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER,
312                     mUserInfoHelper.getCurrentUserId())) {
313                 return false;
314             }
315         } else {
316             if (!mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER,
317                     identity.getUserId())) {
318                 return false;
319             }
320             if (!mUserInfoHelper.isVisibleUserId(identity.getUserId())) {
321                 return false;
322             }
323             if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
324                     identity.getPackageName())) {
325                 return false;
326             }
327         }
328 
329         return true;
330     }
331 
isBackgroundRestrictionExempt(CallerIdentity identity)332     private boolean isBackgroundRestrictionExempt(CallerIdentity identity) {
333         if (identity.getUid() == Process.SYSTEM_UID) {
334             return true;
335         }
336 
337         if (mSettingsHelper.getBackgroundThrottlePackageWhitelist().contains(
338                 identity.getPackageName())) {
339             return true;
340         }
341 
342         return mLocationManagerInternal.isProvider(null, identity);
343     }
344 
345     // this provides a default implementation for all further subclasses which assumes that there is
346     // never an associated request object, and thus nothing interesting to merge. the majority of
347     // gnss listener multiplexers do not current have associated requests, and the ones that do can
348     // override this implementation.
mergeRegistrations( Collection<GnssListenerRegistration> gnssListenerRegistrations)349     protected TMergedRegistration mergeRegistrations(
350             Collection<GnssListenerRegistration> gnssListenerRegistrations) {
351         if (Build.IS_DEBUGGABLE) {
352             for (GnssListenerRegistration registration : gnssListenerRegistrations) {
353                 Preconditions.checkState(registration.getRequest() == null);
354             }
355         }
356 
357         return null;
358     }
359 
360     @Override
onRegister()361     protected void onRegister() {
362         if (!isSupported()) {
363             return;
364         }
365 
366         mUserInfoHelper.addListener(mUserChangedListener);
367         mLocationManagerInternal.addProviderEnabledListener(GPS_PROVIDER,
368                 mProviderEnabledChangedListener);
369         mSettingsHelper.addOnBackgroundThrottlePackageWhitelistChangedListener(
370                 mBackgroundThrottlePackageWhitelistChangedListener);
371         mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
372                 mLocationPackageBlacklistChangedListener);
373         mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
374         mAppForegroundHelper.addListener(mAppForegroundChangedListener);
375         mPackageResetHelper.register(mPackageResetResponder);
376     }
377 
378     @Override
onUnregister()379     protected void onUnregister() {
380         if (!isSupported()) {
381             return;
382         }
383 
384         mUserInfoHelper.removeListener(mUserChangedListener);
385         mLocationManagerInternal.removeProviderEnabledListener(GPS_PROVIDER,
386                 mProviderEnabledChangedListener);
387         mSettingsHelper.removeOnBackgroundThrottlePackageWhitelistChangedListener(
388                 mBackgroundThrottlePackageWhitelistChangedListener);
389         mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
390                 mLocationPackageBlacklistChangedListener);
391         mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
392         mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
393         mPackageResetHelper.unregister(mPackageResetResponder);
394     }
395 
onUserChanged(int userId, int change)396     private void onUserChanged(int userId, int change) {
397         // current user changes affect whether system server location requests are allowed to access
398         // location, and visibility changes affect whether any given user may access location.
399         if (change == UserListener.CURRENT_USER_CHANGED
400                 || change == UserListener.USER_VISIBILITY_CHANGED) {
401             updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
402         }
403     }
404 
onProviderEnabledChanged(String provider, int userId, boolean enabled)405     private void onProviderEnabledChanged(String provider, int userId, boolean enabled) {
406         Preconditions.checkState(GPS_PROVIDER.equals(provider));
407         updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
408     }
409 
onBackgroundThrottlePackageAllowlistChanged()410     private void onBackgroundThrottlePackageAllowlistChanged() {
411         updateRegistrations(registration -> true);
412     }
413 
onLocationPackageDenylistChanged(int userId)414     private void onLocationPackageDenylistChanged(int userId) {
415         updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
416     }
417 
onLocationPermissionsChanged(@ullable String packageName)418     private void onLocationPermissionsChanged(@Nullable String packageName) {
419         updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
420     }
421 
onLocationPermissionsChanged(int uid)422     private void onLocationPermissionsChanged(int uid) {
423         updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
424     }
425 
onAppForegroundChanged(int uid, boolean foreground)426     private void onAppForegroundChanged(int uid, boolean foreground) {
427         updateRegistrations(registration -> registration.onForegroundChanged(uid, foreground));
428     }
429 
onPackageReset(String packageName)430     private void onPackageReset(String packageName) {
431         updateRegistrations(
432                 registration -> {
433                     if (registration.getIdentity().getPackageName().equals(
434                             packageName)) {
435                         registration.remove();
436                     }
437 
438                     return false;
439                 });
440     }
441 
isResetableForPackage(String packageName)442     private boolean isResetableForPackage(String packageName) {
443         // invoked to find out if the given package has any state that can be "force quit"
444         return findRegistration(
445                 registration -> registration.getIdentity().getPackageName().equals(packageName));
446     }
447 
448     @Override
getServiceState()449     protected String getServiceState() {
450         if (!isSupported()) {
451             return "unsupported";
452         } else {
453             return super.getServiceState();
454         }
455     }
456 }
457