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