Home | History | Annotate | Download | only in lowpan
      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 {
     44         public void onInterfaceAdded(LowpanInterface lowpanInterface) {}
     45 
     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 
     69     public static LowpanManager from(Context context) {
     70         return (LowpanManager) context.getSystemService(Context.LOWPAN_SERVICE);
     71     }
     72 
     73     /** @hide */
     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 
     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      */
    104     public LowpanManager(Context context, ILowpanManager service, Looper looper) {
    105         mContext = context;
    106         mService = service;
    107         mLooper = looper;
    108     }
    109 
    110     /** @hide */
    111     @Nullable
    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
    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
    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
    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
    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      */
    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 */
    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      */
    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