1 /* 2 * Copyright (C) 2017 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 android.net.lowpan; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.os.Handler; 23 import android.os.IBinder; 24 import android.os.Looper; 25 import android.os.RemoteException; 26 import android.os.ServiceManager; 27 import java.lang.ref.WeakReference; 28 import java.util.HashMap; 29 import java.util.Map; 30 import java.util.WeakHashMap; 31 32 /** 33 * Manager object for looking up LoWPAN interfaces. 34 * 35 * @hide 36 */ 37 // @SystemApi 38 public class LowpanManager { 39 private static final String TAG = LowpanManager.class.getSimpleName(); 40 41 /** @hide */ 42 // @SystemApi 43 public abstract static class Callback { onInterfaceAdded(LowpanInterface lowpanInterface)44 public void onInterfaceAdded(LowpanInterface lowpanInterface) {} 45 onInterfaceRemoved(LowpanInterface lowpanInterface)46 public void onInterfaceRemoved(LowpanInterface lowpanInterface) {} 47 } 48 49 private final Map<Integer, ILowpanManagerListener> mListenerMap = new HashMap<>(); 50 private final Map<String, LowpanInterface> mInterfaceCache = new HashMap<>(); 51 52 /* This is a WeakHashMap because we don't want to hold onto 53 * a strong reference to ILowpanInterface, so that it can be 54 * garbage collected if it isn't being used anymore. Since 55 * the value class holds onto this specific ILowpanInterface, 56 * we also need to have a weak reference to the value. 57 * This design pattern allows us to skip removal of items 58 * from this Map without leaking memory. 59 */ 60 private final Map<IBinder, WeakReference<LowpanInterface>> mBinderCache = 61 new WeakHashMap<>(); 62 63 private final ILowpanManager mService; 64 private final Context mContext; 65 private final Looper mLooper; 66 67 // Static Methods 68 from(Context context)69 public static LowpanManager from(Context context) { 70 return (LowpanManager) context.getSystemService(Context.LOWPAN_SERVICE); 71 } 72 73 /** @hide */ getManager()74 public static LowpanManager getManager() { 75 IBinder binder = ServiceManager.getService(Context.LOWPAN_SERVICE); 76 77 if (binder != null) { 78 ILowpanManager service = ILowpanManager.Stub.asInterface(binder); 79 return new LowpanManager(service); 80 } 81 82 return null; 83 } 84 85 // Constructors 86 LowpanManager(ILowpanManager service)87 LowpanManager(ILowpanManager service) { 88 mService = service; 89 mContext = null; 90 mLooper = null; 91 } 92 93 /** 94 * Create a new LowpanManager instance. Applications will almost always want to use {@link 95 * android.content.Context#getSystemService Context.getSystemService()} to retrieve the standard 96 * {@link android.content.Context#LOWPAN_SERVICE Context.LOWPAN_SERVICE}. 97 * 98 * @param context the application context 99 * @param service the Binder interface 100 * @param looper the default Looper to run callbacks on 101 * @hide - hide this because it takes in a parameter of type ILowpanManager, which is a system 102 * private class. 103 */ LowpanManager(Context context, ILowpanManager service, Looper looper)104 public LowpanManager(Context context, ILowpanManager service, Looper looper) { 105 mContext = context; 106 mService = service; 107 mLooper = looper; 108 } 109 110 /** @hide */ 111 @Nullable getInterfaceNoCreate(@onNull ILowpanInterface ifaceService)112 public LowpanInterface getInterfaceNoCreate(@NonNull ILowpanInterface ifaceService) { 113 LowpanInterface iface = null; 114 115 synchronized (mBinderCache) { 116 if (mBinderCache.containsKey(ifaceService.asBinder())) { 117 iface = mBinderCache.get(ifaceService.asBinder()).get(); 118 } 119 } 120 121 return iface; 122 } 123 124 /** @hide */ 125 @Nullable getInterface(@onNull ILowpanInterface ifaceService)126 public LowpanInterface getInterface(@NonNull ILowpanInterface ifaceService) { 127 LowpanInterface iface = null; 128 129 try { 130 synchronized (mBinderCache) { 131 if (mBinderCache.containsKey(ifaceService.asBinder())) { 132 iface = mBinderCache.get(ifaceService.asBinder()).get(); 133 } 134 135 if (iface == null) { 136 String ifaceName = ifaceService.getName(); 137 138 iface = new LowpanInterface(mContext, ifaceService, mLooper); 139 140 synchronized (mInterfaceCache) { 141 mInterfaceCache.put(iface.getName(), iface); 142 } 143 144 mBinderCache.put(ifaceService.asBinder(), new WeakReference(iface)); 145 146 /* Make sure we remove the object from the 147 * interface cache if the associated service 148 * dies. 149 */ 150 ifaceService 151 .asBinder() 152 .linkToDeath( 153 new IBinder.DeathRecipient() { 154 @Override 155 public void binderDied() { 156 synchronized (mInterfaceCache) { 157 LowpanInterface iface = 158 mInterfaceCache.get(ifaceName); 159 160 if ((iface != null) 161 && (iface.getService() == ifaceService)) { 162 mInterfaceCache.remove(ifaceName); 163 } 164 } 165 } 166 }, 167 0); 168 } 169 } 170 } catch (RemoteException x) { 171 throw x.rethrowAsRuntimeException(); 172 } 173 174 return iface; 175 } 176 177 /** 178 * Returns a reference to the requested LowpanInterface object. If the given interface doesn't 179 * exist, or it is not a LoWPAN interface, returns null. 180 */ 181 @Nullable getInterface(@onNull String name)182 public LowpanInterface getInterface(@NonNull String name) { 183 LowpanInterface iface = null; 184 185 try { 186 /* This synchronized block covers both branches of the enclosed 187 * if() statement in order to avoid a race condition. Two threads 188 * calling getInterface() with the same name would race to create 189 * the associated LowpanInterface object, creating two of them. 190 * Having the whole block be synchronized avoids that race. 191 */ 192 synchronized (mInterfaceCache) { 193 if (mInterfaceCache.containsKey(name)) { 194 iface = mInterfaceCache.get(name); 195 196 } else { 197 ILowpanInterface ifaceService = mService.getInterface(name); 198 199 if (ifaceService != null) { 200 iface = getInterface(ifaceService); 201 } 202 } 203 } 204 } catch (RemoteException x) { 205 throw x.rethrowFromSystemServer(); 206 } 207 208 return iface; 209 } 210 211 /** 212 * Returns a reference to the first registered LowpanInterface object. If there are no LoWPAN 213 * interfaces registered, returns null. 214 */ 215 @Nullable getInterface()216 public LowpanInterface getInterface() { 217 String[] ifaceList = getInterfaceList(); 218 if (ifaceList.length > 0) { 219 return getInterface(ifaceList[0]); 220 } 221 return null; 222 } 223 224 /** 225 * Returns a string array containing the names of LoWPAN interfaces. This list may contain fewer 226 * interfaces if the calling process does not have permissions to see individual interfaces. 227 */ 228 @NonNull getInterfaceList()229 public String[] getInterfaceList() { 230 try { 231 return mService.getInterfaceList(); 232 } catch (RemoteException x) { 233 throw x.rethrowFromSystemServer(); 234 } 235 } 236 237 /** 238 * Registers a callback object to receive notifications when LoWPAN interfaces are added or 239 * removed. 240 * 241 * @hide 242 */ registerCallback(@onNull Callback cb, @Nullable Handler handler)243 public void registerCallback(@NonNull Callback cb, @Nullable Handler handler) 244 throws LowpanException { 245 ILowpanManagerListener.Stub listenerBinder = 246 new ILowpanManagerListener.Stub() { 247 private Handler mHandler; 248 249 { 250 if (handler != null) { 251 mHandler = handler; 252 } else if (mLooper != null) { 253 mHandler = new Handler(mLooper); 254 } else { 255 mHandler = new Handler(); 256 } 257 } 258 259 @Override 260 public void onInterfaceAdded(ILowpanInterface ifaceService) { 261 Runnable runnable = 262 () -> { 263 LowpanInterface iface = getInterface(ifaceService); 264 265 if (iface != null) { 266 cb.onInterfaceAdded(iface); 267 } 268 }; 269 270 mHandler.post(runnable); 271 } 272 273 @Override 274 public void onInterfaceRemoved(ILowpanInterface ifaceService) { 275 Runnable runnable = 276 () -> { 277 LowpanInterface iface = getInterfaceNoCreate(ifaceService); 278 279 if (iface != null) { 280 cb.onInterfaceRemoved(iface); 281 } 282 }; 283 284 mHandler.post(runnable); 285 } 286 }; 287 try { 288 mService.addListener(listenerBinder); 289 } catch (RemoteException x) { 290 throw x.rethrowFromSystemServer(); 291 } 292 293 synchronized (mListenerMap) { 294 mListenerMap.put(Integer.valueOf(System.identityHashCode(cb)), listenerBinder); 295 } 296 } 297 298 /** @hide */ registerCallback(@onNull Callback cb)299 public void registerCallback(@NonNull Callback cb) throws LowpanException { 300 registerCallback(cb, null); 301 } 302 303 /** 304 * Unregisters a previously registered {@link LowpanManager.Callback} object. 305 * 306 * @hide 307 */ unregisterCallback(@onNull Callback cb)308 public void unregisterCallback(@NonNull Callback cb) { 309 Integer hashCode = Integer.valueOf(System.identityHashCode(cb)); 310 ILowpanManagerListener listenerBinder = null; 311 312 synchronized (mListenerMap) { 313 listenerBinder = mListenerMap.get(hashCode); 314 mListenerMap.remove(hashCode); 315 } 316 317 if (listenerBinder != null) { 318 try { 319 mService.removeListener(listenerBinder); 320 } catch (RemoteException x) { 321 throw x.rethrowFromSystemServer(); 322 } 323 } else { 324 throw new RuntimeException("Attempt to unregister an unknown callback"); 325 } 326 } 327 } 328