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.net.IpPrefix;
     23 import android.net.LinkAddress;
     24 import android.os.DeadObjectException;
     25 import android.os.Handler;
     26 import android.os.Looper;
     27 import android.os.RemoteException;
     28 import android.os.ServiceSpecificException;
     29 import android.util.Log;
     30 import java.util.HashMap;
     31 
     32 /**
     33  * Class for managing a specific Low-power Wireless Personal Area Network (LoWPAN) interface.
     34  *
     35  * @hide
     36  */
     37 // @SystemApi
     38 public class LowpanInterface {
     39     private static final String TAG = LowpanInterface.class.getSimpleName();
     40 
     41     /** Detached role. The interface is not currently attached to a network. */
     42     public static final String ROLE_DETACHED = ILowpanInterface.ROLE_DETACHED;
     43 
     44     /** End-device role. End devices do not route traffic for other nodes. */
     45     public static final String ROLE_END_DEVICE = ILowpanInterface.ROLE_END_DEVICE;
     46 
     47     /** Router role. Routers help route traffic around the mesh network. */
     48     public static final String ROLE_ROUTER = ILowpanInterface.ROLE_ROUTER;
     49 
     50     /**
     51      * Sleepy End-Device role.
     52      *
     53      * <p>End devices with this role are nominally asleep, waking up periodically to check in with
     54      * their parent to see if there are packets destined for them. Such devices are capable of
     55      * extraordinarilly low power consumption, but packet latency can be on the order of dozens of
     56      * seconds(depending on how the node is configured).
     57      */
     58     public static final String ROLE_SLEEPY_END_DEVICE = ILowpanInterface.ROLE_SLEEPY_END_DEVICE;
     59 
     60     /**
     61      * Sleepy-router role.
     62      *
     63      * <p>Routers with this role are nominally asleep, waking up periodically to check in with other
     64      * routers and their children.
     65      */
     66     public static final String ROLE_SLEEPY_ROUTER = ILowpanInterface.ROLE_SLEEPY_ROUTER;
     67 
     68     /** TODO: doc */
     69     public static final String ROLE_LEADER = ILowpanInterface.ROLE_LEADER;
     70 
     71     /** TODO: doc */
     72     public static final String ROLE_COORDINATOR = ILowpanInterface.ROLE_COORDINATOR;
     73 
     74     /**
     75      * Offline state.
     76      *
     77      * <p>This is the initial state of the LoWPAN interface when the underlying driver starts. In
     78      * this state the NCP is idle and not connected to any network.
     79      *
     80      * <p>This state can be explicitly entered by calling {@link #reset()}, {@link #leave()}, or
     81      * <code>setUp(false)</code>, with the later two only working if we were not previously in the
     82      * {@link #STATE_FAULT} state.
     83      *
     84      * @see #getState()
     85      * @see #STATE_FAULT
     86      */
     87     public static final String STATE_OFFLINE = ILowpanInterface.STATE_OFFLINE;
     88 
     89     /**
     90      * Commissioning state.
     91      *
     92      * <p>The interface enters this state after a call to {@link #startCommissioningSession()}. This
     93      * state may only be entered directly from the {@link #STATE_OFFLINE} state.
     94      *
     95      * @see #startCommissioningSession()
     96      * @see #getState()
     97      * @hide
     98      */
     99     public static final String STATE_COMMISSIONING = ILowpanInterface.STATE_COMMISSIONING;
    100 
    101     /**
    102      * Attaching state.
    103      *
    104      * <p>The interface enters this state when it starts the process of trying to find other nodes
    105      * so that it can attach to any pre-existing network fragment, or when it is in the process of
    106      * calculating the optimal values for unspecified parameters when forming a new network.
    107      *
    108      * <p>The interface may stay in this state for a prolonged period of time (or may spontaneously
    109      * enter this state from {@link #STATE_ATTACHED}) if the underlying network technology is
    110      * heirarchical (like ZigBeeIP) or if the device role is that of an "end-device" ({@link
    111      * #ROLE_END_DEVICE} or {@link #ROLE_SLEEPY_END_DEVICE}). This is because such roles cannot
    112      * create their own network fragments.
    113      *
    114      * @see #STATE_ATTACHED
    115      * @see #getState()
    116      */
    117     public static final String STATE_ATTACHING = ILowpanInterface.STATE_ATTACHING;
    118 
    119     /**
    120      * Attached state.
    121      *
    122      * <p>The interface enters this state from {@link #STATE_ATTACHING} once it is actively
    123      * participating on a network fragment.
    124      *
    125      * @see #STATE_ATTACHING
    126      * @see #getState()
    127      */
    128     public static final String STATE_ATTACHED = ILowpanInterface.STATE_ATTACHED;
    129 
    130     /**
    131      * Fault state.
    132      *
    133      * <p>The interface will enter this state when the driver has detected some sort of problem from
    134      * which it was not immediately able to recover.
    135      *
    136      * <p>This state can be entered spontaneously from any other state. Calling {@link #reset} will
    137      * cause the device to return to the {@link #STATE_OFFLINE} state.
    138      *
    139      * @see #getState
    140      * @see #STATE_OFFLINE
    141      */
    142     public static final String STATE_FAULT = ILowpanInterface.STATE_FAULT;
    143 
    144     /**
    145      * Network type for Thread 1.x networks.
    146      *
    147      * @see android.net.lowpan.LowpanIdentity#getType
    148      * @see #getLowpanIdentity
    149      * @hide
    150      */
    151     public static final String NETWORK_TYPE_THREAD_V1 = ILowpanInterface.NETWORK_TYPE_THREAD_V1;
    152 
    153     public static final String EMPTY_PARTITION_ID = "";
    154 
    155     /**
    156      * Callback base class for LowpanInterface
    157      *
    158      * @hide
    159      */
    160     // @SystemApi
    161     public abstract static class Callback {
    162         public void onConnectedChanged(boolean value) {}
    163 
    164         public void onEnabledChanged(boolean value) {}
    165 
    166         public void onUpChanged(boolean value) {}
    167 
    168         public void onRoleChanged(@NonNull String value) {}
    169 
    170         public void onStateChanged(@NonNull String state) {}
    171 
    172         public void onLowpanIdentityChanged(@NonNull LowpanIdentity value) {}
    173 
    174         public void onLinkNetworkAdded(IpPrefix prefix) {}
    175 
    176         public void onLinkNetworkRemoved(IpPrefix prefix) {}
    177 
    178         public void onLinkAddressAdded(LinkAddress address) {}
    179 
    180         public void onLinkAddressRemoved(LinkAddress address) {}
    181     }
    182 
    183     private final ILowpanInterface mBinder;
    184     private final Looper mLooper;
    185     private final HashMap<Integer, ILowpanInterfaceListener> mListenerMap = new HashMap<>();
    186 
    187     /**
    188      * Create a new LowpanInterface instance. Applications will almost always want to use {@link
    189      * LowpanManager#getInterface LowpanManager.getInterface()} instead of this.
    190      *
    191      * @param context the application context
    192      * @param service the Binder interface
    193      * @param looper the Binder interface
    194      * @hide
    195      */
    196     public LowpanInterface(Context context, ILowpanInterface service, Looper looper) {
    197         /* We aren't currently using the context, but if we need
    198          * it later on we can easily add it to the class.
    199          */
    200 
    201         mBinder = service;
    202         mLooper = looper;
    203     }
    204 
    205     /**
    206      * Returns the ILowpanInterface object associated with this interface.
    207      *
    208      * @hide
    209      */
    210     public ILowpanInterface getService() {
    211         return mBinder;
    212     }
    213 
    214     // Public Actions
    215 
    216     /**
    217      * Form a new network with the given network information optional credential. Unspecified fields
    218      * in the network information will be filled in with reasonable values. If the network
    219      * credential is unspecified, one will be generated automatically.
    220      *
    221      * <p>This method will block until either the network was successfully formed or an error
    222      * prevents the network form being formed.
    223      *
    224      * <p>Upon success, the interface will be up and attached to the newly formed network.
    225      *
    226      * @see #join(LowpanProvision)
    227      */
    228     public void form(@NonNull LowpanProvision provision) throws LowpanException {
    229         try {
    230             mBinder.form(provision);
    231 
    232         } catch (RemoteException x) {
    233             throw x.rethrowAsRuntimeException();
    234 
    235         } catch (ServiceSpecificException x) {
    236             throw LowpanException.rethrowFromServiceSpecificException(x);
    237         }
    238     }
    239 
    240     /**
    241      * Attempts to join a new network with the given network information. This method will block
    242      * until either the network was successfully joined or an error prevented the network from being
    243      * formed. Upon success, the interface will be up and attached to the newly joined network.
    244      *
    245      * <p>Note that joining is distinct from attaching: Joining requires at least one other peer
    246      * device to be present in order for the operation to complete successfully.
    247      */
    248     public void join(@NonNull LowpanProvision provision) throws LowpanException {
    249         try {
    250             mBinder.join(provision);
    251 
    252         } catch (RemoteException x) {
    253             throw x.rethrowAsRuntimeException();
    254 
    255         } catch (ServiceSpecificException x) {
    256             throw LowpanException.rethrowFromServiceSpecificException(x);
    257         }
    258     }
    259 
    260     /**
    261      * Attaches to the network described by identity and credential. This is similar to {@link
    262      * #join}, except that (assuming the identity and credential are valid) it will always succeed
    263      * and provision the interface, even if there are no peers nearby.
    264      *
    265      * <p>This method will block execution until the operation has completed.
    266      */
    267     public void attach(@NonNull LowpanProvision provision) throws LowpanException {
    268         try {
    269             mBinder.attach(provision);
    270 
    271         } catch (RemoteException x) {
    272             throw x.rethrowAsRuntimeException();
    273 
    274         } catch (ServiceSpecificException x) {
    275             throw LowpanException.rethrowFromServiceSpecificException(x);
    276         }
    277     }
    278 
    279     /**
    280      * Bring down the network interface and forget all non-volatile details about the current
    281      * network.
    282      *
    283      * <p>This method will block execution until the operation has completed.
    284      */
    285     public void leave() throws LowpanException {
    286         try {
    287             mBinder.leave();
    288 
    289         } catch (RemoteException x) {
    290             throw x.rethrowAsRuntimeException();
    291 
    292         } catch (ServiceSpecificException x) {
    293             throw LowpanException.rethrowFromServiceSpecificException(x);
    294         }
    295     }
    296 
    297     /**
    298      * Start a new commissioning session. Will fail if the interface is attached to a network or if
    299      * the interface is disabled.
    300      */
    301     public @NonNull LowpanCommissioningSession startCommissioningSession(
    302             @NonNull LowpanBeaconInfo beaconInfo) throws LowpanException {
    303         try {
    304             mBinder.startCommissioningSession(beaconInfo);
    305 
    306             return new LowpanCommissioningSession(mBinder, beaconInfo, mLooper);
    307 
    308         } catch (RemoteException x) {
    309             throw x.rethrowAsRuntimeException();
    310 
    311         } catch (ServiceSpecificException x) {
    312             throw LowpanException.rethrowFromServiceSpecificException(x);
    313         }
    314     }
    315 
    316     /**
    317      * Reset this network interface as if it has been power cycled. Will bring the network interface
    318      * down if it was previously up. Will not erase any non-volatile settings.
    319      *
    320      * <p>This method will block execution until the operation has completed.
    321      *
    322      * @hide
    323      */
    324     public void reset() throws LowpanException {
    325         try {
    326             mBinder.reset();
    327 
    328         } catch (RemoteException x) {
    329             throw x.rethrowAsRuntimeException();
    330 
    331         } catch (ServiceSpecificException x) {
    332             throw LowpanException.rethrowFromServiceSpecificException(x);
    333         }
    334     }
    335 
    336     // Public Getters and Setters
    337 
    338     /** Returns the name of this network interface. */
    339     @NonNull
    340     public String getName() {
    341         try {
    342             return mBinder.getName();
    343 
    344         } catch (DeadObjectException x) {
    345             return "";
    346 
    347         } catch (RemoteException x) {
    348             throw x.rethrowAsRuntimeException();
    349         }
    350     }
    351 
    352     /**
    353      * Indicates if the interface is enabled or disabled.
    354      *
    355      * @see #setEnabled
    356      * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED
    357      */
    358     public boolean isEnabled() {
    359         try {
    360             return mBinder.isEnabled();
    361 
    362         } catch (DeadObjectException x) {
    363             return false;
    364 
    365         } catch (RemoteException x) {
    366             throw x.rethrowAsRuntimeException();
    367         }
    368     }
    369 
    370     /**
    371      * Enables or disables the LoWPAN interface. When disabled, the interface is put into a
    372      * low-power state and all commands that require the NCP to be queried will fail with {@link
    373      * android.net.lowpan.LowpanException#LOWPAN_DISABLED}.
    374      *
    375      * @see #isEnabled
    376      * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED
    377      * @hide
    378      */
    379     public void setEnabled(boolean enabled) throws LowpanException {
    380         try {
    381             mBinder.setEnabled(enabled);
    382 
    383         } catch (RemoteException x) {
    384             throw x.rethrowAsRuntimeException();
    385 
    386         } catch (ServiceSpecificException x) {
    387             throw LowpanException.rethrowFromServiceSpecificException(x);
    388         }
    389     }
    390 
    391     /**
    392      * Indicates if the network interface is up or down.
    393      *
    394      * @hide
    395      */
    396     public boolean isUp() {
    397         try {
    398             return mBinder.isUp();
    399 
    400         } catch (DeadObjectException x) {
    401             return false;
    402 
    403         } catch (RemoteException x) {
    404             throw x.rethrowAsRuntimeException();
    405         }
    406     }
    407 
    408     /**
    409      * Indicates if there is at least one peer in range.
    410      *
    411      * @return <code>true</code> if we have at least one other peer in range, <code>false</code>
    412      *     otherwise.
    413      */
    414     public boolean isConnected() {
    415         try {
    416             return mBinder.isConnected();
    417 
    418         } catch (DeadObjectException x) {
    419             return false;
    420 
    421         } catch (RemoteException x) {
    422             throw x.rethrowAsRuntimeException();
    423         }
    424     }
    425 
    426     /**
    427      * Indicates if this interface is currently commissioned onto an existing network. If the
    428      * interface is commissioned, the interface may be brought up using setUp().
    429      */
    430     public boolean isCommissioned() {
    431         try {
    432             return mBinder.isCommissioned();
    433 
    434         } catch (DeadObjectException x) {
    435             return false;
    436 
    437         } catch (RemoteException x) {
    438             throw x.rethrowAsRuntimeException();
    439         }
    440     }
    441 
    442     /**
    443      * Get interface state
    444      *
    445      * <h3>State Diagram</h3>
    446      *
    447      * <img src="LowpanInterface-1.png" />
    448      *
    449      * @return The current state of the interface.
    450      * @see #STATE_OFFLINE
    451      * @see #STATE_COMMISSIONING
    452      * @see #STATE_ATTACHING
    453      * @see #STATE_ATTACHED
    454      * @see #STATE_FAULT
    455      */
    456     public String getState() {
    457         try {
    458             return mBinder.getState();
    459 
    460         } catch (DeadObjectException x) {
    461             return STATE_FAULT;
    462 
    463         } catch (RemoteException x) {
    464             throw x.rethrowAsRuntimeException();
    465         }
    466     }
    467 
    468     /** Get network partition/fragment identifier. */
    469     public String getPartitionId() {
    470         try {
    471             return mBinder.getPartitionId();
    472 
    473         } catch (DeadObjectException x) {
    474             return EMPTY_PARTITION_ID;
    475 
    476         } catch (RemoteException x) {
    477             throw x.rethrowAsRuntimeException();
    478         }
    479     }
    480 
    481     /** TODO: doc */
    482     public LowpanIdentity getLowpanIdentity() {
    483         try {
    484             return mBinder.getLowpanIdentity();
    485 
    486         } catch (DeadObjectException x) {
    487             return new LowpanIdentity();
    488 
    489         } catch (RemoteException x) {
    490             throw x.rethrowAsRuntimeException();
    491         }
    492     }
    493 
    494     /** TODO: doc */
    495     @NonNull
    496     public String getRole() {
    497         try {
    498             return mBinder.getRole();
    499 
    500         } catch (DeadObjectException x) {
    501             return ROLE_DETACHED;
    502 
    503         } catch (RemoteException x) {
    504             throw x.rethrowAsRuntimeException();
    505         }
    506     }
    507 
    508     /** TODO: doc */
    509     @Nullable
    510     public LowpanCredential getLowpanCredential() {
    511         try {
    512             return mBinder.getLowpanCredential();
    513 
    514         } catch (RemoteException x) {
    515             throw x.rethrowAsRuntimeException();
    516         }
    517     }
    518 
    519     public @NonNull String[] getSupportedNetworkTypes() throws LowpanException {
    520         try {
    521             return mBinder.getSupportedNetworkTypes();
    522 
    523         } catch (RemoteException x) {
    524             throw x.rethrowAsRuntimeException();
    525 
    526         } catch (ServiceSpecificException x) {
    527             throw LowpanException.rethrowFromServiceSpecificException(x);
    528         }
    529     }
    530 
    531     public @NonNull LowpanChannelInfo[] getSupportedChannels() throws LowpanException {
    532         try {
    533             return mBinder.getSupportedChannels();
    534 
    535         } catch (RemoteException x) {
    536             throw x.rethrowAsRuntimeException();
    537 
    538         } catch (ServiceSpecificException x) {
    539             throw LowpanException.rethrowFromServiceSpecificException(x);
    540         }
    541     }
    542 
    543     // Listener Support
    544 
    545     /**
    546      * Registers a subclass of {@link LowpanInterface.Callback} to receive events.
    547      *
    548      * @param cb Subclass of {@link LowpanInterface.Callback} which will receive events.
    549      * @param handler If not <code>null</code>, events will be dispatched via the given handler
    550      *     object. If <code>null</code>, the thread upon which events will be dispatched is
    551      *     unspecified.
    552      * @see #registerCallback(Callback)
    553      * @see #unregisterCallback(Callback)
    554      */
    555     public void registerCallback(@NonNull Callback cb, @Nullable Handler handler) {
    556         ILowpanInterfaceListener.Stub listenerBinder =
    557                 new ILowpanInterfaceListener.Stub() {
    558                     private Handler mHandler;
    559 
    560                     {
    561                         if (handler != null) {
    562                             mHandler = handler;
    563                         } else if (mLooper != null) {
    564                             mHandler = new Handler(mLooper);
    565                         } else {
    566                             mHandler = new Handler();
    567                         }
    568                     }
    569 
    570                     @Override
    571                     public void onEnabledChanged(boolean value) {
    572                         mHandler.post(() -> cb.onEnabledChanged(value));
    573                     }
    574 
    575                     @Override
    576                     public void onConnectedChanged(boolean value) {
    577                         mHandler.post(() -> cb.onConnectedChanged(value));
    578                     }
    579 
    580                     @Override
    581                     public void onUpChanged(boolean value) {
    582                         mHandler.post(() -> cb.onUpChanged(value));
    583                     }
    584 
    585                     @Override
    586                     public void onRoleChanged(String value) {
    587                         mHandler.post(() -> cb.onRoleChanged(value));
    588                     }
    589 
    590                     @Override
    591                     public void onStateChanged(String value) {
    592                         mHandler.post(() -> cb.onStateChanged(value));
    593                     }
    594 
    595                     @Override
    596                     public void onLowpanIdentityChanged(LowpanIdentity value) {
    597                         mHandler.post(() -> cb.onLowpanIdentityChanged(value));
    598                     }
    599 
    600                     @Override
    601                     public void onLinkNetworkAdded(IpPrefix value) {
    602                         mHandler.post(() -> cb.onLinkNetworkAdded(value));
    603                     }
    604 
    605                     @Override
    606                     public void onLinkNetworkRemoved(IpPrefix value) {
    607                         mHandler.post(() -> cb.onLinkNetworkRemoved(value));
    608                     }
    609 
    610                     @Override
    611                     public void onLinkAddressAdded(String value) {
    612                         LinkAddress la;
    613                         try {
    614                             la = new LinkAddress(value);
    615                         } catch (IllegalArgumentException x) {
    616                             Log.e(
    617                                     TAG,
    618                                     "onLinkAddressAdded: Bad LinkAddress \"" + value + "\", " + x);
    619                             return;
    620                         }
    621                         mHandler.post(() -> cb.onLinkAddressAdded(la));
    622                     }
    623 
    624                     @Override
    625                     public void onLinkAddressRemoved(String value) {
    626                         LinkAddress la;
    627                         try {
    628                             la = new LinkAddress(value);
    629                         } catch (IllegalArgumentException x) {
    630                             Log.e(
    631                                     TAG,
    632                                     "onLinkAddressRemoved: Bad LinkAddress \""
    633                                             + value
    634                                             + "\", "
    635                                             + x);
    636                             return;
    637                         }
    638                         mHandler.post(() -> cb.onLinkAddressRemoved(la));
    639                     }
    640 
    641                     @Override
    642                     public void onReceiveFromCommissioner(byte[] packet) {
    643                         // This is only used by the LowpanCommissioningSession.
    644                     }
    645                 };
    646         try {
    647             mBinder.addListener(listenerBinder);
    648         } catch (RemoteException x) {
    649             throw x.rethrowAsRuntimeException();
    650         }
    651 
    652         synchronized (mListenerMap) {
    653             mListenerMap.put(System.identityHashCode(cb), listenerBinder);
    654         }
    655     }
    656 
    657     /**
    658      * Registers a subclass of {@link LowpanInterface.Callback} to receive events.
    659      *
    660      * <p>The thread upon which events will be dispatched is unspecified.
    661      *
    662      * @param cb Subclass of {@link LowpanInterface.Callback} which will receive events.
    663      * @see #registerCallback(Callback, Handler)
    664      * @see #unregisterCallback(Callback)
    665      */
    666     public void registerCallback(Callback cb) {
    667         registerCallback(cb, null);
    668     }
    669 
    670     /**
    671      * Unregisters a previously registered callback class.
    672      *
    673      * @param cb Subclass of {@link LowpanInterface.Callback} which was previously registered to
    674      *     receive events.
    675      * @see #registerCallback(Callback, Handler)
    676      * @see #registerCallback(Callback)
    677      */
    678     public void unregisterCallback(Callback cb) {
    679         int hashCode = System.identityHashCode(cb);
    680         synchronized (mListenerMap) {
    681             ILowpanInterfaceListener listenerBinder = mListenerMap.get(hashCode);
    682 
    683             if (listenerBinder != null) {
    684                 mListenerMap.remove(hashCode);
    685 
    686                 try {
    687                     mBinder.removeListener(listenerBinder);
    688                 } catch (DeadObjectException x) {
    689                     // We ignore a dead object exception because that
    690                     // pretty clearly means our callback isn't registered.
    691                 } catch (RemoteException x) {
    692                     throw x.rethrowAsRuntimeException();
    693                 }
    694             }
    695         }
    696     }
    697 
    698     // Active and Passive Scanning
    699 
    700     /**
    701      * Creates a new {@link android.net.lowpan.LowpanScanner} object for this interface.
    702      *
    703      * <p>This method allocates a new unique object for each call.
    704      *
    705      * @see android.net.lowpan.LowpanScanner
    706      */
    707     public @NonNull LowpanScanner createScanner() {
    708         return new LowpanScanner(mBinder);
    709     }
    710 
    711     // Route Management
    712 
    713     /**
    714      * Makes a copy of the internal list of LinkAddresses.
    715      *
    716      * @hide
    717      */
    718     public LinkAddress[] getLinkAddresses() throws LowpanException {
    719         try {
    720             String[] linkAddressStrings = mBinder.getLinkAddresses();
    721             LinkAddress[] ret = new LinkAddress[linkAddressStrings.length];
    722             int i = 0;
    723             for (String str : linkAddressStrings) {
    724                 ret[i++] = new LinkAddress(str);
    725             }
    726             return ret;
    727 
    728         } catch (RemoteException x) {
    729             throw x.rethrowAsRuntimeException();
    730 
    731         } catch (ServiceSpecificException x) {
    732             throw LowpanException.rethrowFromServiceSpecificException(x);
    733         }
    734     }
    735 
    736     /**
    737      * Makes a copy of the internal list of networks reachable on via this link.
    738      *
    739      * @hide
    740      */
    741     public IpPrefix[] getLinkNetworks() throws LowpanException {
    742         try {
    743             return mBinder.getLinkNetworks();
    744 
    745         } catch (RemoteException x) {
    746             throw x.rethrowAsRuntimeException();
    747 
    748         } catch (ServiceSpecificException x) {
    749             throw LowpanException.rethrowFromServiceSpecificException(x);
    750         }
    751     }
    752 
    753     /**
    754      * Advertise the given IP prefix as an on-mesh prefix.
    755      *
    756      * @hide
    757      */
    758     public void addOnMeshPrefix(IpPrefix prefix, int flags) throws LowpanException {
    759         try {
    760             mBinder.addOnMeshPrefix(prefix, flags);
    761 
    762         } catch (RemoteException x) {
    763             throw x.rethrowAsRuntimeException();
    764 
    765         } catch (ServiceSpecificException x) {
    766             throw LowpanException.rethrowFromServiceSpecificException(x);
    767         }
    768     }
    769 
    770     /**
    771      * Remove an IP prefix previously advertised by this device from the list of advertised on-mesh
    772      * prefixes.
    773      *
    774      * @hide
    775      */
    776     public void removeOnMeshPrefix(IpPrefix prefix) {
    777         try {
    778             mBinder.removeOnMeshPrefix(prefix);
    779 
    780         } catch (RemoteException x) {
    781             throw x.rethrowAsRuntimeException();
    782 
    783         } catch (ServiceSpecificException x) {
    784             // Catch and ignore all service exceptions
    785             Log.e(TAG, x.toString());
    786         }
    787     }
    788 
    789     /**
    790      * Advertise this device to other devices on the mesh network as having a specific route to the
    791      * given network. This device will then receive forwarded traffic for that network.
    792      *
    793      * @hide
    794      */
    795     public void addExternalRoute(IpPrefix prefix, int flags) throws LowpanException {
    796         try {
    797             mBinder.addExternalRoute(prefix, flags);
    798 
    799         } catch (RemoteException x) {
    800             throw x.rethrowAsRuntimeException();
    801 
    802         } catch (ServiceSpecificException x) {
    803             throw LowpanException.rethrowFromServiceSpecificException(x);
    804         }
    805     }
    806 
    807     /**
    808      * Revoke a previously advertised specific route to the given network.
    809      *
    810      * @hide
    811      */
    812     public void removeExternalRoute(IpPrefix prefix) {
    813         try {
    814             mBinder.removeExternalRoute(prefix);
    815 
    816         } catch (RemoteException x) {
    817             throw x.rethrowAsRuntimeException();
    818 
    819         } catch (ServiceSpecificException x) {
    820             // Catch and ignore all service exceptions
    821             Log.e(TAG, x.toString());
    822         }
    823     }
    824 }
    825