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