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.lang.Runnable; 29 import java.util.HashMap; 30 import java.util.Map; 31 32 /** 33 * A helper class, that handles operations in remote listeners, and tracks for remote process death. 34 */ 35 abstract class RemoteListenerHelper<TListener extends IInterface> { 36 37 protected static final int RESULT_SUCCESS = 0; 38 protected static final int RESULT_NOT_AVAILABLE = 1; 39 protected static final int RESULT_NOT_SUPPORTED = 2; 40 protected static final int RESULT_GPS_LOCATION_DISABLED = 3; 41 protected static final int RESULT_INTERNAL_ERROR = 4; 42 protected static final int RESULT_UNKNOWN = 5; 43 44 private final Handler mHandler; 45 private final String mTag; 46 47 private final Map<IBinder, LinkedListener> mListenerMap = new HashMap<>(); 48 49 private boolean mIsRegistered; // must access only on handler thread 50 private boolean mHasIsSupported; 51 private boolean mIsSupported; 52 53 private int mLastReportedResult = RESULT_UNKNOWN; 54 55 protected RemoteListenerHelper(Handler handler, String name) { 56 Preconditions.checkNotNull(name); 57 mHandler = handler; 58 mTag = name; 59 } 60 61 public boolean addListener(@NonNull TListener listener) { 62 Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener."); 63 IBinder binder = listener.asBinder(); 64 LinkedListener deathListener = new LinkedListener(listener); 65 synchronized (mListenerMap) { 66 if (mListenerMap.containsKey(binder)) { 67 // listener already added 68 return true; 69 } 70 try { 71 binder.linkToDeath(deathListener, 0 /* flags */); 72 } catch (RemoteException e) { 73 // if the remote process registering the listener is already death, just swallow the 74 // exception and return 75 Log.v(mTag, "Remote listener already died.", e); 76 return false; 77 } 78 mListenerMap.put(binder, deathListener); 79 80 // update statuses we already know about, starting from the ones that will never change 81 int result; 82 if (!isAvailableInPlatform()) { 83 result = RESULT_NOT_AVAILABLE; 84 } else if (mHasIsSupported && !mIsSupported) { 85 result = RESULT_NOT_SUPPORTED; 86 } else if (!isGpsEnabled()) { 87 // only attempt to register if GPS is enabled, otherwise we will register once GPS 88 // becomes available 89 result = RESULT_GPS_LOCATION_DISABLED; 90 } else if (mHasIsSupported && mIsSupported) { 91 tryRegister(); 92 // initially presume success, possible internal error could follow asynchornously 93 result = RESULT_SUCCESS; 94 } else { 95 // at this point if the supported flag is not set, the notification will be sent 96 // asynchronously in the future 97 return true; 98 } 99 post(listener, getHandlerOperation(result)); 100 } 101 return true; 102 } 103 104 public void removeListener(@NonNull TListener listener) { 105 Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener."); 106 IBinder binder = listener.asBinder(); 107 LinkedListener linkedListener; 108 synchronized (mListenerMap) { 109 linkedListener = mListenerMap.remove(binder); 110 if (mListenerMap.isEmpty()) { 111 tryUnregister(); 112 } 113 } 114 if (linkedListener != null) { 115 binder.unlinkToDeath(linkedListener, 0 /* flags */); 116 } 117 } 118 119 protected abstract boolean isAvailableInPlatform(); 120 protected abstract boolean isGpsEnabled(); 121 protected abstract boolean registerWithService(); // must access only on handler thread 122 protected abstract void unregisterFromService(); // must access only on handler thread 123 protected abstract ListenerOperation<TListener> getHandlerOperation(int result); 124 125 protected interface ListenerOperation<TListener extends IInterface> { 126 void execute(TListener listener) throws RemoteException; 127 } 128 129 protected void foreach(ListenerOperation<TListener> operation) { 130 synchronized (mListenerMap) { 131 foreachUnsafe(operation); 132 } 133 } 134 135 protected void setSupported(boolean value) { 136 synchronized (mListenerMap) { 137 mHasIsSupported = true; 138 mIsSupported = value; 139 } 140 } 141 142 protected void tryUpdateRegistrationWithService() { 143 synchronized (mListenerMap) { 144 if (!isGpsEnabled()) { 145 tryUnregister(); 146 return; 147 } 148 if (mListenerMap.isEmpty()) { 149 return; 150 } 151 tryRegister(); 152 } 153 } 154 155 protected void updateResult() { 156 synchronized (mListenerMap) { 157 int newResult = calculateCurrentResultUnsafe(); 158 if (mLastReportedResult == newResult) { 159 return; 160 } 161 foreachUnsafe(getHandlerOperation(newResult)); 162 mLastReportedResult = newResult; 163 } 164 } 165 166 private void foreachUnsafe(ListenerOperation<TListener> operation) { 167 for (LinkedListener linkedListener : mListenerMap.values()) { 168 post(linkedListener.getUnderlyingListener(), operation); 169 } 170 } 171 172 private void post(TListener listener, ListenerOperation<TListener> operation) { 173 if (operation != null) { 174 mHandler.post(new HandlerRunnable(listener, operation)); 175 } 176 } 177 178 private void tryRegister() { 179 mHandler.post(new Runnable() { 180 @Override 181 public void run() { 182 if (!mIsRegistered) { 183 mIsRegistered = registerWithService(); 184 } 185 if (!mIsRegistered) { 186 // post back a failure 187 mHandler.post(new Runnable() { 188 @Override 189 public void run() { 190 synchronized (mListenerMap) { 191 ListenerOperation<TListener> operation = getHandlerOperation(RESULT_INTERNAL_ERROR); 192 foreachUnsafe(operation); 193 } 194 } 195 }); 196 } 197 } 198 }); 199 } 200 201 private void tryUnregister() { 202 mHandler.post(new Runnable() { 203 @Override 204 public void run() { 205 if (!mIsRegistered) { 206 return; 207 } 208 unregisterFromService(); 209 mIsRegistered = false; 210 } 211 }); 212 } 213 214 private int calculateCurrentResultUnsafe() { 215 // update statuses we already know about, starting from the ones that will never change 216 if (!isAvailableInPlatform()) { 217 return RESULT_NOT_AVAILABLE; 218 } 219 if (!mHasIsSupported || mListenerMap.isEmpty()) { 220 // we'll update once we have a supported status available 221 return RESULT_UNKNOWN; 222 } 223 if (!mIsSupported) { 224 return RESULT_NOT_SUPPORTED; 225 } 226 if (!isGpsEnabled()) { 227 return RESULT_GPS_LOCATION_DISABLED; 228 } 229 return RESULT_SUCCESS; 230 } 231 232 private class LinkedListener implements IBinder.DeathRecipient { 233 private final TListener mListener; 234 235 public LinkedListener(@NonNull TListener listener) { 236 mListener = listener; 237 } 238 239 @NonNull 240 public TListener getUnderlyingListener() { 241 return mListener; 242 } 243 244 @Override 245 public void binderDied() { 246 Log.d(mTag, "Remote Listener died: " + mListener); 247 removeListener(mListener); 248 } 249 } 250 251 private class HandlerRunnable implements Runnable { 252 private final TListener mListener; 253 private final ListenerOperation<TListener> mOperation; 254 255 public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) { 256 mListener = listener; 257 mOperation = operation; 258 } 259 260 @Override 261 public void run() { 262 try { 263 mOperation.execute(mListener); 264 } catch (RemoteException e) { 265 Log.v(mTag, "Error in monitored listener.", e); 266 } 267 } 268 } 269 } 270