blob: 402b6018e0391c94f4d34c10aafae5838ec8b24a [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;
29
30/**
31 * A helper class, that handles operations in remote listeners, and tracks for remote process death.
32 */
33abstract class RemoteListenerHelper<TListener extends IInterface> {
destradaa6568d702014-10-27 12:47:41 -070034 protected static final int RESULT_SUCCESS = 0;
35 protected static final int RESULT_NOT_AVAILABLE = 1;
36 protected static final int RESULT_NOT_SUPPORTED = 2;
37 protected static final int RESULT_GPS_LOCATION_DISABLED = 3;
38 protected static final int RESULT_INTERNAL_ERROR = 4;
destradaaea8a8a62014-06-23 18:19:03 -070039
destradaa6568d702014-10-27 12:47:41 -070040 private final Handler mHandler;
41 private final String mTag;
42
43 private final HashMap<IBinder, LinkedListener> mListenerMap = new HashMap<>();
44
45 private boolean mIsRegistered;
46 private boolean mHasIsSupported;
47 private boolean mIsSupported;
48
49 protected RemoteListenerHelper(Handler handler, String name) {
destradaa4b3e3932014-07-21 18:01:47 -070050 Preconditions.checkNotNull(name);
destradaa6568d702014-10-27 12:47:41 -070051 mHandler = handler;
destradaa4b3e3932014-07-21 18:01:47 -070052 mTag = name;
53 }
54
destradaaea8a8a62014-06-23 18:19:03 -070055 public boolean addListener(@NonNull TListener listener) {
56 Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
destradaaea8a8a62014-06-23 18:19:03 -070057 IBinder binder = listener.asBinder();
58 LinkedListener deathListener = new LinkedListener(listener);
59 synchronized (mListenerMap) {
60 if (mListenerMap.containsKey(binder)) {
61 // listener already added
62 return true;
63 }
destradaaea8a8a62014-06-23 18:19:03 -070064 try {
65 binder.linkToDeath(deathListener, 0 /* flags */);
66 } catch (RemoteException e) {
67 // if the remote process registering the listener is already death, just swallow the
destradaa6568d702014-10-27 12:47:41 -070068 // exception and return
69 Log.v(mTag, "Remote listener already died.", e);
destradaaea8a8a62014-06-23 18:19:03 -070070 return false;
71 }
destradaaea8a8a62014-06-23 18:19:03 -070072 mListenerMap.put(binder, deathListener);
destradaaea8a8a62014-06-23 18:19:03 -070073
destradaa6568d702014-10-27 12:47:41 -070074 // update statuses we already know about, starting from the ones that will never change
75 int result;
76 if (!isAvailableInPlatform()) {
77 result = RESULT_NOT_AVAILABLE;
78 } else if (mHasIsSupported && !mIsSupported) {
79 result = RESULT_NOT_SUPPORTED;
80 } else if (!isGpsEnabled()) {
81 result = RESULT_GPS_LOCATION_DISABLED;
82 } else if (!tryRegister()) {
83 // only attempt to register if GPS is enabled, otherwise we will register once GPS
84 // becomes available
85 result = RESULT_INTERNAL_ERROR;
86 } else if (mHasIsSupported && mIsSupported) {
87 result = RESULT_SUCCESS;
88 } else {
89 // at this point if the supported flag is not set, the notification will be sent
90 // asynchronously in the future
91 return true;
92 }
93 post(listener, getHandlerOperation(result));
94 }
destradaaea8a8a62014-06-23 18:19:03 -070095 return true;
96 }
97
destradaa6568d702014-10-27 12:47:41 -070098 public void removeListener(@NonNull TListener listener) {
destradaaea8a8a62014-06-23 18:19:03 -070099 Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
destradaaea8a8a62014-06-23 18:19:03 -0700100 IBinder binder = listener.asBinder();
101 LinkedListener linkedListener;
102 synchronized (mListenerMap) {
103 linkedListener = mListenerMap.remove(binder);
destradaa6568d702014-10-27 12:47:41 -0700104 if (mListenerMap.isEmpty()) {
105 tryUnregister();
destradaaea8a8a62014-06-23 18:19:03 -0700106 }
107 }
destradaaea8a8a62014-06-23 18:19:03 -0700108 if (linkedListener != null) {
109 binder.unlinkToDeath(linkedListener, 0 /* flags */);
110 }
destradaaea8a8a62014-06-23 18:19:03 -0700111 }
112
destradaa6568d702014-10-27 12:47:41 -0700113 public void onGpsEnabledChanged(boolean enabled) {
114 // handle first the sub-class implementation, so any error in registration can take
115 // precedence
116 handleGpsEnabledChanged(enabled);
117 synchronized (mListenerMap) {
118 if (!enabled) {
119 tryUnregister();
120 return;
121 }
122 if (mListenerMap.isEmpty()) {
123 return;
124 }
125 if (tryRegister()) {
126 // registration was successful, there is no need to update the state
127 return;
128 }
129 ListenerOperation<TListener> operation = getHandlerOperation(RESULT_INTERNAL_ERROR);
130 foreachUnsafe(operation);
131 }
132 }
133
134 protected abstract boolean isAvailableInPlatform();
135 protected abstract boolean isGpsEnabled();
destradaa4b3e3932014-07-21 18:01:47 -0700136 protected abstract boolean registerWithService();
137 protected abstract void unregisterFromService();
destradaa6568d702014-10-27 12:47:41 -0700138 protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
139 protected abstract void handleGpsEnabledChanged(boolean enabled);
destradaaea8a8a62014-06-23 18:19:03 -0700140
141 protected interface ListenerOperation<TListener extends IInterface> {
142 void execute(TListener listener) throws RemoteException;
143 }
144
destradaa6568d702014-10-27 12:47:41 -0700145 protected void foreach(ListenerOperation<TListener> operation) {
destradaaea8a8a62014-06-23 18:19:03 -0700146 synchronized (mListenerMap) {
destradaa6568d702014-10-27 12:47:41 -0700147 foreachUnsafe(operation);
destradaaea8a8a62014-06-23 18:19:03 -0700148 }
destradaa6568d702014-10-27 12:47:41 -0700149 }
destradaaea8a8a62014-06-23 18:19:03 -0700150
destradaa6568d702014-10-27 12:47:41 -0700151 protected void setSupported(boolean value, ListenerOperation<TListener> notifier) {
152 synchronized (mListenerMap) {
153 mHasIsSupported = true;
154 mIsSupported = value;
155 foreachUnsafe(notifier);
destradaaea8a8a62014-06-23 18:19:03 -0700156 }
157 }
158
destradaa6568d702014-10-27 12:47:41 -0700159 private void foreachUnsafe(ListenerOperation<TListener> operation) {
160 for (LinkedListener linkedListener : mListenerMap.values()) {
161 post(linkedListener.getUnderlyingListener(), operation);
162 }
163 }
164
165 private void post(TListener listener, ListenerOperation<TListener> operation) {
166 if (operation != null) {
167 mHandler.post(new HandlerRunnable(listener, operation));
168 }
169 }
170
171 private boolean tryRegister() {
172 if (!mIsRegistered) {
173 mIsRegistered = registerWithService();
174 }
175 return mIsRegistered;
176 }
177
178 private void tryUnregister() {
179 if (!mIsRegistered) {
180 return;
181 }
182 unregisterFromService();
183 mIsRegistered = false;
184 }
185
destradaaea8a8a62014-06-23 18:19:03 -0700186 private class LinkedListener implements IBinder.DeathRecipient {
187 private final TListener mListener;
188
189 public LinkedListener(@NonNull TListener listener) {
190 mListener = listener;
191 }
192
193 @NonNull
194 public TListener getUnderlyingListener() {
195 return mListener;
196 }
197
198 @Override
199 public void binderDied() {
destradaa4b3e3932014-07-21 18:01:47 -0700200 Log.d(mTag, "Remote Listener died: " + mListener);
destradaaea8a8a62014-06-23 18:19:03 -0700201 removeListener(mListener);
202 }
203 }
destradaa6568d702014-10-27 12:47:41 -0700204
205 private class HandlerRunnable implements Runnable {
206 private final TListener mListener;
207 private final ListenerOperation<TListener> mOperation;
208
209 public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) {
210 mListener = listener;
211 mOperation = operation;
212 }
213
214 @Override
215 public void run() {
216 try {
217 mOperation.execute(mListener);
218 } catch (RemoteException e) {
219 Log.v(mTag, "Error in monitored listener.", e);
220 }
221 }
222 }
destradaaea8a8a62014-06-23 18:19:03 -0700223}