blob: 015227394ffe66ef52b7db09050d3ccba56311dd [file] [log] [blame]
destradaaea8a8a62014-06-23 18:19:03 -07001/*
2 * Copyright (C) 2014 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
17package com.android.server.location;
18
destradaaea8a8a62014-06-23 18:19:03 -070019import android.annotation.NonNull;
Anil Admal75b9fd62018-11-28 11:22:50 -080020import android.app.AppOpsManager;
21import android.content.Context;
destradaa6568d702014-10-27 12:47:41 -070022import android.os.Handler;
destradaaea8a8a62014-06-23 18:19:03 -070023import android.os.IBinder;
24import android.os.IInterface;
25import android.os.RemoteException;
26import android.util.Log;
27
destradaaea8a8a62014-06-23 18:19:03 -070028import java.util.HashMap;
destradaa13a60b02015-01-15 18:36:01 -080029import java.util.Map;
Daulet Zhanguzin90e91ce2020-01-02 17:37:10 +000030import java.util.Objects;
destradaaea8a8a62014-06-23 18:19:03 -070031
32/**
Anil Admal98d49b72019-02-06 15:26:33 -080033 * A helper class that handles operations in remote listeners.
34 *
35 * @param <TListener> the type of GNSS data listener.
destradaaea8a8a62014-06-23 18:19:03 -070036 */
Anil Admal98d49b72019-02-06 15:26:33 -080037public abstract class RemoteListenerHelper<TListener extends IInterface> {
destradaa13a60b02015-01-15 18:36:01 -080038
destradaa6568d702014-10-27 12:47:41 -070039 protected static final int RESULT_SUCCESS = 0;
40 protected static final int RESULT_NOT_AVAILABLE = 1;
41 protected static final int RESULT_NOT_SUPPORTED = 2;
42 protected static final int RESULT_GPS_LOCATION_DISABLED = 3;
43 protected static final int RESULT_INTERNAL_ERROR = 4;
destradaa13a60b02015-01-15 18:36:01 -080044 protected static final int RESULT_UNKNOWN = 5;
gomo48f1a642017-11-10 20:35:46 -080045 protected static final int RESULT_NOT_ALLOWED = 6;
destradaaea8a8a62014-06-23 18:19:03 -070046
gomo226b7b72018-12-12 16:49:39 -080047 protected final Handler mHandler;
destradaa6568d702014-10-27 12:47:41 -070048 private final String mTag;
49
Anil Admal98d49b72019-02-06 15:26:33 -080050 private final Map<IBinder, IdentifiedListener> mListenerMap = new HashMap<>();
destradaa6568d702014-10-27 12:47:41 -070051
Anil Admal75b9fd62018-11-28 11:22:50 -080052 protected final Context mContext;
53 protected final AppOpsManager mAppOps;
54
Wyatt Riley74479bd2018-01-17 08:48:27 -080055 private volatile boolean mIsRegistered; // must access only on handler thread, or read-only
56
destradaa6568d702014-10-27 12:47:41 -070057 private boolean mHasIsSupported;
58 private boolean mIsSupported;
59
destradaa13a60b02015-01-15 18:36:01 -080060 private int mLastReportedResult = RESULT_UNKNOWN;
61
Anil Admal75b9fd62018-11-28 11:22:50 -080062 protected RemoteListenerHelper(Context context, Handler handler, String name) {
Daulet Zhanguzin90e91ce2020-01-02 17:37:10 +000063 Objects.requireNonNull(name);
destradaa6568d702014-10-27 12:47:41 -070064 mHandler = handler;
destradaa4b3e3932014-07-21 18:01:47 -070065 mTag = name;
Anil Admal75b9fd62018-11-28 11:22:50 -080066 mContext = context;
67 mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
destradaa4b3e3932014-07-21 18:01:47 -070068 }
69
Wyatt Riley74479bd2018-01-17 08:48:27 -080070 // read-only access for a dump() thread assured via volatile
71 public boolean isRegistered() {
72 return mIsRegistered;
73 }
74
Anil Admal98d49b72019-02-06 15:26:33 -080075 /**
76 * Adds GNSS data listener {@code listener} with caller identify {@code callerIdentify}.
77 */
78 public void addListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
Daulet Zhanguzin90e91ce2020-01-02 17:37:10 +000079 Objects.requireNonNull(listener, "Attempted to register a 'null' listener.");
destradaaea8a8a62014-06-23 18:19:03 -070080 IBinder binder = listener.asBinder();
destradaaea8a8a62014-06-23 18:19:03 -070081 synchronized (mListenerMap) {
82 if (mListenerMap.containsKey(binder)) {
83 // listener already added
Anil Admal98d49b72019-02-06 15:26:33 -080084 return;
destradaaea8a8a62014-06-23 18:19:03 -070085 }
Anil Admal98d49b72019-02-06 15:26:33 -080086
87 IdentifiedListener identifiedListener = new IdentifiedListener(listener,
88 callerIdentity);
89 mListenerMap.put(binder, identifiedListener);
destradaaea8a8a62014-06-23 18:19:03 -070090
destradaa6568d702014-10-27 12:47:41 -070091 // update statuses we already know about, starting from the ones that will never change
92 int result;
93 if (!isAvailableInPlatform()) {
94 result = RESULT_NOT_AVAILABLE;
95 } else if (mHasIsSupported && !mIsSupported) {
96 result = RESULT_NOT_SUPPORTED;
97 } else if (!isGpsEnabled()) {
destradaa6568d702014-10-27 12:47:41 -070098 // only attempt to register if GPS is enabled, otherwise we will register once GPS
99 // becomes available
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700100 result = RESULT_GPS_LOCATION_DISABLED;
destradaa6568d702014-10-27 12:47:41 -0700101 } else if (mHasIsSupported && mIsSupported) {
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700102 tryRegister();
103 // initially presume success, possible internal error could follow asynchornously
destradaa6568d702014-10-27 12:47:41 -0700104 result = RESULT_SUCCESS;
105 } else {
106 // at this point if the supported flag is not set, the notification will be sent
107 // asynchronously in the future
Anil Admal98d49b72019-02-06 15:26:33 -0800108 return;
destradaa6568d702014-10-27 12:47:41 -0700109 }
Anil Admal98d49b72019-02-06 15:26:33 -0800110 post(identifiedListener, getHandlerOperation(result));
destradaa6568d702014-10-27 12:47:41 -0700111 }
destradaaea8a8a62014-06-23 18:19:03 -0700112 }
113
Anil Admal98d49b72019-02-06 15:26:33 -0800114 /**
115 * Remove GNSS data listener {@code listener}.
116 */
destradaa6568d702014-10-27 12:47:41 -0700117 public void removeListener(@NonNull TListener listener) {
Daulet Zhanguzin90e91ce2020-01-02 17:37:10 +0000118 Objects.requireNonNull(listener, "Attempted to remove a 'null' listener.");
destradaaea8a8a62014-06-23 18:19:03 -0700119 synchronized (mListenerMap) {
Anil Admal98d49b72019-02-06 15:26:33 -0800120 mListenerMap.remove(listener.asBinder());
destradaa6568d702014-10-27 12:47:41 -0700121 if (mListenerMap.isEmpty()) {
122 tryUnregister();
destradaaea8a8a62014-06-23 18:19:03 -0700123 }
124 }
destradaaea8a8a62014-06-23 18:19:03 -0700125 }
126
destradaa6568d702014-10-27 12:47:41 -0700127 protected abstract boolean isAvailableInPlatform();
128 protected abstract boolean isGpsEnabled();
gomo48f1a642017-11-10 20:35:46 -0800129 // must access only on handler thread
130 protected abstract int registerWithService();
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700131 protected abstract void unregisterFromService(); // must access only on handler thread
destradaa6568d702014-10-27 12:47:41 -0700132 protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
destradaaea8a8a62014-06-23 18:19:03 -0700133
134 protected interface ListenerOperation<TListener extends IInterface> {
Anil Admal08b96122019-01-30 16:55:05 -0800135 void execute(TListener listener, CallerIdentity callerIdentity) throws RemoteException;
destradaaea8a8a62014-06-23 18:19:03 -0700136 }
137
destradaa6568d702014-10-27 12:47:41 -0700138 protected void foreach(ListenerOperation<TListener> operation) {
destradaaea8a8a62014-06-23 18:19:03 -0700139 synchronized (mListenerMap) {
destradaa6568d702014-10-27 12:47:41 -0700140 foreachUnsafe(operation);
destradaaea8a8a62014-06-23 18:19:03 -0700141 }
destradaa6568d702014-10-27 12:47:41 -0700142 }
destradaaea8a8a62014-06-23 18:19:03 -0700143
destradaa13a60b02015-01-15 18:36:01 -0800144 protected void setSupported(boolean value) {
destradaa6568d702014-10-27 12:47:41 -0700145 synchronized (mListenerMap) {
146 mHasIsSupported = true;
147 mIsSupported = value;
destradaa13a60b02015-01-15 18:36:01 -0800148 }
149 }
150
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700151 protected void tryUpdateRegistrationWithService() {
destradaa13a60b02015-01-15 18:36:01 -0800152 synchronized (mListenerMap) {
153 if (!isGpsEnabled()) {
154 tryUnregister();
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700155 return;
destradaa13a60b02015-01-15 18:36:01 -0800156 }
157 if (mListenerMap.isEmpty()) {
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700158 return;
destradaa13a60b02015-01-15 18:36:01 -0800159 }
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700160 tryRegister();
destradaa13a60b02015-01-15 18:36:01 -0800161 }
162 }
163
164 protected void updateResult() {
165 synchronized (mListenerMap) {
166 int newResult = calculateCurrentResultUnsafe();
167 if (mLastReportedResult == newResult) {
168 return;
169 }
170 foreachUnsafe(getHandlerOperation(newResult));
171 mLastReportedResult = newResult;
destradaaea8a8a62014-06-23 18:19:03 -0700172 }
173 }
174
Anil Admal08b96122019-01-30 16:55:05 -0800175 protected boolean hasPermission(Context context, CallerIdentity callerIdentity) {
176 if (LocationPermissionUtil.doesCallerReportToAppOps(context, callerIdentity)) {
177 // The caller is identified as a location provider that will report location
178 // access to AppOps. Skip noteOp but do checkOp to check for location permission.
179 return mAppOps.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid,
180 callerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED;
181 }
182
183 return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid,
Philip P. Moltmann6c7377c2019-09-27 17:06:25 -0700184 callerIdentity.mPackageName, callerIdentity.mFeatureId,
Philip P. Moltmannbc8b48a2019-09-27 17:06:25 -0700185 "Location sent to " + callerIdentity.mListenerIdentifier)
186 == AppOpsManager.MODE_ALLOWED;
Anil Admal75b9fd62018-11-28 11:22:50 -0800187 }
188
189 protected void logPermissionDisabledEventNotReported(String tag, String packageName,
190 String event) {
191 if (Log.isLoggable(tag, Log.DEBUG)) {
192 Log.d(tag, "Location permission disabled. Skipping " + event + " reporting for app: "
193 + packageName);
destradaa6568d702014-10-27 12:47:41 -0700194 }
195 }
196
Anil Admal75b9fd62018-11-28 11:22:50 -0800197 private void foreachUnsafe(ListenerOperation<TListener> operation) {
Anil Admal98d49b72019-02-06 15:26:33 -0800198 for (IdentifiedListener identifiedListener : mListenerMap.values()) {
199 post(identifiedListener, operation);
Anil Admal75b9fd62018-11-28 11:22:50 -0800200 }
201 }
202
Anil Admal98d49b72019-02-06 15:26:33 -0800203 private void post(IdentifiedListener identifiedListener,
204 ListenerOperation<TListener> operation) {
destradaa6568d702014-10-27 12:47:41 -0700205 if (operation != null) {
Anil Admal98d49b72019-02-06 15:26:33 -0800206 mHandler.post(new HandlerRunnable(identifiedListener, operation));
destradaa6568d702014-10-27 12:47:41 -0700207 }
208 }
209
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700210 private void tryRegister() {
211 mHandler.post(new Runnable() {
gomo48f1a642017-11-10 20:35:46 -0800212 int registrationState = RESULT_INTERNAL_ERROR;
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700213 @Override
214 public void run() {
215 if (!mIsRegistered) {
gomo48f1a642017-11-10 20:35:46 -0800216 registrationState = registerWithService();
217 mIsRegistered = registrationState == RESULT_SUCCESS;
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700218 }
219 if (!mIsRegistered) {
220 // post back a failure
Anil Admal75b9fd62018-11-28 11:22:50 -0800221 mHandler.post(() -> {
222 synchronized (mListenerMap) {
223 foreachUnsafe(getHandlerOperation(registrationState));
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700224 }
225 });
226 }
227 }
228 });
destradaa6568d702014-10-27 12:47:41 -0700229 }
230
231 private void tryUnregister() {
Anil Admal75b9fd62018-11-28 11:22:50 -0800232 mHandler.post(() -> {
233 if (!mIsRegistered) {
234 return;
235 }
236 unregisterFromService();
237 mIsRegistered = false;
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700238 }
Anil Admal75b9fd62018-11-28 11:22:50 -0800239 );
destradaa6568d702014-10-27 12:47:41 -0700240 }
241
destradaa13a60b02015-01-15 18:36:01 -0800242 private int calculateCurrentResultUnsafe() {
243 // update statuses we already know about, starting from the ones that will never change
244 if (!isAvailableInPlatform()) {
245 return RESULT_NOT_AVAILABLE;
246 }
247 if (!mHasIsSupported || mListenerMap.isEmpty()) {
248 // we'll update once we have a supported status available
249 return RESULT_UNKNOWN;
250 }
251 if (!mIsSupported) {
252 return RESULT_NOT_SUPPORTED;
253 }
254 if (!isGpsEnabled()) {
255 return RESULT_GPS_LOCATION_DISABLED;
256 }
257 return RESULT_SUCCESS;
258 }
259
Anil Admal98d49b72019-02-06 15:26:33 -0800260 private class IdentifiedListener {
destradaaea8a8a62014-06-23 18:19:03 -0700261 private final TListener mListener;
Anil Admal08b96122019-01-30 16:55:05 -0800262 private final CallerIdentity mCallerIdentity;
destradaaea8a8a62014-06-23 18:19:03 -0700263
Anil Admal98d49b72019-02-06 15:26:33 -0800264 private IdentifiedListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
destradaaea8a8a62014-06-23 18:19:03 -0700265 mListener = listener;
Anil Admal08b96122019-01-30 16:55:05 -0800266 mCallerIdentity = callerIdentity;
destradaaea8a8a62014-06-23 18:19:03 -0700267 }
destradaaea8a8a62014-06-23 18:19:03 -0700268 }
destradaa6568d702014-10-27 12:47:41 -0700269
270 private class HandlerRunnable implements Runnable {
Anil Admal98d49b72019-02-06 15:26:33 -0800271 private final IdentifiedListener mIdentifiedListener;
destradaa6568d702014-10-27 12:47:41 -0700272 private final ListenerOperation<TListener> mOperation;
273
Anil Admal98d49b72019-02-06 15:26:33 -0800274 private HandlerRunnable(IdentifiedListener identifiedListener,
275 ListenerOperation<TListener> operation) {
276 mIdentifiedListener = identifiedListener;
destradaa6568d702014-10-27 12:47:41 -0700277 mOperation = operation;
278 }
279
280 @Override
281 public void run() {
282 try {
Anil Admal98d49b72019-02-06 15:26:33 -0800283 mOperation.execute(mIdentifiedListener.mListener,
284 mIdentifiedListener.mCallerIdentity);
destradaa6568d702014-10-27 12:47:41 -0700285 } catch (RemoteException e) {
286 Log.v(mTag, "Error in monitored listener.", e);
287 }
288 }
289 }
destradaaea8a8a62014-06-23 18:19:03 -0700290}