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 54 protected RemoteListenerHelper(Handler handler, String name) { 55 Preconditions.checkNotNull(name); 56 mHandler = handler; 57 mTag = name; 58 } 59 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 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 118 protected abstract boolean isAvailableInPlatform(); 119 protected abstract boolean isGpsEnabled(); 120 protected abstract boolean registerWithService(); 121 protected abstract void unregisterFromService(); 122 protected abstract ListenerOperation<TListener> getHandlerOperation(int result); 123 124 protected interface ListenerOperation<TListener extends IInterface> { 125 void execute(TListener listener) throws RemoteException; 126 } 127 128 protected void foreach(ListenerOperation<TListener> operation) { 129 synchronized (mListenerMap) { 130 foreachUnsafe(operation); 131 } 132 } 133 134 protected void setSupported(boolean value) { 135 synchronized (mListenerMap) { 136 mHasIsSupported = true; 137 mIsSupported = value; 138 } 139 } 140 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 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 171 private void foreachUnsafe(ListenerOperation<TListener> operation) { 172 for (LinkedListener linkedListener : mListenerMap.values()) { 173 post(linkedListener.getUnderlyingListener(), operation); 174 } 175 } 176 177 private void post(TListener listener, ListenerOperation<TListener> operation) { 178 if (operation != null) { 179 mHandler.post(new HandlerRunnable(listener, operation)); 180 } 181 } 182 183 private boolean tryRegister() { 184 if (!mIsRegistered) { 185 mIsRegistered = registerWithService(); 186 } 187 return mIsRegistered; 188 } 189 190 private void tryUnregister() { 191 if (!mIsRegistered) { 192 return; 193 } 194 unregisterFromService(); 195 mIsRegistered = false; 196 } 197 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 219 public LinkedListener(@NonNull TListener listener) { 220 mListener = listener; 221 } 222 223 @NonNull 224 public TListener getUnderlyingListener() { 225 return mListener; 226 } 227 228 @Override 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 239 public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) { 240 mListener = listener; 241 mOperation = operation; 242 } 243 244 @Override 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