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 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 63 public boolean isRegistered() { 64 return mIsRegistered; 65 } 66 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 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 125 protected abstract boolean isAvailableInPlatform(); 126 protected abstract boolean isGpsEnabled(); 127 // must access only on handler thread 128 protected abstract int registerWithService(); 129 protected abstract void unregisterFromService(); // must access only on handler thread 130 protected abstract ListenerOperation<TListener> getHandlerOperation(int result); 131 132 protected interface ListenerOperation<TListener extends IInterface> { 133 void execute(TListener listener) throws RemoteException; 134 } 135 136 protected void foreach(ListenerOperation<TListener> operation) { 137 synchronized (mListenerMap) { 138 foreachUnsafe(operation); 139 } 140 } 141 142 protected void setSupported(boolean value) { 143 synchronized (mListenerMap) { 144 mHasIsSupported = true; 145 mIsSupported = value; 146 } 147 } 148 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 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 173 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 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 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 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 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() { 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 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 } 278 } 279