blob: ec2828b1a59f183abd52c02f136282d3f6d6ddc5 [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
19import com.android.internal.util.Preconditions;
20
21import android.annotation.NonNull;
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;
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;
destradaaea8a8a62014-06-23 18:19:03 -070042
destradaa6568d702014-10-27 12:47:41 -070043 private final Handler mHandler;
44 private final String mTag;
45
destradaa13a60b02015-01-15 18:36:01 -080046 private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>();
destradaa6568d702014-10-27 12:47:41 -070047
48 private boolean mIsRegistered;
49 private boolean mHasIsSupported;
50 private boolean mIsSupported;
51
destradaa13a60b02015-01-15 18:36:01 -080052 private int mLastReportedResult = RESULT_UNKNOWN;
53
destradaa6568d702014-10-27 12:47:41 -070054 protected RemoteListenerHelper(Handler handler, String name) {
destradaa4b3e3932014-07-21 18:01:47 -070055 Preconditions.checkNotNull(name);
destradaa6568d702014-10-27 12:47:41 -070056 mHandler = handler;
destradaa4b3e3932014-07-21 18:01:47 -070057 mTag = name;
58 }
59
destradaaea8a8a62014-06-23 18:19:03 -070060 public boolean addListener(@NonNull TListener listener) {
61 Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
destradaaea8a8a62014-06-23 18:19:03 -070062 IBinder binder = listener.asBinder();
63 LinkedListener deathListener = new LinkedListener(listener);
64 synchronized (mListenerMap) {
65 if (mListenerMap.containsKey(binder)) {
66 // listener already added
67 return true;
68 }
destradaaea8a8a62014-06-23 18:19:03 -070069 try {
70 binder.linkToDeath(deathListener, 0 /* flags */);
71 } catch (RemoteException e) {
72 // if the remote process registering the listener is already death, just swallow the
destradaa6568d702014-10-27 12:47:41 -070073 // exception and return
74 Log.v(mTag, "Remote listener already died.", e);
destradaaea8a8a62014-06-23 18:19:03 -070075 return false;
76 }
destradaaea8a8a62014-06-23 18:19:03 -070077 mListenerMap.put(binder, deathListener);
destradaaea8a8a62014-06-23 18:19:03 -070078
destradaa6568d702014-10-27 12:47:41 -070079 // update statuses we already know about, starting from the ones that will never change
80 int result;
81 if (!isAvailableInPlatform()) {
82 result = RESULT_NOT_AVAILABLE;
83 } else if (mHasIsSupported && !mIsSupported) {
84 result = RESULT_NOT_SUPPORTED;
85 } else if (!isGpsEnabled()) {
86 result = RESULT_GPS_LOCATION_DISABLED;
87 } else if (!tryRegister()) {
88 // only attempt to register if GPS is enabled, otherwise we will register once GPS
89 // becomes available
90 result = RESULT_INTERNAL_ERROR;
91 } else if (mHasIsSupported && mIsSupported) {
92 result = RESULT_SUCCESS;
93 } else {
94 // at this point if the supported flag is not set, the notification will be sent
95 // asynchronously in the future
96 return true;
97 }
98 post(listener, getHandlerOperation(result));
99 }
destradaaea8a8a62014-06-23 18:19:03 -0700100 return true;
101 }
102
destradaa6568d702014-10-27 12:47:41 -0700103 public void removeListener(@NonNull TListener listener) {
destradaaea8a8a62014-06-23 18:19:03 -0700104 Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
destradaaea8a8a62014-06-23 18:19:03 -0700105 IBinder binder = listener.asBinder();
106 LinkedListener linkedListener;
107 synchronized (mListenerMap) {
108 linkedListener = mListenerMap.remove(binder);
destradaa6568d702014-10-27 12:47:41 -0700109 if (mListenerMap.isEmpty()) {
110 tryUnregister();
destradaaea8a8a62014-06-23 18:19:03 -0700111 }
112 }
destradaaea8a8a62014-06-23 18:19:03 -0700113 if (linkedListener != null) {
114 binder.unlinkToDeath(linkedListener, 0 /* flags */);
115 }
destradaaea8a8a62014-06-23 18:19:03 -0700116 }
117
destradaa6568d702014-10-27 12:47:41 -0700118 protected abstract boolean isAvailableInPlatform();
119 protected abstract boolean isGpsEnabled();
destradaa4b3e3932014-07-21 18:01:47 -0700120 protected abstract boolean registerWithService();
121 protected abstract void unregisterFromService();
destradaa6568d702014-10-27 12:47:41 -0700122 protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
destradaaea8a8a62014-06-23 18:19:03 -0700123
124 protected interface ListenerOperation<TListener extends IInterface> {
125 void execute(TListener listener) throws RemoteException;
126 }
127
destradaa6568d702014-10-27 12:47:41 -0700128 protected void foreach(ListenerOperation<TListener> operation) {
destradaaea8a8a62014-06-23 18:19:03 -0700129 synchronized (mListenerMap) {
destradaa6568d702014-10-27 12:47:41 -0700130 foreachUnsafe(operation);
destradaaea8a8a62014-06-23 18:19:03 -0700131 }
destradaa6568d702014-10-27 12:47:41 -0700132 }
destradaaea8a8a62014-06-23 18:19:03 -0700133
destradaa13a60b02015-01-15 18:36:01 -0800134 protected void setSupported(boolean value) {
destradaa6568d702014-10-27 12:47:41 -0700135 synchronized (mListenerMap) {
136 mHasIsSupported = true;
137 mIsSupported = value;
destradaa13a60b02015-01-15 18:36:01 -0800138 }
139 }
140
141 protected boolean tryUpdateRegistrationWithService() {
142 synchronized (mListenerMap) {
143 if (!isGpsEnabled()) {
144 tryUnregister();
145 return true;
146 }
147 if (mListenerMap.isEmpty()) {
148 return true;
149 }
150 if (tryRegister()) {
151 // registration was successful, there is no need to update the state
152 return true;
153 }
154 ListenerOperation<TListener> operation = getHandlerOperation(RESULT_INTERNAL_ERROR);
155 foreachUnsafe(operation);
156 return false;
157 }
158 }
159
160 protected void updateResult() {
161 synchronized (mListenerMap) {
162 int newResult = calculateCurrentResultUnsafe();
163 if (mLastReportedResult == newResult) {
164 return;
165 }
166 foreachUnsafe(getHandlerOperation(newResult));
167 mLastReportedResult = newResult;
destradaaea8a8a62014-06-23 18:19:03 -0700168 }
169 }
170
destradaa6568d702014-10-27 12:47:41 -0700171 private void foreachUnsafe(ListenerOperation<TListener> operation) {
172 for (LinkedListener linkedListener : mListenerMap.values()) {
173 post(linkedListener.getUnderlyingListener(), operation);
174 }
175 }
176
177 private void post(TListener listener, ListenerOperation<TListener> operation) {
178 if (operation != null) {
179 mHandler.post(new HandlerRunnable(listener, operation));
180 }
181 }
182
183 private boolean tryRegister() {
184 if (!mIsRegistered) {
185 mIsRegistered = registerWithService();
186 }
187 return mIsRegistered;
188 }
189
190 private void tryUnregister() {
191 if (!mIsRegistered) {
192 return;
193 }
194 unregisterFromService();
195 mIsRegistered = false;
196 }
197
destradaa13a60b02015-01-15 18:36:01 -0800198 private int calculateCurrentResultUnsafe() {
199 // update statuses we already know about, starting from the ones that will never change
200 if (!isAvailableInPlatform()) {
201 return RESULT_NOT_AVAILABLE;
202 }
203 if (!mHasIsSupported || mListenerMap.isEmpty()) {
204 // we'll update once we have a supported status available
205 return RESULT_UNKNOWN;
206 }
207 if (!mIsSupported) {
208 return RESULT_NOT_SUPPORTED;
209 }
210 if (!isGpsEnabled()) {
211 return RESULT_GPS_LOCATION_DISABLED;
212 }
213 return RESULT_SUCCESS;
214 }
215
destradaaea8a8a62014-06-23 18:19:03 -0700216 private class LinkedListener implements IBinder.DeathRecipient {
217 private final TListener mListener;
218
219 public LinkedListener(@NonNull TListener listener) {
220 mListener = listener;
221 }
222
223 @NonNull
224 public TListener getUnderlyingListener() {
225 return mListener;
226 }
227
228 @Override
229 public void binderDied() {
destradaa4b3e3932014-07-21 18:01:47 -0700230 Log.d(mTag, "Remote Listener died: " + mListener);
destradaaea8a8a62014-06-23 18:19:03 -0700231 removeListener(mListener);
232 }
233 }
destradaa6568d702014-10-27 12:47:41 -0700234
235 private class HandlerRunnable implements Runnable {
236 private final TListener mListener;
237 private final ListenerOperation<TListener> mOperation;
238
239 public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) {
240 mListener = listener;
241 mOperation = operation;
242 }
243
244 @Override
245 public void run() {
246 try {
247 mOperation.execute(mListener);
248 } catch (RemoteException e) {
249 Log.v(mTag, "Error in monitored listener.", e);
250 }
251 }
252 }
destradaaea8a8a62014-06-23 18:19:03 -0700253}