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.injector;
18 
19 import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
20 
21 import android.annotation.Nullable;
22 import android.location.util.identity.CallerIdentity;
23 
24 import com.android.server.location.LocationPermissions;
25 import com.android.server.location.LocationPermissions.PermissionLevel;
26 
27 import java.util.concurrent.CopyOnWriteArrayList;
28 
29 /**
30  * Provides helpers and listeners for appops.
31  */
32 public abstract class LocationPermissionsHelper {
33 
34     /**
35      * Listener for current user changes.
36      */
37     public interface LocationPermissionsListener {
38 
39         /**
40          * Called when something has changed about location permissions for the given package. A
41          * null package indicates this affects every package.
42          */
onLocationPermissionsChanged(@ullable String packageName)43         void onLocationPermissionsChanged(@Nullable String packageName);
44 
45         /**
46          * Called when something has changed about location permissions for the given uid.
47          */
onLocationPermissionsChanged(int uid)48         void onLocationPermissionsChanged(int uid);
49     }
50 
51     private final CopyOnWriteArrayList<LocationPermissionsListener> mListeners;
52     private final AppOpsHelper mAppOps;
53 
LocationPermissionsHelper(AppOpsHelper appOps)54     public LocationPermissionsHelper(AppOpsHelper appOps) {
55         mListeners = new CopyOnWriteArrayList<>();
56         mAppOps = appOps;
57 
58         mAppOps.addListener(this::onAppOpsChanged);
59     }
60 
notifyLocationPermissionsChanged(String packageName)61     protected final void notifyLocationPermissionsChanged(String packageName) {
62         for (LocationPermissionsListener listener : mListeners) {
63             listener.onLocationPermissionsChanged(packageName);
64         }
65     }
66 
notifyLocationPermissionsChanged(int uid)67     protected final void notifyLocationPermissionsChanged(int uid) {
68         for (LocationPermissionsListener listener : mListeners) {
69             listener.onLocationPermissionsChanged(uid);
70         }
71     }
72 
onAppOpsChanged(String packageName)73     private void onAppOpsChanged(String packageName) {
74         notifyLocationPermissionsChanged(packageName);
75     }
76 
77     /**
78      * Adds a listener for location permissions events. Callbacks occur on an unspecified thread.
79      */
addListener(LocationPermissionsListener listener)80     public final void addListener(LocationPermissionsListener listener) {
81         mListeners.add(listener);
82     }
83 
84     /**
85      * Removes a listener for location permissions events.
86      */
removeListener(LocationPermissionsListener listener)87     public final void removeListener(LocationPermissionsListener listener) {
88         mListeners.remove(listener);
89     }
90 
91     /**
92      * Returns true if the given identity may access location at the given permissions level, taking
93      * into account both permissions and appops.
94      */
hasLocationPermissions(@ermissionLevel int permissionLevel, CallerIdentity identity)95     public final boolean hasLocationPermissions(@PermissionLevel int permissionLevel,
96             CallerIdentity identity) {
97         if (permissionLevel == PERMISSION_NONE) {
98             return false;
99         }
100 
101         if (!hasPermission(LocationPermissions.asPermission(permissionLevel), identity)) {
102             return false;
103         }
104 
105         return mAppOps.checkOpNoThrow(LocationPermissions.asAppOp(permissionLevel), identity);
106     }
107 
hasPermission(String permission, CallerIdentity callerIdentity)108     protected abstract boolean hasPermission(String permission, CallerIdentity callerIdentity);
109 }
110