Home | History | Annotate | Download | only in lowpan
      1 /*
      2  * Copyright 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 com.android.commands.lowpan;
     18 
     19 import android.net.LinkAddress;
     20 import android.net.lowpan.ILowpanInterface;
     21 import android.net.lowpan.LowpanBeaconInfo;
     22 import android.net.lowpan.LowpanCredential;
     23 import android.net.lowpan.LowpanEnergyScanResult;
     24 import android.net.lowpan.LowpanException;
     25 import android.net.lowpan.LowpanIdentity;
     26 import android.net.lowpan.LowpanInterface;
     27 import android.net.lowpan.LowpanManager;
     28 import android.net.lowpan.LowpanProvision;
     29 import android.net.lowpan.LowpanScanner;
     30 import android.os.RemoteException;
     31 import android.os.ServiceSpecificException;
     32 import android.util.AndroidRuntimeException;
     33 import com.android.internal.os.BaseCommand;
     34 import com.android.internal.util.HexDump;
     35 import java.io.PrintStream;
     36 import java.util.HashMap;
     37 import java.util.Map;
     38 import java.util.concurrent.Semaphore;
     39 import java.util.concurrent.TimeUnit;
     40 
     41 public class LowpanCtl extends BaseCommand {
     42     private LowpanManager mLowpanManager;
     43     private LowpanInterface mLowpanInterface;
     44     private ILowpanInterface mILowpanInterface;
     45     private String mLowpanInterfaceName;
     46 
     47     /**
     48      * Command-line entry point.
     49      *
     50      * @param args The command-line arguments
     51      */
     52     public static void main(String[] args) {
     53         new LowpanCtl().run(args);
     54     }
     55 
     56     @Override
     57     public void onShowUsage(PrintStream out) {
     58         out.println(
     59                 "usage: lowpanctl [options] [subcommand] [subcommand-options]\n"
     60                         + "options:\n"
     61                         + "       -I / --interface <iface-name> ..... Interface Name\n"
     62                         + "subcommands:\n"
     63                         + "       lowpanctl status\n"
     64                         + "       lowpanctl form\n"
     65                         + "       lowpanctl join\n"
     66                         + "       lowpanctl attach\n"
     67                         + "       lowpanctl leave\n"
     68                         + "       lowpanctl enable\n"
     69                         + "       lowpanctl disable\n"
     70                         + "       lowpanctl show-credential\n"
     71                         + "       lowpanctl scan\n"
     72                         + "       lowpanctl reset\n"
     73                         + "       lowpanctl list\n"
     74                         + "\n"
     75                         + "usage: lowpanctl [options] join/form/attach [network-name]\n"
     76                         + "subcommand-options:\n"
     77                         + "       --name <network-name> ............. Network Name\n"
     78                         + "       -p / --panid <panid> .............. PANID\n"
     79                         + "       -c / --channel <channel> .......... Channel Index\n"
     80                         + "       -x / --xpanid <xpanid> ............ XPANID\n"
     81                         + "       -k / --master-key <master-key> .... Master Key\n"
     82                         + "       --master-key-index <key-index> .... Key Index\n"
     83                         + "\n"
     84                         + "usage: lowpanctl [options] show-credential\n"
     85                         + "subcommand-options:\n"
     86                         + "       -r / --raw ........................ Print only key contents\n"
     87                         + "\n");
     88     }
     89 
     90     private class CommandErrorException extends AndroidRuntimeException {
     91         public CommandErrorException(String desc) {
     92             super(desc);
     93         }
     94     }
     95 
     96     private class ArgumentErrorException extends IllegalArgumentException {
     97         public ArgumentErrorException(String desc) {
     98             super(desc);
     99         }
    100     }
    101 
    102     private void throwCommandError(String desc) {
    103         throw new CommandErrorException(desc);
    104     }
    105 
    106     private void throwArgumentError(String desc) {
    107         throw new ArgumentErrorException(desc);
    108     }
    109 
    110     private LowpanManager getLowpanManager() {
    111         if (mLowpanManager == null) {
    112             mLowpanManager = LowpanManager.getManager();
    113 
    114             if (mLowpanManager == null) {
    115                 System.err.println(NO_SYSTEM_ERROR_CODE);
    116                 throwCommandError("Can't connect to LoWPAN service; is the service running?");
    117             }
    118         }
    119         return mLowpanManager;
    120     }
    121 
    122     private LowpanInterface getLowpanInterface() {
    123         if (mLowpanInterface == null) {
    124             if (mLowpanInterfaceName == null) {
    125                 String interfaceArray[] = getLowpanManager().getInterfaceList();
    126                 if (interfaceArray.length != 0) {
    127                     mLowpanInterfaceName = interfaceArray[0];
    128                 } else {
    129                     throwCommandError("No LoWPAN interfaces are present");
    130                 }
    131             }
    132             mLowpanInterface = getLowpanManager().getInterface(mLowpanInterfaceName);
    133 
    134             if (mLowpanInterface == null) {
    135                 throwCommandError("Unknown LoWPAN interface \"" + mLowpanInterfaceName + "\"");
    136             }
    137         }
    138         return mLowpanInterface;
    139     }
    140 
    141     private ILowpanInterface getILowpanInterface() {
    142         if (mILowpanInterface == null) {
    143             mILowpanInterface = getLowpanInterface().getService();
    144         }
    145         return mILowpanInterface;
    146     }
    147 
    148     @Override
    149     public void onRun() throws Exception {
    150         try {
    151             String op;
    152             while ((op = nextArgRequired()) != null) {
    153                 if (op.equals("-I") || op.equals("--interface")) {
    154                     mLowpanInterfaceName = nextArgRequired();
    155                 } else if (op.equals("-h") || op.equals("--help") || op.equals("help")) {
    156                     onShowUsage(System.out);
    157                     break;
    158                 } else if (op.startsWith("-")) {
    159                     throwArgumentError("Unrecognized argument \"" + op + "\"");
    160                 } else if (op.equals("status") || op.equals("stat")) {
    161                     runStatus();
    162                     break;
    163                 } else if (op.equals("scan") || op.equals("netscan") || op.equals("ns")) {
    164                     runNetScan();
    165                     break;
    166                 } else if (op.equals("attach")) {
    167                     runAttach();
    168                     break;
    169                 } else if (op.equals("enable")) {
    170                     runEnable();
    171                     break;
    172                 } else if (op.equals("disable")) {
    173                     runDisable();
    174                     break;
    175                 } else if (op.equals("show-credential")) {
    176                     runShowCredential();
    177                     break;
    178                 } else if (op.equals("join")) {
    179                     runJoin();
    180                     break;
    181                 } else if (op.equals("form")) {
    182                     runForm();
    183                     break;
    184                 } else if (op.equals("leave")) {
    185                     runLeave();
    186                     break;
    187                 } else if (op.equals("energyscan") || op.equals("energy") || op.equals("es")) {
    188                     runEnergyScan();
    189                     break;
    190                 } else if (op.equals("list") || op.equals("ls")) {
    191                     runListInterfaces();
    192                     break;
    193                 } else if (op.equals("reset")) {
    194                     runReset();
    195                     break;
    196                 } else {
    197                     throwArgumentError("Unrecognized argument \"" + op + "\"");
    198                     break;
    199                 }
    200             }
    201         } catch (ServiceSpecificException x) {
    202             System.out.println(
    203                     "ServiceSpecificException: " + x.errorCode + ": " + x.getLocalizedMessage());
    204             throw x;
    205 
    206         } catch (ArgumentErrorException x) {
    207             // Rethrow so we get syntax help.
    208             throw x;
    209 
    210         } catch (IllegalArgumentException x) {
    211             // This was an argument exception that wasn't an
    212             // argument error. We dump our stack trace immediately
    213             // because this might not be from a command line argument.
    214             x.printStackTrace(System.err);
    215             System.exit(1);
    216 
    217         } catch (CommandErrorException x) {
    218             // Command errors are normal errors that just
    219             // get printed out without any fanfare.
    220             System.out.println("error: " + x.getLocalizedMessage());
    221             System.exit(1);
    222         }
    223     }
    224 
    225     private void runReset() throws LowpanException {
    226         getLowpanInterface().reset();
    227     }
    228 
    229     private void runEnable() throws LowpanException {
    230         getLowpanInterface().setEnabled(true);
    231     }
    232 
    233     private void runDisable() throws LowpanException {
    234         getLowpanInterface().setEnabled(false);
    235     }
    236 
    237     private LowpanProvision getProvisionFromArgs(boolean credentialRequired) {
    238         LowpanProvision.Builder builder = new LowpanProvision.Builder();
    239         Map<String, Object> properties = new HashMap();
    240         LowpanIdentity.Builder identityBuilder = new LowpanIdentity.Builder();
    241         LowpanCredential credential = null;
    242         String arg;
    243         byte[] masterKey = null;
    244         int masterKeyIndex = 0;
    245         boolean hasName = false;
    246 
    247         while ((arg = nextArg()) != null) {
    248             if (arg.equals("--name")) {
    249                 identityBuilder.setName(nextArgRequired());
    250                 hasName = true;
    251             } else if (arg.equals("-p") || arg.equals("--panid")) {
    252                 identityBuilder.setPanid(Integer.decode(nextArgRequired()));
    253             } else if (arg.equals("-c") || arg.equals("--channel")) {
    254                 identityBuilder.setChannel(Integer.decode(nextArgRequired()));
    255             } else if (arg.equals("-x") || arg.equals("--xpanid")) {
    256                 identityBuilder.setXpanid(HexDump.hexStringToByteArray(nextArgRequired()));
    257             } else if (arg.equals("-k") || arg.equals("--master-key")) {
    258                 masterKey = HexDump.hexStringToByteArray(nextArgRequired());
    259             } else if (arg.equals("--master-key-index")) {
    260                 masterKeyIndex = Integer.decode(nextArgRequired());
    261             } else if (arg.startsWith("-") || hasName) {
    262                 throwArgumentError("Unrecognized argument \"" + arg + "\"");
    263             } else {
    264                 // This is the network name
    265                 identityBuilder.setName(arg);
    266                 hasName = true;
    267             }
    268         }
    269 
    270         if (credential == null && masterKey != null) {
    271             if (masterKeyIndex == 0) {
    272                 credential = LowpanCredential.createMasterKey(masterKey);
    273             } else {
    274                 credential = LowpanCredential.createMasterKey(masterKey, masterKeyIndex);
    275             }
    276         }
    277 
    278         if (credential != null) {
    279             builder.setLowpanCredential(credential);
    280         } else if (credentialRequired) {
    281             throwArgumentError("No credential (like a master key) was specified!");
    282         }
    283 
    284         return builder.setLowpanIdentity(identityBuilder.build()).build();
    285     }
    286 
    287     private void runAttach() throws LowpanException {
    288         LowpanProvision provision = getProvisionFromArgs(true);
    289 
    290         System.out.println(
    291                 "Attaching to " + provision.getLowpanIdentity() + " with provided credential");
    292 
    293         getLowpanInterface().attach(provision);
    294 
    295         System.out.println("Attached.");
    296     }
    297 
    298     private void runLeave() throws LowpanException {
    299         getLowpanInterface().leave();
    300     }
    301 
    302     private void runJoin() throws LowpanException {
    303         LowpanProvision provision = getProvisionFromArgs(true);
    304 
    305         System.out.println(
    306                 "Joining " + provision.getLowpanIdentity() + " with provided credential");
    307 
    308         getLowpanInterface().join(provision);
    309 
    310         System.out.println("Joined.");
    311     }
    312 
    313     private void runForm() throws LowpanException {
    314         LowpanProvision provision = getProvisionFromArgs(false);
    315 
    316         if (provision.getLowpanCredential() != null) {
    317             System.out.println(
    318                     "Forming " + provision.getLowpanIdentity() + " with provided credential");
    319         } else {
    320             System.out.println("Forming " + provision.getLowpanIdentity());
    321         }
    322 
    323         getLowpanInterface().form(provision);
    324 
    325         System.out.println("Formed.");
    326     }
    327 
    328     private void runStatus() throws LowpanException, RemoteException {
    329         LowpanInterface iface = getLowpanInterface();
    330         StringBuffer sb = new StringBuffer();
    331 
    332         sb.append(iface.getName())
    333                 .append("\t")
    334                 .append(iface.getState());
    335 
    336         if (!iface.isEnabled()) {
    337             sb.append(" DISABLED");
    338 
    339         } else if (iface.getState() != LowpanInterface.STATE_FAULT) {
    340             sb.append(" (" + iface.getRole() + ")");
    341 
    342             if (iface.isUp()) {
    343                 sb.append(" UP");
    344             }
    345 
    346             if (iface.isConnected()) {
    347                 sb.append(" CONNECTED");
    348             }
    349 
    350             if (iface.isCommissioned()) {
    351                 sb.append(" COMMISSIONED");
    352 
    353                 LowpanIdentity identity = getLowpanInterface().getLowpanIdentity();
    354 
    355                 if (identity != null) {
    356                     sb.append("\n\t").append(identity);
    357                 }
    358             }
    359 
    360             if (iface.isUp()) {
    361                 for (LinkAddress addr : iface.getLinkAddresses()) {
    362                     sb.append("\n\t").append(addr);
    363                 }
    364             }
    365         }
    366 
    367         sb.append("\n");
    368         System.out.println(sb.toString());
    369     }
    370 
    371     private void runShowCredential() throws LowpanException, RemoteException {
    372         LowpanInterface iface = getLowpanInterface();
    373         boolean raw = false;
    374         String arg;
    375         while ((arg = nextArg()) != null) {
    376             if (arg.equals("--raw") || arg.equals("-r")) {
    377                 raw = true;
    378             } else {
    379                 throwArgumentError("Unrecognized argument \"" + arg + "\"");
    380             }
    381         }
    382 
    383         LowpanCredential credential = iface.getLowpanCredential();
    384         if (raw) {
    385             System.out.println(HexDump.toHexString(credential.getMasterKey()));
    386         } else {
    387             System.out.println(iface.getName() + "\t" + credential.toSensitiveString());
    388         }
    389     }
    390 
    391     private void runListInterfaces() {
    392         for (String name : getLowpanManager().getInterfaceList()) {
    393             System.out.println(name);
    394         }
    395     }
    396 
    397     private void runNetScan() throws LowpanException, InterruptedException {
    398         LowpanScanner scanner = getLowpanInterface().createScanner();
    399         String arg;
    400 
    401         while ((arg = nextArg()) != null) {
    402             if (arg.equals("-c") || arg.equals("--channel")) {
    403                 scanner.addChannel(Integer.decode(nextArgRequired()));
    404             } else {
    405                 throwArgumentError("Unrecognized argument \"" + arg + "\"");
    406             }
    407         }
    408 
    409         Semaphore semaphore = new Semaphore(1);
    410 
    411         scanner.setCallback(
    412                 new LowpanScanner.Callback() {
    413                     @Override
    414                     public void onNetScanBeacon(LowpanBeaconInfo beacon) {
    415                         System.out.println(beacon.toString());
    416                     }
    417 
    418                     @Override
    419                     public void onScanFinished() {
    420                         semaphore.release();
    421                     }
    422                 });
    423 
    424         semaphore.acquire();
    425         scanner.startNetScan();
    426 
    427         // Wait for our scan to complete.
    428         if (semaphore.tryAcquire(1, 60L, TimeUnit.SECONDS)) {
    429             semaphore.release();
    430         } else {
    431             throwCommandError("Timeout while waiting for scan to complete.");
    432         }
    433     }
    434 
    435     private void runEnergyScan() throws LowpanException, InterruptedException {
    436         LowpanScanner scanner = getLowpanInterface().createScanner();
    437         String arg;
    438 
    439         while ((arg = nextArg()) != null) {
    440             if (arg.equals("-c") || arg.equals("--channel")) {
    441                 scanner.addChannel(Integer.decode(nextArgRequired()));
    442             } else {
    443                 throwArgumentError("Unrecognized argument \"" + arg + "\"");
    444             }
    445         }
    446 
    447         Semaphore semaphore = new Semaphore(1);
    448 
    449         scanner.setCallback(
    450                 new LowpanScanner.Callback() {
    451                     @Override
    452                     public void onEnergyScanResult(LowpanEnergyScanResult result) {
    453                         System.out.println(result.toString());
    454                     }
    455 
    456                     @Override
    457                     public void onScanFinished() {
    458                         semaphore.release();
    459                     }
    460                 });
    461 
    462         semaphore.acquire();
    463         scanner.startEnergyScan();
    464 
    465         // Wait for our scan to complete.
    466         if (semaphore.tryAcquire(1, 60L, TimeUnit.SECONDS)) {
    467             semaphore.release();
    468         } else {
    469             throwCommandError("Timeout while waiting for scan to complete.");
    470         }
    471     }
    472 }
    473