• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server.location;
18 
19 import android.annotation.NonNull;
20 import android.os.Handler;
21 import android.os.IBinder;
22 import android.os.IInterface;
23 import android.os.RemoteException;
24 import android.util.Log;
25 
26 import com.android.internal.util.Preconditions;
27 
28 import java.util.HashMap;
29 import java.util.Map;
30 
31 /**
32  * A helper class, that handles operations in remote listeners, and tracks for remote process death.
33  */
34 abstract class RemoteListenerHelper<TListener extends IInterface> {
35 
36     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;
41     protected static final int RESULT_UNKNOWN = 5;
42     protected static final int RESULT_NOT_ALLOWED = 6;
43 
44     private final Handler mHandler;
45     private final String mTag;
46 
47     private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>();
48 
49     private volatile boolean mIsRegistered;  // must access only on handler thread, or read-only
50 
51     private boolean mHasIsSupported;
52     private boolean mIsSupported;
53 
54     private int mLastReportedResult = RESULT_UNKNOWN;
55 
RemoteListenerHelper(Handler handler, String name)56     protected RemoteListenerHelper(Handler handler, String name) {
57         Preconditions.checkNotNull(name);
58         mHandler = handler;
59         mTag = name;
60     }
61 
62     // read-only access for a dump() thread assured via volatile
isRegistered()63     public boolean isRegistered() {
64         return mIsRegistered;
65     }
66 
addListener(@onNull TListener listener)67     public boolean addListener(@NonNull TListener listener) {
68         Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
69         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             }
76             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
80                 // exception and return
81                 Log.v(mTag, "Remote listener already died.", e);
82                 return false;
83             }
84             mListenerMap.put(binder, deathListener);
85 
86             // 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()) {
93                 // only attempt to register if GPS is enabled, otherwise we will register once GPS
94                 // becomes available
95                 result = RESULT_GPS_LOCATION_DISABLED;
96             } else if (mHasIsSupported && mIsSupported) {
97                 tryRegister();
98                 // initially presume success, possible internal error could follow asynchornously
99                 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         }
107         return true;
108     }
109 
removeListener(@onNull TListener listener)110     public void removeListener(@NonNull TListener listener) {
111         Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
112         IBinder binder = listener.asBinder();
113         LinkedListener linkedListener;
114         synchronized (mListenerMap) {
115             linkedListener = mListenerMap.remove(binder);
116             if (mListenerMap.isEmpty()) {
117                 tryUnregister();
118             }
119         }
120         if (linkedListener != null) {
121             binder.unlinkToDeath(linkedListener, 0 /* flags */);
122         }
123     }
124 
isAvailableInPlatform()125     protected abstract boolean isAvailableInPlatform();
isGpsEnabled()126     protected abstract boolean isGpsEnabled();
127     // must access only on handler thread
registerWithService()128     protected abstract int registerWithService();
unregisterFromService()129     protected abstract void unregisterFromService(); // must access only on handler thread
getHandlerOperation(int result)130     protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
131 
132     protected interface ListenerOperation<TListener extends IInterface> {
execute(TListener listener)133         void execute(TListener listener) throws RemoteException;
134     }
135 
foreach(ListenerOperation<TListener> operation)136     protected void foreach(ListenerOperation<TListener> operation) {
137         synchronized (mListenerMap) {
138             foreachUnsafe(operation);
139         }
140     }
141 
setSupported(boolean value)142     protected void setSupported(boolean value) {
143         synchronized (mListenerMap) {
144             mHasIsSupported = true;
145             mIsSupported = value;
146         }
147     }
148 
tryUpdateRegistrationWithService()149     protected void tryUpdateRegistrationWithService() {
150         synchronized (mListenerMap) {
151             if (!isGpsEnabled()) {
152                 tryUnregister();
153                 return;
154             }
155             if (mListenerMap.isEmpty()) {
156                 return;
157             }
158             tryRegister();
159         }
160     }
161 
updateResult()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;
170         }
171     }
172 
foreachUnsafe(ListenerOperation<TListener> operation)173     private void foreachUnsafe(ListenerOperation<TListener> operation) {
174         for (LinkedListener linkedListener : mListenerMap.values()) {
175             post(linkedListener.getUnderlyingListener(), operation);
176         }
177     }
178 
post(TListener listener, ListenerOperation<TListener> operation)179     private void post(TListener listener, ListenerOperation<TListener> operation) {
180         if (operation != null) {
181             mHandler.post(new HandlerRunnable(listener, operation));
182         }
183     }
184 
tryRegister()185     private void tryRegister() {
186         mHandler.post(new Runnable() {
187             int registrationState = RESULT_INTERNAL_ERROR;
188             @Override
189             public void run() {
190                 if (!mIsRegistered) {
191                     registrationState = registerWithService();
192                     mIsRegistered = registrationState == RESULT_SUCCESS;
193                 }
194                 if (!mIsRegistered) {
195                     // post back a failure
196                     mHandler.post(new Runnable() {
197                         @Override
198                         public void run() {
199                             synchronized (mListenerMap) {
200                                 ListenerOperation<TListener> operation = getHandlerOperation(registrationState);
201                                 foreachUnsafe(operation);
202                             }
203                         }
204                     });
205                 }
206             }
207         });
208     }
209 
tryUnregister()210     private void tryUnregister() {
211         mHandler.post(new Runnable() {
212             @Override
213             public void run() {
214                 if (!mIsRegistered) {
215                     return;
216                 }
217                 unregisterFromService();
218                 mIsRegistered = false;
219             }
220         });
221     }
222 
calculateCurrentResultUnsafe()223     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 
241     private class LinkedListener implements IBinder.DeathRecipient {
242         private final TListener mListener;
243 
LinkedListener(@onNull TListener listener)244         public LinkedListener(@NonNull TListener listener) {
245             mListener = listener;
246         }
247 
248         @NonNull
getUnderlyingListener()249         public TListener getUnderlyingListener() {
250             return mListener;
251         }
252 
253         @Override
binderDied()254         public void binderDied() {
255             Log.d(mTag, "Remote Listener died: " + mListener);
256             removeListener(mListener);
257         }
258     }
259 
260     private class HandlerRunnable implements Runnable {
261         private final TListener mListener;
262         private final ListenerOperation<TListener> mOperation;
263 
HandlerRunnable(TListener listener, ListenerOperation<TListener> operation)264         public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) {
265             mListener = listener;
266             mOperation = operation;
267         }
268 
269         @Override
run()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     }
278 }
279