• 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 com.android.internal.util.Preconditions;
20 
21 import android.annotation.NonNull;
22 import android.os.Handler;
23 import android.os.IBinder;
24 import android.os.IInterface;
25 import android.os.RemoteException;
26 import android.util.Log;
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 
43     private final Handler mHandler;
44     private final String mTag;
45 
46     private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>();
47 
48     private boolean mIsRegistered;
49     private boolean mHasIsSupported;
50     private boolean mIsSupported;
51 
52     private int mLastReportedResult = RESULT_UNKNOWN;
53 
RemoteListenerHelper(Handler handler, String name)54     protected RemoteListenerHelper(Handler handler, String name) {
55         Preconditions.checkNotNull(name);
56         mHandler = handler;
57         mTag = name;
58     }
59 
addListener(@onNull TListener listener)60     public boolean addListener(@NonNull TListener listener) {
61         Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
62         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             }
69             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
73                 // exception and return
74                 Log.v(mTag, "Remote listener already died.", e);
75                 return false;
76             }
77             mListenerMap.put(binder, deathListener);
78 
79             // 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         }
100         return true;
101     }
102 
removeListener(@onNull TListener listener)103     public void removeListener(@NonNull TListener listener) {
104         Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
105         IBinder binder = listener.asBinder();
106         LinkedListener linkedListener;
107         synchronized (mListenerMap) {
108             linkedListener = mListenerMap.remove(binder);
109             if (mListenerMap.isEmpty()) {
110                 tryUnregister();
111             }
112         }
113         if (linkedListener != null) {
114             binder.unlinkToDeath(linkedListener, 0 /* flags */);
115         }
116     }
117 
isAvailableInPlatform()118     protected abstract boolean isAvailableInPlatform();
isGpsEnabled()119     protected abstract boolean isGpsEnabled();
registerWithService()120     protected abstract boolean registerWithService();
unregisterFromService()121     protected abstract void unregisterFromService();
getHandlerOperation(int result)122     protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
123 
124     protected interface ListenerOperation<TListener extends IInterface> {
execute(TListener listener)125         void execute(TListener listener) throws RemoteException;
126     }
127 
foreach(ListenerOperation<TListener> operation)128     protected void foreach(ListenerOperation<TListener> operation) {
129         synchronized (mListenerMap) {
130             foreachUnsafe(operation);
131         }
132     }
133 
setSupported(boolean value)134     protected void setSupported(boolean value) {
135         synchronized (mListenerMap) {
136             mHasIsSupported = true;
137             mIsSupported = value;
138         }
139     }
140 
tryUpdateRegistrationWithService()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 
updateResult()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;
168         }
169     }
170 
foreachUnsafe(ListenerOperation<TListener> operation)171     private void foreachUnsafe(ListenerOperation<TListener> operation) {
172         for (LinkedListener linkedListener : mListenerMap.values()) {
173             post(linkedListener.getUnderlyingListener(), operation);
174         }
175     }
176 
post(TListener listener, ListenerOperation<TListener> operation)177     private void post(TListener listener, ListenerOperation<TListener> operation) {
178         if (operation != null) {
179             mHandler.post(new HandlerRunnable(listener, operation));
180         }
181     }
182 
tryRegister()183     private boolean tryRegister() {
184         if (!mIsRegistered) {
185             mIsRegistered = registerWithService();
186         }
187         return mIsRegistered;
188     }
189 
tryUnregister()190     private void tryUnregister() {
191         if (!mIsRegistered) {
192             return;
193         }
194         unregisterFromService();
195         mIsRegistered = false;
196     }
197 
calculateCurrentResultUnsafe()198     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 
216     private class LinkedListener implements IBinder.DeathRecipient {
217         private final TListener mListener;
218 
LinkedListener(@onNull TListener listener)219         public LinkedListener(@NonNull TListener listener) {
220             mListener = listener;
221         }
222 
223         @NonNull
getUnderlyingListener()224         public TListener getUnderlyingListener() {
225             return mListener;
226         }
227 
228         @Override
binderDied()229         public void binderDied() {
230             Log.d(mTag, "Remote Listener died: " + mListener);
231             removeListener(mListener);
232         }
233     }
234 
235     private class HandlerRunnable implements Runnable {
236         private final TListener mListener;
237         private final ListenerOperation<TListener> mOperation;
238 
HandlerRunnable(TListener listener, ListenerOperation<TListener> operation)239         public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) {
240             mListener = listener;
241             mOperation = operation;
242         }
243 
244         @Override
run()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     }
253 }
254