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