/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net.lowpan;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
import java.util.HashMap;

Class for managing a specific Low-power Wireless Personal Area Network (LoWPAN) interface.
@hide
/** * Class for managing a specific Low-power Wireless Personal Area Network (LoWPAN) interface. * * @hide */
// @SystemApi public class LowpanInterface { private static final String TAG = LowpanInterface.class.getSimpleName();
Detached role. The interface is not currently attached to a network.
/** Detached role. The interface is not currently attached to a network. */
public static final String ROLE_DETACHED = ILowpanInterface.ROLE_DETACHED;
End-device role. End devices do not route traffic for other nodes.
/** End-device role. End devices do not route traffic for other nodes. */
public static final String ROLE_END_DEVICE = ILowpanInterface.ROLE_END_DEVICE;
Router role. Routers help route traffic around the mesh network.
/** Router role. Routers help route traffic around the mesh network. */
public static final String ROLE_ROUTER = ILowpanInterface.ROLE_ROUTER;
Sleepy End-Device role.

End devices with this role are nominally asleep, waking up periodically to check in with their parent to see if there are packets destined for them. Such devices are capable of extraordinarilly low power consumption, but packet latency can be on the order of dozens of seconds(depending on how the node is configured).

/** * Sleepy End-Device role. * * <p>End devices with this role are nominally asleep, waking up periodically to check in with * their parent to see if there are packets destined for them. Such devices are capable of * extraordinarilly low power consumption, but packet latency can be on the order of dozens of * seconds(depending on how the node is configured). */
public static final String ROLE_SLEEPY_END_DEVICE = ILowpanInterface.ROLE_SLEEPY_END_DEVICE;
Sleepy-router role.

Routers with this role are nominally asleep, waking up periodically to check in with other routers and their children.

/** * Sleepy-router role. * * <p>Routers with this role are nominally asleep, waking up periodically to check in with other * routers and their children. */
public static final String ROLE_SLEEPY_ROUTER = ILowpanInterface.ROLE_SLEEPY_ROUTER;
TODO: doc
/** TODO: doc */
public static final String ROLE_LEADER = ILowpanInterface.ROLE_LEADER;
TODO: doc
/** TODO: doc */
public static final String ROLE_COORDINATOR = ILowpanInterface.ROLE_COORDINATOR;
Offline state.

This is the initial state of the LoWPAN interface when the underlying driver starts. In this state the NCP is idle and not connected to any network.

This state can be explicitly entered by calling reset(), leave(), or setUp(false), with the later two only working if we were not previously in the STATE_FAULT state.

See Also:
/** * Offline state. * * <p>This is the initial state of the LoWPAN interface when the underlying driver starts. In * this state the NCP is idle and not connected to any network. * * <p>This state can be explicitly entered by calling {@link #reset()}, {@link #leave()}, or * <code>setUp(false)</code>, with the later two only working if we were not previously in the * {@link #STATE_FAULT} state. * * @see #getState() * @see #STATE_FAULT */
public static final String STATE_OFFLINE = ILowpanInterface.STATE_OFFLINE;
Commissioning state.

The interface enters this state after a call to startCommissioningSession(). This state may only be entered directly from the STATE_OFFLINE state.

See Also:
@hide
/** * Commissioning state. * * <p>The interface enters this state after a call to {@link #startCommissioningSession()}. This * state may only be entered directly from the {@link #STATE_OFFLINE} state. * * @see #startCommissioningSession() * @see #getState() * @hide */
public static final String STATE_COMMISSIONING = ILowpanInterface.STATE_COMMISSIONING;
Attaching state.

The interface enters this state when it starts the process of trying to find other nodes so that it can attach to any pre-existing network fragment, or when it is in the process of calculating the optimal values for unspecified parameters when forming a new network.

The interface may stay in this state for a prolonged period of time (or may spontaneously enter this state from STATE_ATTACHED) if the underlying network technology is heirarchical (like ZigBeeIP) or if the device role is that of an "end-device" (ROLE_END_DEVICE or ROLE_SLEEPY_END_DEVICE). This is because such roles cannot create their own network fragments.

See Also:
/** * Attaching state. * * <p>The interface enters this state when it starts the process of trying to find other nodes * so that it can attach to any pre-existing network fragment, or when it is in the process of * calculating the optimal values for unspecified parameters when forming a new network. * * <p>The interface may stay in this state for a prolonged period of time (or may spontaneously * enter this state from {@link #STATE_ATTACHED}) if the underlying network technology is * heirarchical (like ZigBeeIP) or if the device role is that of an "end-device" ({@link * #ROLE_END_DEVICE} or {@link #ROLE_SLEEPY_END_DEVICE}). This is because such roles cannot * create their own network fragments. * * @see #STATE_ATTACHED * @see #getState() */
public static final String STATE_ATTACHING = ILowpanInterface.STATE_ATTACHING;
Attached state.

The interface enters this state from STATE_ATTACHING once it is actively participating on a network fragment.

See Also:
/** * Attached state. * * <p>The interface enters this state from {@link #STATE_ATTACHING} once it is actively * participating on a network fragment. * * @see #STATE_ATTACHING * @see #getState() */
public static final String STATE_ATTACHED = ILowpanInterface.STATE_ATTACHED;
Fault state.

The interface will enter this state when the driver has detected some sort of problem from which it was not immediately able to recover.

This state can be entered spontaneously from any other state. Calling reset will cause the device to return to the STATE_OFFLINE state.

See Also:
/** * Fault state. * * <p>The interface will enter this state when the driver has detected some sort of problem from * which it was not immediately able to recover. * * <p>This state can be entered spontaneously from any other state. Calling {@link #reset} will * cause the device to return to the {@link #STATE_OFFLINE} state. * * @see #getState * @see #STATE_OFFLINE */
public static final String STATE_FAULT = ILowpanInterface.STATE_FAULT;
Network type for Thread 1.x networks.
See Also:
@hide
/** * Network type for Thread 1.x networks. * * @see android.net.lowpan.LowpanIdentity#getType * @see #getLowpanIdentity * @hide */
public static final String NETWORK_TYPE_THREAD_V1 = ILowpanInterface.NETWORK_TYPE_THREAD_V1; public static final String EMPTY_PARTITION_ID = "";
Callback base class for LowpanInterface
@hide
/** * Callback base class for LowpanInterface * * @hide */
// @SystemApi public abstract static class Callback { public void onConnectedChanged(boolean value) {} public void onEnabledChanged(boolean value) {} public void onUpChanged(boolean value) {} public void onRoleChanged(@NonNull String value) {} public void onStateChanged(@NonNull String state) {} public void onLowpanIdentityChanged(@NonNull LowpanIdentity value) {} public void onLinkNetworkAdded(IpPrefix prefix) {} public void onLinkNetworkRemoved(IpPrefix prefix) {} public void onLinkAddressAdded(LinkAddress address) {} public void onLinkAddressRemoved(LinkAddress address) {} } private final ILowpanInterface mBinder; private final Looper mLooper; private final HashMap<Integer, ILowpanInterfaceListener> mListenerMap = new HashMap<>();
Create a new LowpanInterface instance. Applications will almost always want to use LowpanManager.getInterface() instead of this.
Params:
  • context – the application context
  • service – the Binder interface
  • looper – the Binder interface
@hide
/** * Create a new LowpanInterface instance. Applications will almost always want to use {@link * LowpanManager#getInterface LowpanManager.getInterface()} instead of this. * * @param context the application context * @param service the Binder interface * @param looper the Binder interface * @hide */
public LowpanInterface(Context context, ILowpanInterface service, Looper looper) { /* We aren't currently using the context, but if we need * it later on we can easily add it to the class. */ mBinder = service; mLooper = looper; }
Returns the ILowpanInterface object associated with this interface.
@hide
/** * Returns the ILowpanInterface object associated with this interface. * * @hide */
public ILowpanInterface getService() { return mBinder; } // Public Actions
Form a new network with the given network information optional credential. Unspecified fields in the network information will be filled in with reasonable values. If the network credential is unspecified, one will be generated automatically.

This method will block until either the network was successfully formed or an error prevents the network form being formed.

Upon success, the interface will be up and attached to the newly formed network.

See Also:
  • join(LowpanProvision)
/** * Form a new network with the given network information optional credential. Unspecified fields * in the network information will be filled in with reasonable values. If the network * credential is unspecified, one will be generated automatically. * * <p>This method will block until either the network was successfully formed or an error * prevents the network form being formed. * * <p>Upon success, the interface will be up and attached to the newly formed network. * * @see #join(LowpanProvision) */
public void form(@NonNull LowpanProvision provision) throws LowpanException { try { mBinder.form(provision); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { throw LowpanException.rethrowFromServiceSpecificException(x); } }
Attempts to join a new network with the given network information. This method will block until either the network was successfully joined or an error prevented the network from being formed. Upon success, the interface will be up and attached to the newly joined network.

Note that “joining” is distinct from “attaching”: Joining requires at least one other peer device to be present in order for the operation to complete successfully.

/** * Attempts to join a new network with the given network information. This method will block * until either the network was successfully joined or an error prevented the network from being * formed. Upon success, the interface will be up and attached to the newly joined network. * * <p>Note that “joining” is distinct from “attaching”: Joining requires at least one other peer * device to be present in order for the operation to complete successfully. */
public void join(@NonNull LowpanProvision provision) throws LowpanException { try { mBinder.join(provision); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { throw LowpanException.rethrowFromServiceSpecificException(x); } }
Attaches to the network described by identity and credential. This is similar to join, except that (assuming the identity and credential are valid) it will always succeed and provision the interface, even if there are no peers nearby.

This method will block execution until the operation has completed.

/** * Attaches to the network described by identity and credential. This is similar to {@link * #join}, except that (assuming the identity and credential are valid) it will always succeed * and provision the interface, even if there are no peers nearby. * * <p>This method will block execution until the operation has completed. */
public void attach(@NonNull LowpanProvision provision) throws LowpanException { try { mBinder.attach(provision); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { throw LowpanException.rethrowFromServiceSpecificException(x); } }
Bring down the network interface and forget all non-volatile details about the current network.

This method will block execution until the operation has completed.

/** * Bring down the network interface and forget all non-volatile details about the current * network. * * <p>This method will block execution until the operation has completed. */
public void leave() throws LowpanException { try { mBinder.leave(); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { throw LowpanException.rethrowFromServiceSpecificException(x); } }
Start a new commissioning session. Will fail if the interface is attached to a network or if the interface is disabled.
/** * Start a new commissioning session. Will fail if the interface is attached to a network or if * the interface is disabled. */
public @NonNull LowpanCommissioningSession startCommissioningSession( @NonNull LowpanBeaconInfo beaconInfo) throws LowpanException { try { mBinder.startCommissioningSession(beaconInfo); return new LowpanCommissioningSession(mBinder, beaconInfo, mLooper); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { throw LowpanException.rethrowFromServiceSpecificException(x); } }
Reset this network interface as if it has been power cycled. Will bring the network interface down if it was previously up. Will not erase any non-volatile settings.

This method will block execution until the operation has completed.

@hide
/** * Reset this network interface as if it has been power cycled. Will bring the network interface * down if it was previously up. Will not erase any non-volatile settings. * * <p>This method will block execution until the operation has completed. * * @hide */
public void reset() throws LowpanException { try { mBinder.reset(); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { throw LowpanException.rethrowFromServiceSpecificException(x); } } // Public Getters and Setters
Returns the name of this network interface.
/** Returns the name of this network interface. */
@NonNull public String getName() { try { return mBinder.getName(); } catch (DeadObjectException x) { return ""; } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } }
Indicates if the interface is enabled or disabled.
See Also:
  • setEnabled
  • LowpanException.LOWPAN_DISABLED
/** * Indicates if the interface is enabled or disabled. * * @see #setEnabled * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED */
public boolean isEnabled() { try { return mBinder.isEnabled(); } catch (DeadObjectException x) { return false; } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } }
Enables or disables the LoWPAN interface. When disabled, the interface is put into a low-power state and all commands that require the NCP to be queried will fail with LowpanException.LOWPAN_DISABLED.
See Also:
@hide
/** * Enables or disables the LoWPAN interface. When disabled, the interface is put into a * low-power state and all commands that require the NCP to be queried will fail with {@link * android.net.lowpan.LowpanException#LOWPAN_DISABLED}. * * @see #isEnabled * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED * @hide */
public void setEnabled(boolean enabled) throws LowpanException { try { mBinder.setEnabled(enabled); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { throw LowpanException.rethrowFromServiceSpecificException(x); } }
Indicates if the network interface is up or down.
@hide
/** * Indicates if the network interface is up or down. * * @hide */
public boolean isUp() { try { return mBinder.isUp(); } catch (DeadObjectException x) { return false; } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } }
Indicates if there is at least one peer in range.
Returns:true if we have at least one other peer in range, false otherwise.
/** * Indicates if there is at least one peer in range. * * @return <code>true</code> if we have at least one other peer in range, <code>false</code> * otherwise. */
public boolean isConnected() { try { return mBinder.isConnected(); } catch (DeadObjectException x) { return false; } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } }
Indicates if this interface is currently commissioned onto an existing network. If the interface is commissioned, the interface may be brought up using setUp().
/** * Indicates if this interface is currently commissioned onto an existing network. If the * interface is commissioned, the interface may be brought up using setUp(). */
public boolean isCommissioned() { try { return mBinder.isCommissioned(); } catch (DeadObjectException x) { return false; } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } }
Get interface state

State Diagram

See Also:
Returns:The current state of the interface.
/** * Get interface state * * <h3>State Diagram</h3> * * <img src="LowpanInterface-1.png" /> * * @return The current state of the interface. * @see #STATE_OFFLINE * @see #STATE_COMMISSIONING * @see #STATE_ATTACHING * @see #STATE_ATTACHED * @see #STATE_FAULT */
public String getState() { try { return mBinder.getState(); } catch (DeadObjectException x) { return STATE_FAULT; } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } }
Get network partition/fragment identifier.
/** Get network partition/fragment identifier. */
public String getPartitionId() { try { return mBinder.getPartitionId(); } catch (DeadObjectException x) { return EMPTY_PARTITION_ID; } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } }
TODO: doc
/** TODO: doc */
public LowpanIdentity getLowpanIdentity() { try { return mBinder.getLowpanIdentity(); } catch (DeadObjectException x) { return new LowpanIdentity(); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } }
TODO: doc
/** TODO: doc */
@NonNull public String getRole() { try { return mBinder.getRole(); } catch (DeadObjectException x) { return ROLE_DETACHED; } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } }
TODO: doc
/** TODO: doc */
@Nullable public LowpanCredential getLowpanCredential() { try { return mBinder.getLowpanCredential(); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } } public @NonNull String[] getSupportedNetworkTypes() throws LowpanException { try { return mBinder.getSupportedNetworkTypes(); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { throw LowpanException.rethrowFromServiceSpecificException(x); } } public @NonNull LowpanChannelInfo[] getSupportedChannels() throws LowpanException { try { return mBinder.getSupportedChannels(); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { throw LowpanException.rethrowFromServiceSpecificException(x); } } // Listener Support
Registers a subclass of Callback to receive events.
Params:
  • cb – Subclass of Callback which will receive events.
  • handler – If not null, events will be dispatched via the given handler object. If null, the thread upon which events will be dispatched is unspecified.
See Also:
/** * Registers a subclass of {@link LowpanInterface.Callback} to receive events. * * @param cb Subclass of {@link LowpanInterface.Callback} which will receive events. * @param handler If not <code>null</code>, events will be dispatched via the given handler * object. If <code>null</code>, the thread upon which events will be dispatched is * unspecified. * @see #registerCallback(Callback) * @see #unregisterCallback(Callback) */
public void registerCallback(@NonNull Callback cb, @Nullable Handler handler) { ILowpanInterfaceListener.Stub listenerBinder = new ILowpanInterfaceListener.Stub() { private Handler mHandler; { if (handler != null) { mHandler = handler; } else if (mLooper != null) { mHandler = new Handler(mLooper); } else { mHandler = new Handler(); } } @Override public void onEnabledChanged(boolean value) { mHandler.post(() -> cb.onEnabledChanged(value)); } @Override public void onConnectedChanged(boolean value) { mHandler.post(() -> cb.onConnectedChanged(value)); } @Override public void onUpChanged(boolean value) { mHandler.post(() -> cb.onUpChanged(value)); } @Override public void onRoleChanged(String value) { mHandler.post(() -> cb.onRoleChanged(value)); } @Override public void onStateChanged(String value) { mHandler.post(() -> cb.onStateChanged(value)); } @Override public void onLowpanIdentityChanged(LowpanIdentity value) { mHandler.post(() -> cb.onLowpanIdentityChanged(value)); } @Override public void onLinkNetworkAdded(IpPrefix value) { mHandler.post(() -> cb.onLinkNetworkAdded(value)); } @Override public void onLinkNetworkRemoved(IpPrefix value) { mHandler.post(() -> cb.onLinkNetworkRemoved(value)); } @Override public void onLinkAddressAdded(String value) { LinkAddress la; try { la = new LinkAddress(value); } catch (IllegalArgumentException x) { Log.e( TAG, "onLinkAddressAdded: Bad LinkAddress \"" + value + "\", " + x); return; } mHandler.post(() -> cb.onLinkAddressAdded(la)); } @Override public void onLinkAddressRemoved(String value) { LinkAddress la; try { la = new LinkAddress(value); } catch (IllegalArgumentException x) { Log.e( TAG, "onLinkAddressRemoved: Bad LinkAddress \"" + value + "\", " + x); return; } mHandler.post(() -> cb.onLinkAddressRemoved(la)); } @Override public void onReceiveFromCommissioner(byte[] packet) { // This is only used by the LowpanCommissioningSession. } }; try { mBinder.addListener(listenerBinder); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } synchronized (mListenerMap) { mListenerMap.put(System.identityHashCode(cb), listenerBinder); } }
Registers a subclass of Callback to receive events.

The thread upon which events will be dispatched is unspecified.

Params:
  • cb – Subclass of Callback which will receive events.
See Also:
/** * Registers a subclass of {@link LowpanInterface.Callback} to receive events. * * <p>The thread upon which events will be dispatched is unspecified. * * @param cb Subclass of {@link LowpanInterface.Callback} which will receive events. * @see #registerCallback(Callback, Handler) * @see #unregisterCallback(Callback) */
public void registerCallback(Callback cb) { registerCallback(cb, null); }
Unregisters a previously registered callback class.
Params:
  • cb – Subclass of Callback which was previously registered to receive events.
See Also:
/** * Unregisters a previously registered callback class. * * @param cb Subclass of {@link LowpanInterface.Callback} which was previously registered to * receive events. * @see #registerCallback(Callback, Handler) * @see #registerCallback(Callback) */
public void unregisterCallback(Callback cb) { int hashCode = System.identityHashCode(cb); synchronized (mListenerMap) { ILowpanInterfaceListener listenerBinder = mListenerMap.get(hashCode); if (listenerBinder != null) { mListenerMap.remove(hashCode); try { mBinder.removeListener(listenerBinder); } catch (DeadObjectException x) { // We ignore a dead object exception because that // pretty clearly means our callback isn't registered. } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } } } } // Active and Passive Scanning
Creates a new LowpanScanner object for this interface.

This method allocates a new unique object for each call.

See Also:
/** * Creates a new {@link android.net.lowpan.LowpanScanner} object for this interface. * * <p>This method allocates a new unique object for each call. * * @see android.net.lowpan.LowpanScanner */
public @NonNull LowpanScanner createScanner() { return new LowpanScanner(mBinder); } // Route Management
Makes a copy of the internal list of LinkAddresses.
@hide
/** * Makes a copy of the internal list of LinkAddresses. * * @hide */
public LinkAddress[] getLinkAddresses() throws LowpanException { try { String[] linkAddressStrings = mBinder.getLinkAddresses(); LinkAddress[] ret = new LinkAddress[linkAddressStrings.length]; int i = 0; for (String str : linkAddressStrings) { ret[i++] = new LinkAddress(str); } return ret; } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { throw LowpanException.rethrowFromServiceSpecificException(x); } }
Makes a copy of the internal list of networks reachable on via this link.
@hide
/** * Makes a copy of the internal list of networks reachable on via this link. * * @hide */
public IpPrefix[] getLinkNetworks() throws LowpanException { try { return mBinder.getLinkNetworks(); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { throw LowpanException.rethrowFromServiceSpecificException(x); } }
Advertise the given IP prefix as an on-mesh prefix.
@hide
/** * Advertise the given IP prefix as an on-mesh prefix. * * @hide */
public void addOnMeshPrefix(IpPrefix prefix, int flags) throws LowpanException { try { mBinder.addOnMeshPrefix(prefix, flags); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { throw LowpanException.rethrowFromServiceSpecificException(x); } }
Remove an IP prefix previously advertised by this device from the list of advertised on-mesh prefixes.
@hide
/** * Remove an IP prefix previously advertised by this device from the list of advertised on-mesh * prefixes. * * @hide */
public void removeOnMeshPrefix(IpPrefix prefix) { try { mBinder.removeOnMeshPrefix(prefix); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { // Catch and ignore all service exceptions Log.e(TAG, x.toString()); } }
Advertise this device to other devices on the mesh network as having a specific route to the given network. This device will then receive forwarded traffic for that network.
@hide
/** * Advertise this device to other devices on the mesh network as having a specific route to the * given network. This device will then receive forwarded traffic for that network. * * @hide */
public void addExternalRoute(IpPrefix prefix, int flags) throws LowpanException { try { mBinder.addExternalRoute(prefix, flags); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { throw LowpanException.rethrowFromServiceSpecificException(x); } }
Revoke a previously advertised specific route to the given network.
@hide
/** * Revoke a previously advertised specific route to the given network. * * @hide */
public void removeExternalRoute(IpPrefix prefix) { try { mBinder.removeExternalRoute(prefix); } catch (RemoteException x) { throw x.rethrowAsRuntimeException(); } catch (ServiceSpecificException x) { // Catch and ignore all service exceptions Log.e(TAG, x.toString()); } } }