blob: fcdb9d1a87cacac138380022478845138a726cc6 [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;
destradaa6568d702014-10-27 12:47:41 -070020import android.os.Handler;
destradaaea8a8a62014-06-23 18:19:03 -070021import android.os.IBinder;
22import android.os.IInterface;
23import android.os.RemoteException;
24import android.util.Log;
25
gomo48f1a642017-11-10 20:35:46 -080026import com.android.internal.util.Preconditions;
27
destradaaea8a8a62014-06-23 18:19:03 -070028import java.util.HashMap;
destradaa13a60b02015-01-15 18:36:01 -080029import java.util.Map;
destradaaea8a8a62014-06-23 18:19:03 -070030
31/**
32 * A helper class, that handles operations in remote listeners, and tracks for remote process death.
33 */
34abstract class RemoteListenerHelper<TListener extends IInterface> {
destradaa13a60b02015-01-15 18:36:01 -080035
destradaa6568d702014-10-27 12:47:41 -070036 protected static final int RESULT_SUCCESS = 0;
37 protected static final int RESULT_NOT_AVAILABLE = 1;
38 protected static final int RESULT_NOT_SUPPORTED = 2;
39 protected static final int RESULT_GPS_LOCATION_DISABLED = 3;
40 protected static final int RESULT_INTERNAL_ERROR = 4;
destradaa13a60b02015-01-15 18:36:01 -080041 protected static final int RESULT_UNKNOWN = 5;
gomo48f1a642017-11-10 20:35:46 -080042 protected static final int RESULT_NOT_ALLOWED = 6;
destradaaea8a8a62014-06-23 18:19:03 -070043
destradaa6568d702014-10-27 12:47:41 -070044 private final Handler mHandler;
45 private final String mTag;
46
destradaa13a60b02015-01-15 18:36:01 -080047 private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>();
destradaa6568d702014-10-27 12:47:41 -070048
Wyatt Riley74479bd2018-01-17 08:48:27 -080049 private volatile boolean mIsRegistered; // must access only on handler thread, or read-only
50
destradaa6568d702014-10-27 12:47:41 -070051 private boolean mHasIsSupported;
52 private boolean mIsSupported;
53
destradaa13a60b02015-01-15 18:36:01 -080054 private int mLastReportedResult = RESULT_UNKNOWN;
55
destradaa6568d702014-10-27 12:47:41 -070056 protected RemoteListenerHelper(Handler handler, String name) {
destradaa4b3e3932014-07-21 18:01:47 -070057 Preconditions.checkNotNull(name);
destradaa6568d702014-10-27 12:47:41 -070058 mHandler = handler;
destradaa4b3e3932014-07-21 18:01:47 -070059 mTag = name;
60 }
61
Wyatt Riley74479bd2018-01-17 08:48:27 -080062 // read-only access for a dump() thread assured via volatile
63 public boolean isRegistered() {
64 return mIsRegistered;
65 }
66
destradaaea8a8a62014-06-23 18:19:03 -070067 public boolean addListener(@NonNull TListener listener) {
68 Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
destradaaea8a8a62014-06-23 18:19:03 -070069 IBinder binder = listener.asBinder();
70 LinkedListener deathListener = new LinkedListener(listener);
71 synchronized (mListenerMap) {
72 if (mListenerMap.containsKey(binder)) {
73 // listener already added
74 return true;
75 }
destradaaea8a8a62014-06-23 18:19:03 -070076 try {
77 binder.linkToDeath(deathListener, 0 /* flags */);
78 } catch (RemoteException e) {
79 // if the remote process registering the listener is already death, just swallow the
destradaa6568d702014-10-27 12:47:41 -070080 // exception and return
81 Log.v(mTag, "Remote listener already died.", e);
destradaaea8a8a62014-06-23 18:19:03 -070082 return false;
83 }
destradaaea8a8a62014-06-23 18:19:03 -070084 mListenerMap.put(binder, deathListener);
destradaaea8a8a62014-06-23 18:19:03 -070085
destradaa6568d702014-10-27 12:47:41 -070086 // update statuses we already know about, starting from the ones that will never change
87 int result;
88 if (!isAvailableInPlatform()) {
89 result = RESULT_NOT_AVAILABLE;
90 } else if (mHasIsSupported && !mIsSupported) {
91 result = RESULT_NOT_SUPPORTED;
92 } else if (!isGpsEnabled()) {
destradaa6568d702014-10-27 12:47:41 -070093 // only attempt to register if GPS is enabled, otherwise we will register once GPS
94 // becomes available
Wyatt Rileyaa420d52017-07-03 15:14:42 -070095 result = RESULT_GPS_LOCATION_DISABLED;
destradaa6568d702014-10-27 12:47:41 -070096 } else if (mHasIsSupported && mIsSupported) {
Wyatt Rileyaa420d52017-07-03 15:14:42 -070097 tryRegister();
98 // initially presume success, possible internal error could follow asynchornously
destradaa6568d702014-10-27 12:47:41 -070099 result = RESULT_SUCCESS;
100 } else {
101 // at this point if the supported flag is not set, the notification will be sent
102 // asynchronously in the future
103 return true;
104 }
105 post(listener, getHandlerOperation(result));
106 }
destradaaea8a8a62014-06-23 18:19:03 -0700107 return true;
108 }
109
destradaa6568d702014-10-27 12:47:41 -0700110 public void removeListener(@NonNull TListener listener) {
destradaaea8a8a62014-06-23 18:19:03 -0700111 Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
destradaaea8a8a62014-06-23 18:19:03 -0700112 IBinder binder = listener.asBinder();
113 LinkedListener linkedListener;
114 synchronized (mListenerMap) {
115 linkedListener = mListenerMap.remove(binder);
destradaa6568d702014-10-27 12:47:41 -0700116 if (mListenerMap.isEmpty()) {
117 tryUnregister();
destradaaea8a8a62014-06-23 18:19:03 -0700118 }
119 }
destradaaea8a8a62014-06-23 18:19:03 -0700120 if (linkedListener != null) {
121 binder.unlinkToDeath(linkedListener, 0 /* flags */);
122 }
destradaaea8a8a62014-06-23 18:19:03 -0700123 }
124
destradaa6568d702014-10-27 12:47:41 -0700125 protected abstract boolean isAvailableInPlatform();
126 protected abstract boolean isGpsEnabled();
gomo48f1a642017-11-10 20:35:46 -0800127 // must access only on handler thread
128 protected abstract int registerWithService();
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700129 protected abstract void unregisterFromService(); // must access only on handler thread
destradaa6568d702014-10-27 12:47:41 -0700130 protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
destradaaea8a8a62014-06-23 18:19:03 -0700131
132 protected interface ListenerOperation<TListener extends IInterface> {
133 void execute(TListener listener) throws RemoteException;
134 }
135
destradaa6568d702014-10-27 12:47:41 -0700136 protected void foreach(ListenerOperation<TListener> operation) {
destradaaea8a8a62014-06-23 18:19:03 -0700137 synchronized (mListenerMap) {
destradaa6568d702014-10-27 12:47:41 -0700138 foreachUnsafe(operation);
destradaaea8a8a62014-06-23 18:19:03 -0700139 }
destradaa6568d702014-10-27 12:47:41 -0700140 }
destradaaea8a8a62014-06-23 18:19:03 -0700141
destradaa13a60b02015-01-15 18:36:01 -0800142 protected void setSupported(boolean value) {
destradaa6568d702014-10-27 12:47:41 -0700143 synchronized (mListenerMap) {
144 mHasIsSupported = true;
145 mIsSupported = value;
destradaa13a60b02015-01-15 18:36:01 -0800146 }
147 }
148
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700149 protected void tryUpdateRegistrationWithService() {
destradaa13a60b02015-01-15 18:36:01 -0800150 synchronized (mListenerMap) {
151 if (!isGpsEnabled()) {
152 tryUnregister();
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700153 return;
destradaa13a60b02015-01-15 18:36:01 -0800154 }
155 if (mListenerMap.isEmpty()) {
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700156 return;
destradaa13a60b02015-01-15 18:36:01 -0800157 }
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700158 tryRegister();
destradaa13a60b02015-01-15 18:36:01 -0800159 }
160 }
161
162 protected void updateResult() {
163 synchronized (mListenerMap) {
164 int newResult = calculateCurrentResultUnsafe();
165 if (mLastReportedResult == newResult) {
166 return;
167 }
168 foreachUnsafe(getHandlerOperation(newResult));
169 mLastReportedResult = newResult;
destradaaea8a8a62014-06-23 18:19:03 -0700170 }
171 }
172
destradaa6568d702014-10-27 12:47:41 -0700173 private void foreachUnsafe(ListenerOperation<TListener> operation) {
174 for (LinkedListener linkedListener : mListenerMap.values()) {
175 post(linkedListener.getUnderlyingListener(), operation);
176 }
177 }
178
179 private void post(TListener listener, ListenerOperation<TListener> operation) {
180 if (operation != null) {
181 mHandler.post(new HandlerRunnable(listener, operation));
182 }
183 }
184
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700185 private void tryRegister() {
186 mHandler.post(new Runnable() {
gomo48f1a642017-11-10 20:35:46 -0800187 int registrationState = RESULT_INTERNAL_ERROR;
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700188 @Override
189 public void run() {
190 if (!mIsRegistered) {
gomo48f1a642017-11-10 20:35:46 -0800191 registrationState = registerWithService();
192 mIsRegistered = registrationState == RESULT_SUCCESS;
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700193 }
194 if (!mIsRegistered) {
195 // post back a failure
196 mHandler.post(new Runnable() {
197 @Override
198 public void run() {
199 synchronized (mListenerMap) {
gomo48f1a642017-11-10 20:35:46 -0800200 ListenerOperation<TListener> operation = getHandlerOperation(registrationState);
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700201 foreachUnsafe(operation);
202 }
203 }
204 });
205 }
206 }
207 });
destradaa6568d702014-10-27 12:47:41 -0700208 }
209
210 private void tryUnregister() {
Wyatt Rileyaa420d52017-07-03 15:14:42 -0700211 mHandler.post(new Runnable() {
212 @Override
213 public void run() {
214 if (!mIsRegistered) {
215 return;
216 }
217 unregisterFromService();
218 mIsRegistered = false;
219 }
220 });
destradaa6568d702014-10-27 12:47:41 -0700221 }
222
destradaa13a60b02015-01-15 18:36:01 -0800223 private int calculateCurrentResultUnsafe() {
224 // update statuses we already know about, starting from the ones that will never change
225 if (!isAvailableInPlatform()) {
226 return RESULT_NOT_AVAILABLE;
227 }
228 if (!mHasIsSupported || mListenerMap.isEmpty()) {
229 // we'll update once we have a supported status available
230 return RESULT_UNKNOWN;
231 }
232 if (!mIsSupported) {
233 return RESULT_NOT_SUPPORTED;
234 }
235 if (!isGpsEnabled()) {
236 return RESULT_GPS_LOCATION_DISABLED;
237 }
238 return RESULT_SUCCESS;
239 }
240
destradaaea8a8a62014-06-23 18:19:03 -0700241 private class LinkedListener implements IBinder.DeathRecipient {
242 private final TListener mListener;
243
244 public LinkedListener(@NonNull TListener listener) {
245 mListener = listener;
246 }
247
248 @NonNull
249 public TListener getUnderlyingListener() {
250 return mListener;
251 }
252
253 @Override
254 public void binderDied() {
destradaa4b3e3932014-07-21 18:01:47 -0700255 Log.d(mTag, "Remote Listener died: " + mListener);
destradaaea8a8a62014-06-23 18:19:03 -0700256 removeListener(mListener);
257 }
258 }
destradaa6568d702014-10-27 12:47:41 -0700259
260 private class HandlerRunnable implements Runnable {
261 private final TListener mListener;
262 private final ListenerOperation<TListener> mOperation;
263
264 public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) {
265 mListener = listener;
266 mOperation = operation;
267 }
268
269 @Override
270 public void run() {
271 try {
272 mOperation.execute(mListener);
273 } catch (RemoteException e) {
274 Log.v(mTag, "Error in monitored listener.", e);
275 }
276 }
277 }
destradaaea8a8a62014-06-23 18:19:03 -0700278}