blob: 60ce1f4d56f7895bedaecdbec8983c321d179365 [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
gomo48f1a642017-11-10 20:35:46 -080028import com.android.internal.util.Preconditions;
29
destradaaea8a8a62014-06-23 18:19:03 -070030import java.util.HashMap;
destradaa13a60b02015-01-15 18:36:01 -080031import java.util.Map;
destradaaea8a8a62014-06-23 18:19:03 -070032
33/**
Anil Admal98d49b72019-02-06 15:26:33 -080034 * A helper class that handles operations in remote listeners.
35 *
36 * @param <TListener> the type of GNSS data listener.
destradaaea8a8a62014-06-23 18:19:03 -070037 */
Anil Admal98d49b72019-02-06 15:26:33 -080038public abstract class RemoteListenerHelper<TListener extends IInterface> {
destradaa13a60b02015-01-15 18:36:01 -080039
destradaa6568d702014-10-27 12:47:41 -070040 protected static final int RESULT_SUCCESS = 0;
41 protected static final int RESULT_NOT_AVAILABLE = 1;
42 protected static final int RESULT_NOT_SUPPORTED = 2;
43 protected static final int RESULT_GPS_LOCATION_DISABLED = 3;
44 protected static final int RESULT_INTERNAL_ERROR = 4;
destradaa13a60b02015-01-15 18:36:01 -080045 protected static final int RESULT_UNKNOWN = 5;
gomo48f1a642017-11-10 20:35:46 -080046 protected static final int RESULT_NOT_ALLOWED = 6;
destradaaea8a8a62014-06-23 18:19:03 -070047
gomo226b7b72018-12-12 16:49:39 -080048 protected final Handler mHandler;
destradaa6568d702014-10-27 12:47:41 -070049 private final String mTag;
50
Anil Admal98d49b72019-02-06 15:26:33 -080051 private final Map<IBinder, IdentifiedListener> mListenerMap = new HashMap<>();
destradaa6568d702014-10-27 12:47:41 -070052
Anil Admal75b9fd62018-11-28 11:22:50 -080053 protected final Context mContext;
54 protected final AppOpsManager mAppOps;
55
Wyatt Riley74479bd2018-01-17 08:48:27 -080056 private volatile boolean mIsRegistered; // must access only on handler thread, or read-only
57
destradaa6568d702014-10-27 12:47:41 -070058 private boolean mHasIsSupported;
59 private boolean mIsSupported;
60
destradaa13a60b02015-01-15 18:36:01 -080061 private int mLastReportedResult = RESULT_UNKNOWN;
62
Anil Admal75b9fd62018-11-28 11:22:50 -080063 protected RemoteListenerHelper(Context context, Handler handler, String name) {
destradaa4b3e3932014-07-21 18:01:47 -070064 Preconditions.checkNotNull(name);
destradaa6568d702014-10-27 12:47:41 -070065 mHandler = handler;
destradaa4b3e3932014-07-21 18:01:47 -070066 mTag = name;
Anil Admal75b9fd62018-11-28 11:22:50 -080067 mContext = context;
68 mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
destradaa4b3e3932014-07-21 18:01:47 -070069 }
70
Wyatt Riley74479bd2018-01-17 08:48:27 -080071 // read-only access for a dump() thread assured via volatile
72 public boolean isRegistered() {
73 return mIsRegistered;
74 }
75
Anil Admal98d49b72019-02-06 15:26:33 -080076 /**
77 * Adds GNSS data listener {@code listener} with caller identify {@code callerIdentify}.
78 */
79 public void addListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
destradaaea8a8a62014-06-23 18:19:03 -070080 Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
destradaaea8a8a62014-06-23 18:19:03 -070081 IBinder binder = listener.asBinder();
destradaaea8a8a62014-06-23 18:19:03 -070082 synchronized (mListenerMap) {
83 if (mListenerMap.containsKey(binder)) {
84 // listener already added
Anil Admal98d49b72019-02-06 15:26:33 -080085 return;
destradaaea8a8a62014-06-23 18:19:03 -070086 }
Anil Admal98d49b72019-02-06 15:26:33 -080087
88 IdentifiedListener identifiedListener = new IdentifiedListener(listener,
89 callerIdentity);
90 mListenerMap.put(binder, identifiedListener);
destradaaea8a8a62014-06-23 18:19:03 -070091
destradaa6568d702014-10-27 12:47:41 -070092 // update statuses we already know about, starting from the ones that will never change
93 int result;
94 if (!isAvailableInPlatform()) {
95 result = RESULT_NOT_AVAILABLE;
96 } else if (mHasIsSupported && !mIsSupported) {
97 result = RESULT_NOT_SUPPORTED;
98 } else if (!isGpsEnabled()) {
destradaa6568d702014-10-27 12:47:41 -070099 // only attempt to register if GPS is enabled, otherwise we will register once GPS
100 // becomes available
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700101 result = RESULT_GPS_LOCATION_DISABLED;
destradaa6568d702014-10-27 12:47:41 -0700102 } else if (mHasIsSupported && mIsSupported) {
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700103 tryRegister();
104 // initially presume success, possible internal error could follow asynchornously
destradaa6568d702014-10-27 12:47:41 -0700105 result = RESULT_SUCCESS;
106 } else {
107 // at this point if the supported flag is not set, the notification will be sent
108 // asynchronously in the future
Anil Admal98d49b72019-02-06 15:26:33 -0800109 return;
destradaa6568d702014-10-27 12:47:41 -0700110 }
Anil Admal98d49b72019-02-06 15:26:33 -0800111 post(identifiedListener, getHandlerOperation(result));
destradaa6568d702014-10-27 12:47:41 -0700112 }
destradaaea8a8a62014-06-23 18:19:03 -0700113 }
114
Anil Admal98d49b72019-02-06 15:26:33 -0800115 /**
116 * Remove GNSS data listener {@code listener}.
117 */
destradaa6568d702014-10-27 12:47:41 -0700118 public void removeListener(@NonNull TListener listener) {
destradaaea8a8a62014-06-23 18:19:03 -0700119 Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
destradaaea8a8a62014-06-23 18:19:03 -0700120 synchronized (mListenerMap) {
Anil Admal98d49b72019-02-06 15:26:33 -0800121 mListenerMap.remove(listener.asBinder());
destradaa6568d702014-10-27 12:47:41 -0700122 if (mListenerMap.isEmpty()) {
123 tryUnregister();
destradaaea8a8a62014-06-23 18:19:03 -0700124 }
125 }
destradaaea8a8a62014-06-23 18:19:03 -0700126 }
127
destradaa6568d702014-10-27 12:47:41 -0700128 protected abstract boolean isAvailableInPlatform();
129 protected abstract boolean isGpsEnabled();
gomo48f1a642017-11-10 20:35:46 -0800130 // must access only on handler thread
131 protected abstract int registerWithService();
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700132 protected abstract void unregisterFromService(); // must access only on handler thread
destradaa6568d702014-10-27 12:47:41 -0700133 protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
destradaaea8a8a62014-06-23 18:19:03 -0700134
135 protected interface ListenerOperation<TListener extends IInterface> {
Anil Admal08b96122019-01-30 16:55:05 -0800136 void execute(TListener listener, CallerIdentity callerIdentity) throws RemoteException;
destradaaea8a8a62014-06-23 18:19:03 -0700137 }
138
destradaa6568d702014-10-27 12:47:41 -0700139 protected void foreach(ListenerOperation<TListener> operation) {
destradaaea8a8a62014-06-23 18:19:03 -0700140 synchronized (mListenerMap) {
destradaa6568d702014-10-27 12:47:41 -0700141 foreachUnsafe(operation);
destradaaea8a8a62014-06-23 18:19:03 -0700142 }
destradaa6568d702014-10-27 12:47:41 -0700143 }
destradaaea8a8a62014-06-23 18:19:03 -0700144
destradaa13a60b02015-01-15 18:36:01 -0800145 protected void setSupported(boolean value) {
destradaa6568d702014-10-27 12:47:41 -0700146 synchronized (mListenerMap) {
147 mHasIsSupported = true;
148 mIsSupported = value;
destradaa13a60b02015-01-15 18:36:01 -0800149 }
150 }
151
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700152 protected void tryUpdateRegistrationWithService() {
destradaa13a60b02015-01-15 18:36:01 -0800153 synchronized (mListenerMap) {
154 if (!isGpsEnabled()) {
155 tryUnregister();
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700156 return;
destradaa13a60b02015-01-15 18:36:01 -0800157 }
158 if (mListenerMap.isEmpty()) {
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700159 return;
destradaa13a60b02015-01-15 18:36:01 -0800160 }
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700161 tryRegister();
destradaa13a60b02015-01-15 18:36:01 -0800162 }
163 }
164
165 protected void updateResult() {
166 synchronized (mListenerMap) {
167 int newResult = calculateCurrentResultUnsafe();
168 if (mLastReportedResult == newResult) {
169 return;
170 }
171 foreachUnsafe(getHandlerOperation(newResult));
172 mLastReportedResult = newResult;
destradaaea8a8a62014-06-23 18:19:03 -0700173 }
174 }
175
Anil Admal08b96122019-01-30 16:55:05 -0800176 protected boolean hasPermission(Context context, CallerIdentity callerIdentity) {
177 if (LocationPermissionUtil.doesCallerReportToAppOps(context, callerIdentity)) {
178 // The caller is identified as a location provider that will report location
179 // access to AppOps. Skip noteOp but do checkOp to check for location permission.
180 return mAppOps.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid,
181 callerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED;
182 }
183
184 return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid,
Philip P. Moltmann6c7377c2019-09-27 17:06:25 -0700185 callerIdentity.mPackageName, callerIdentity.mFeatureId,
Philip P. Moltmannbc8b48a2019-09-27 17:06:25 -0700186 "Location sent to " + callerIdentity.mListenerIdentifier)
187 == AppOpsManager.MODE_ALLOWED;
Anil Admal75b9fd62018-11-28 11:22:50 -0800188 }
189
190 protected void logPermissionDisabledEventNotReported(String tag, String packageName,
191 String event) {
192 if (Log.isLoggable(tag, Log.DEBUG)) {
193 Log.d(tag, "Location permission disabled. Skipping " + event + " reporting for app: "
194 + packageName);
destradaa6568d702014-10-27 12:47:41 -0700195 }
196 }
197
Anil Admal75b9fd62018-11-28 11:22:50 -0800198 private void foreachUnsafe(ListenerOperation<TListener> operation) {
Anil Admal98d49b72019-02-06 15:26:33 -0800199 for (IdentifiedListener identifiedListener : mListenerMap.values()) {
200 post(identifiedListener, operation);
Anil Admal75b9fd62018-11-28 11:22:50 -0800201 }
202 }
203
Anil Admal98d49b72019-02-06 15:26:33 -0800204 private void post(IdentifiedListener identifiedListener,
205 ListenerOperation<TListener> operation) {
destradaa6568d702014-10-27 12:47:41 -0700206 if (operation != null) {
Anil Admal98d49b72019-02-06 15:26:33 -0800207 mHandler.post(new HandlerRunnable(identifiedListener, operation));
destradaa6568d702014-10-27 12:47:41 -0700208 }
209 }
210
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700211 private void tryRegister() {
212 mHandler.post(new Runnable() {
gomo48f1a642017-11-10 20:35:46 -0800213 int registrationState = RESULT_INTERNAL_ERROR;
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700214 @Override
215 public void run() {
216 if (!mIsRegistered) {
gomo48f1a642017-11-10 20:35:46 -0800217 registrationState = registerWithService();
218 mIsRegistered = registrationState == RESULT_SUCCESS;
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700219 }
220 if (!mIsRegistered) {
221 // post back a failure
Anil Admal75b9fd62018-11-28 11:22:50 -0800222 mHandler.post(() -> {
223 synchronized (mListenerMap) {
224 foreachUnsafe(getHandlerOperation(registrationState));
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700225 }
226 });
227 }
228 }
229 });
destradaa6568d702014-10-27 12:47:41 -0700230 }
231
232 private void tryUnregister() {
Anil Admal75b9fd62018-11-28 11:22:50 -0800233 mHandler.post(() -> {
234 if (!mIsRegistered) {
235 return;
236 }
237 unregisterFromService();
238 mIsRegistered = false;
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700239 }
Anil Admal75b9fd62018-11-28 11:22:50 -0800240 );
destradaa6568d702014-10-27 12:47:41 -0700241 }
242
destradaa13a60b02015-01-15 18:36:01 -0800243 private int calculateCurrentResultUnsafe() {
244 // update statuses we already know about, starting from the ones that will never change
245 if (!isAvailableInPlatform()) {
246 return RESULT_NOT_AVAILABLE;
247 }
248 if (!mHasIsSupported || mListenerMap.isEmpty()) {
249 // we'll update once we have a supported status available
250 return RESULT_UNKNOWN;
251 }
252 if (!mIsSupported) {
253 return RESULT_NOT_SUPPORTED;
254 }
255 if (!isGpsEnabled()) {
256 return RESULT_GPS_LOCATION_DISABLED;
257 }
258 return RESULT_SUCCESS;
259 }
260
Anil Admal98d49b72019-02-06 15:26:33 -0800261 private class IdentifiedListener {
destradaaea8a8a62014-06-23 18:19:03 -0700262 private final TListener mListener;
Anil Admal08b96122019-01-30 16:55:05 -0800263 private final CallerIdentity mCallerIdentity;
destradaaea8a8a62014-06-23 18:19:03 -0700264
Anil Admal98d49b72019-02-06 15:26:33 -0800265 private IdentifiedListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
destradaaea8a8a62014-06-23 18:19:03 -0700266 mListener = listener;
Anil Admal08b96122019-01-30 16:55:05 -0800267 mCallerIdentity = callerIdentity;
destradaaea8a8a62014-06-23 18:19:03 -0700268 }
destradaaea8a8a62014-06-23 18:19:03 -0700269 }
destradaa6568d702014-10-27 12:47:41 -0700270
271 private class HandlerRunnable implements Runnable {
Anil Admal98d49b72019-02-06 15:26:33 -0800272 private final IdentifiedListener mIdentifiedListener;
destradaa6568d702014-10-27 12:47:41 -0700273 private final ListenerOperation<TListener> mOperation;
274
Anil Admal98d49b72019-02-06 15:26:33 -0800275 private HandlerRunnable(IdentifiedListener identifiedListener,
276 ListenerOperation<TListener> operation) {
277 mIdentifiedListener = identifiedListener;
destradaa6568d702014-10-27 12:47:41 -0700278 mOperation = operation;
279 }
280
281 @Override
282 public void run() {
283 try {
Anil Admal98d49b72019-02-06 15:26:33 -0800284 mOperation.execute(mIdentifiedListener.mListener,
285 mIdentifiedListener.mCallerIdentity);
destradaa6568d702014-10-27 12:47:41 -0700286 } catch (RemoteException e) {
287 Log.v(mTag, "Error in monitored listener.", e);
288 }
289 }
290 }
destradaaea8a8a62014-06-23 18:19:03 -0700291}