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 17package com.android.server.location; 18 19import com.android.internal.util.Preconditions; 20 21import android.annotation.NonNull; 22import android.os.Handler; 23import android.os.IBinder; 24import android.os.IInterface; 25import android.os.RemoteException; 26import android.util.Log; 27 28import java.util.HashMap; 29 30/** 31 * A helper class, that handles operations in remote listeners, and tracks for remote process death. 32 */ 33abstract class RemoteListenerHelper<TListener extends IInterface> { 34 protected static final int RESULT_SUCCESS = 0; 35 protected static final int RESULT_NOT_AVAILABLE = 1; 36 protected static final int RESULT_NOT_SUPPORTED = 2; 37 protected static final int RESULT_GPS_LOCATION_DISABLED = 3; 38 protected static final int RESULT_INTERNAL_ERROR = 4; 39 40 private final Handler mHandler; 41 private final String mTag; 42 43 private final HashMap<IBinder, LinkedListener> mListenerMap = new HashMap<>(); 44 45 private boolean mIsRegistered; 46 private boolean mHasIsSupported; 47 private boolean mIsSupported; 48 49 protected RemoteListenerHelper(Handler handler, String name) { 50 Preconditions.checkNotNull(name); 51 mHandler = handler; 52 mTag = name; 53 } 54 55 public boolean addListener(@NonNull TListener listener) { 56 Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener."); 57 IBinder binder = listener.asBinder(); 58 LinkedListener deathListener = new LinkedListener(listener); 59 synchronized (mListenerMap) { 60 if (mListenerMap.containsKey(binder)) { 61 // listener already added 62 return true; 63 } 64 try { 65 binder.linkToDeath(deathListener, 0 /* flags */); 66 } catch (RemoteException e) { 67 // if the remote process registering the listener is already death, just swallow the 68 // exception and return 69 Log.v(mTag, "Remote listener already died.", e); 70 return false; 71 } 72 mListenerMap.put(binder, deathListener); 73 74 // update statuses we already know about, starting from the ones that will never change 75 int result; 76 if (!isAvailableInPlatform()) { 77 result = RESULT_NOT_AVAILABLE; 78 } else if (mHasIsSupported && !mIsSupported) { 79 result = RESULT_NOT_SUPPORTED; 80 } else if (!isGpsEnabled()) { 81 result = RESULT_GPS_LOCATION_DISABLED; 82 } else if (!tryRegister()) { 83 // only attempt to register if GPS is enabled, otherwise we will register once GPS 84 // becomes available 85 result = RESULT_INTERNAL_ERROR; 86 } else if (mHasIsSupported && mIsSupported) { 87 result = RESULT_SUCCESS; 88 } else { 89 // at this point if the supported flag is not set, the notification will be sent 90 // asynchronously in the future 91 return true; 92 } 93 post(listener, getHandlerOperation(result)); 94 } 95 return true; 96 } 97 98 public void removeListener(@NonNull TListener listener) { 99 Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener."); 100 IBinder binder = listener.asBinder(); 101 LinkedListener linkedListener; 102 synchronized (mListenerMap) { 103 linkedListener = mListenerMap.remove(binder); 104 if (mListenerMap.isEmpty()) { 105 tryUnregister(); 106 } 107 } 108 if (linkedListener != null) { 109 binder.unlinkToDeath(linkedListener, 0 /* flags */); 110 } 111 } 112 113 public void onGpsEnabledChanged(boolean enabled) { 114 // handle first the sub-class implementation, so any error in registration can take 115 // precedence 116 handleGpsEnabledChanged(enabled); 117 synchronized (mListenerMap) { 118 if (!enabled) { 119 tryUnregister(); 120 return; 121 } 122 if (mListenerMap.isEmpty()) { 123 return; 124 } 125 if (tryRegister()) { 126 // registration was successful, there is no need to update the state 127 return; 128 } 129 ListenerOperation<TListener> operation = getHandlerOperation(RESULT_INTERNAL_ERROR); 130 foreachUnsafe(operation); 131 } 132 } 133 134 protected abstract boolean isAvailableInPlatform(); 135 protected abstract boolean isGpsEnabled(); 136 protected abstract boolean registerWithService(); 137 protected abstract void unregisterFromService(); 138 protected abstract ListenerOperation<TListener> getHandlerOperation(int result); 139 protected abstract void handleGpsEnabledChanged(boolean enabled); 140 141 protected interface ListenerOperation<TListener extends IInterface> { 142 void execute(TListener listener) throws RemoteException; 143 } 144 145 protected void foreach(ListenerOperation<TListener> operation) { 146 synchronized (mListenerMap) { 147 foreachUnsafe(operation); 148 } 149 } 150 151 protected void setSupported(boolean value, ListenerOperation<TListener> notifier) { 152 synchronized (mListenerMap) { 153 mHasIsSupported = true; 154 mIsSupported = value; 155 foreachUnsafe(notifier); 156 } 157 } 158 159 private void foreachUnsafe(ListenerOperation<TListener> operation) { 160 for (LinkedListener linkedListener : mListenerMap.values()) { 161 post(linkedListener.getUnderlyingListener(), operation); 162 } 163 } 164 165 private void post(TListener listener, ListenerOperation<TListener> operation) { 166 if (operation != null) { 167 mHandler.post(new HandlerRunnable(listener, operation)); 168 } 169 } 170 171 private boolean tryRegister() { 172 if (!mIsRegistered) { 173 mIsRegistered = registerWithService(); 174 } 175 return mIsRegistered; 176 } 177 178 private void tryUnregister() { 179 if (!mIsRegistered) { 180 return; 181 } 182 unregisterFromService(); 183 mIsRegistered = false; 184 } 185 186 private class LinkedListener implements IBinder.DeathRecipient { 187 private final TListener mListener; 188 189 public LinkedListener(@NonNull TListener listener) { 190 mListener = listener; 191 } 192 193 @NonNull 194 public TListener getUnderlyingListener() { 195 return mListener; 196 } 197 198 @Override 199 public void binderDied() { 200 Log.d(mTag, "Remote Listener died: " + mListener); 201 removeListener(mListener); 202 } 203 } 204 205 private class HandlerRunnable implements Runnable { 206 private final TListener mListener; 207 private final ListenerOperation<TListener> mOperation; 208 209 public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) { 210 mListener = listener; 211 mOperation = operation; 212 } 213 214 @Override 215 public void run() { 216 try { 217 mOperation.execute(mListener); 218 } catch (RemoteException e) { 219 Log.v(mTag, "Error in monitored listener.", e); 220 } 221 } 222 } 223} 224