diff options
author | Xin Li <delphij@google.com> | 2017-12-06 11:52:02 -0800 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2017-12-06 14:24:50 -0800 |
commit | d65ab13260cca31e9fb33bab97b0d7fd7ecd65ca (patch) | |
tree | 6aa83f202600cf75d6a27bca41ca63393bca6432 | |
parent | 7bf3ff0542b52af0693d7c093159bcd8691a10a5 (diff) | |
parent | e6a1be5f36c91c12ba89bca99233bdb9d7b448e0 (diff) | |
download | lowpan-d65ab13260cca31e9fb33bab97b0d7fd7ecd65ca.tar.gz |
DO NOT MERGE: Merge Oreo MR1 into masterandroid-wear-8.0.0_r1
Exempt-From-Owner-Approval: Changes already landed internally
Change-Id: I4bdc4ff1c0313e76927996807892650a842396ba
43 files changed, 4150 insertions, 0 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bc24d41 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +*.java text whitespace=trailing-space,tab-in-indent,tabwidth=4,blank-at-eol,blank-at-eof +*.cpp text whitespace=trailing-space,tab-in-indent,tabwidth=4,blank-at-eol,blank-at-eof +*.h text whitespace=trailing-space,tab-in-indent,tabwidth=4,blank-at-eol,blank-at-eof +*.aidl text whitespace=trailing-space,tab-in-indent,tabwidth=4,blank-at-eol,blank-at-eof +*.xml text whitespace=trailing-space,tab-in-indent,tabwidth=4,blank-at-eol,blank-at-eof diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6ef218 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea + diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..7bd636b --- /dev/null +++ b/Android.mk @@ -0,0 +1,19 @@ +# +# 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. +# + +LOCAL_PATH := $(call my-dir) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/command/Android.mk b/command/Android.mk new file mode 100644 index 0000000..82c0ffd --- /dev/null +++ b/command/Android.mk @@ -0,0 +1,36 @@ +# +# 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. +# + +LOCAL_PATH := $(call my-dir) + +ifneq (,$(findstring lowpan/java,$(FRAMEWORKS_BASE_SUBDIRS))) + +include $(CLEAR_VARS) +LOCAL_MODULE := lowpan-command +LOCAL_SRC_FILES += java/com/android/commands/lowpan/LowpanCtl.java +include $(BUILD_JAVA_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := lowpanctl +LOCAL_MODULE_TAGS := optional +LOCAL_REQUIRED_MODULES := lowpan-command +LOCAL_SRC_FILES := lowpanctl +LOCAL_MODULE_CLASS := EXECUTABLES +include $(BUILD_PREBUILT) + +endif + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/command/java/com/android/commands/lowpan/LowpanCtl.java b/command/java/com/android/commands/lowpan/LowpanCtl.java new file mode 100644 index 0000000..8e36841 --- /dev/null +++ b/command/java/com/android/commands/lowpan/LowpanCtl.java @@ -0,0 +1,433 @@ +/* + * Copyright 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 com.android.commands.lowpan; + +import android.net.lowpan.ILowpanInterface; +import android.net.lowpan.LowpanBeaconInfo; +import android.net.lowpan.LowpanCredential; +import android.net.lowpan.LowpanEnergyScanResult; +import android.net.lowpan.LowpanException; +import android.net.lowpan.LowpanIdentity; +import android.net.lowpan.LowpanInterface; +import android.net.lowpan.LowpanManager; +import android.net.lowpan.LowpanProvision; +import android.net.lowpan.LowpanScanner; +import android.net.LinkAddress; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.util.AndroidRuntimeException; +import com.android.internal.os.BaseCommand; +import com.android.internal.util.HexDump; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +public class LowpanCtl extends BaseCommand { + private LowpanManager mLowpanManager; + private LowpanInterface mLowpanInterface; + private ILowpanInterface mILowpanInterface; + private String mLowpanInterfaceName; + + /** + * Command-line entry point. + * + * @param args The command-line arguments + */ + public static void main(String[] args) { + new LowpanCtl().run(args); + } + + @Override + public void onShowUsage(PrintStream out) { + out.println( + "usage: lowpanctl [options] [subcommand] [subcommand-options]\n" + + "options:\n" + + " -I / --interface <iface-name> ..... Interface Name\n" + + "subcommands:\n" + + " lowpanctl status\n" + + " lowpanctl form\n" + + " lowpanctl join\n" + + " lowpanctl attach\n" + + " lowpanctl leave\n" + + " lowpanctl enable\n" + + " lowpanctl disable\n" + + " lowpanctl show-credential\n" + + " lowpanctl scan\n" + + " lowpanctl reset\n" + + " lowpanctl list\n" + + "\n" + + "usage: lowpanctl [options] join/form/attach [network-name]\n" + + "subcommand-options:\n" + + " --name <network-name> ............. Network Name\n" + + " -p / --panid <panid> .............. PANID\n" + + " -c / --channel <channel> .......... Channel Index\n" + + " -x / --xpanid <xpanid> ............ XPANID\n" + + " -k / --master-key <master-key> .... Master Key\n" + + " --master-key-index <key-index> .... Key Index\n" + + "\n" + + "usage: lowpanctl [options] show-credential\n" + + "subcommand-options:\n" + + " -r / --raw ........................ Print only key contents\n" + + "\n"); + + } + + private class CommandErrorException extends AndroidRuntimeException { + public CommandErrorException(String desc) { + super(desc); + } + } + + private void throwCommandError(String desc) { + throw new CommandErrorException(desc); + } + + private LowpanInterface getLowpanInterface() { + if (mLowpanInterface == null) { + if (mLowpanInterfaceName == null) { + String interfaceArray[] = mLowpanManager.getInterfaceList(); + if (interfaceArray.length != 0) { + mLowpanInterfaceName = interfaceArray[0]; + } else { + throwCommandError("No LoWPAN interfaces are present"); + } + } + mLowpanInterface = mLowpanManager.getInterface(mLowpanInterfaceName); + + if (mLowpanInterface == null) { + throwCommandError("Unknown LoWPAN interface \"" + mLowpanInterfaceName + "\""); + } + } + return mLowpanInterface; + } + + private ILowpanInterface getILowpanInterface() { + if (mILowpanInterface == null) { + mILowpanInterface = getLowpanInterface().getService(); + } + return mILowpanInterface; + } + + @Override + public void onRun() throws Exception { + mLowpanManager = LowpanManager.getManager(); + + if (mLowpanManager == null) { + System.err.println(NO_SYSTEM_ERROR_CODE); + throwCommandError("Can't connect to LoWPAN service; is the service running?"); + } + + try { + String op; + while ((op = nextArgRequired()) != null) { + if (op.equals("-I") || op.equals("--interface")) { + mLowpanInterfaceName = nextArgRequired(); + } else if (op.startsWith("-")) { + throwCommandError("Unrecognized argument \"" + op + "\""); + } else if (op.equals("status") || op.equals("stat")) { + runStatus(); + break; + } else if (op.equals("scan") || op.equals("netscan") || op.equals("ns")) { + runNetScan(); + break; + } else if (op.equals("attach")) { + runAttach(); + break; + } else if (op.equals("enable")) { + runEnable(); + break; + } else if (op.equals("disable")) { + runDisable(); + break; + } else if (op.equals("show-credential")) { + runShowCredential(); + break; + } else if (op.equals("join")) { + runJoin(); + break; + } else if (op.equals("form")) { + runForm(); + break; + } else if (op.equals("leave")) { + runLeave(); + break; + } else if (op.equals("energyscan") || op.equals("energy") || op.equals("es")) { + runEnergyScan(); + break; + } else if (op.equals("list") || op.equals("ls")) { + runListInterfaces(); + break; + } else if (op.equals("reset")) { + runReset(); + break; + } else { + showError("Error: unknown command '" + op + "'"); + break; + } + } + } catch (ServiceSpecificException x) { + System.out.println( + "ServiceSpecificException: " + x.errorCode + ": " + x.getLocalizedMessage()); + } catch (CommandErrorException x) { + System.out.println("error: " + x.getLocalizedMessage()); + } + } + + private void runReset() throws LowpanException { + getLowpanInterface().reset(); + } + + private void runEnable() throws LowpanException { + getLowpanInterface().setEnabled(true); + } + + private void runDisable() throws LowpanException { + getLowpanInterface().setEnabled(false); + } + + private LowpanProvision getProvisionFromArgs(boolean credentialRequired) { + LowpanProvision.Builder builder = new LowpanProvision.Builder(); + Map<String, Object> properties = new HashMap(); + LowpanIdentity.Builder identityBuilder = new LowpanIdentity.Builder(); + LowpanCredential credential = null; + String arg; + byte[] masterKey = null; + int masterKeyIndex = 0; + boolean hasName = false; + + while ((arg = nextArg()) != null) { + if (arg.equals("--name")) { + identityBuilder.setName(nextArgRequired()); + hasName = true; + } else if (arg.equals("-p") || arg.equals("--panid")) { + identityBuilder.setPanid(Integer.decode(nextArgRequired())); + } else if (arg.equals("-c") || arg.equals("--channel")) { + identityBuilder.setChannel(Integer.decode(nextArgRequired())); + } else if (arg.equals("-x") || arg.equals("--xpanid")) { + identityBuilder.setXpanid(HexDump.hexStringToByteArray(nextArgRequired())); + } else if (arg.equals("-k") || arg.equals("--master-key")) { + masterKey = HexDump.hexStringToByteArray(nextArgRequired()); + } else if (arg.equals("--master-key-index")) { + masterKeyIndex = Integer.decode(nextArgRequired()); + } else if (arg.equals("--help")) { + throwCommandError(""); + } else if (arg.startsWith("-") || hasName) { + throwCommandError("Unrecognized argument \"" + arg + "\""); + } else { + // This is the network name + identityBuilder.setName(arg); + hasName = true; + } + } + + if (credential == null && masterKey != null) { + if (masterKeyIndex == 0) { + credential = LowpanCredential.createMasterKey(masterKey); + } else { + credential = LowpanCredential.createMasterKey(masterKey, masterKeyIndex); + } + } + + if (credential != null) { + builder.setLowpanCredential(credential); + } else if (credentialRequired) { + throwCommandError("No credential (like a master key) was specified!"); + } + + return builder.setLowpanIdentity(identityBuilder.build()).build(); + } + + private void runAttach() throws LowpanException { + LowpanProvision provision = getProvisionFromArgs(true); + + System.out.println( + "Attaching to " + provision.getLowpanIdentity() + " with provided credential"); + + getLowpanInterface().attach(provision); + + System.out.println("Attached."); + } + + private void runLeave() throws LowpanException { + getLowpanInterface().leave(); + } + + private void runJoin() throws LowpanException { + LowpanProvision provision = getProvisionFromArgs(true); + + System.out.println( + "Joining " + provision.getLowpanIdentity() + " with provided credential"); + + getLowpanInterface().join(provision); + + System.out.println("Joined."); + } + + private void runForm() throws LowpanException { + LowpanProvision provision = getProvisionFromArgs(false); + + if (provision.getLowpanCredential() != null) { + System.out.println( + "Forming " + + provision.getLowpanIdentity() + + " with provided credential"); + } else { + System.out.println("Forming " + provision.getLowpanIdentity()); + } + + getLowpanInterface().form(provision); + + System.out.println("Formed."); + } + + private void runStatus() throws LowpanException, RemoteException { + LowpanInterface iface = getLowpanInterface(); + StringBuffer sb = new StringBuffer(); + + sb.append(iface.getName()) + .append("\t") + .append(iface.getState() + " (" + iface.getRole() + ")"); + + if (iface.isUp()) { + sb.append(" UP"); + } + + if (iface.isConnected()) { + sb.append(" CONNECTED"); + } + + if (iface.isCommissioned()) { + sb.append(" COMMISSIONED"); + } + + sb + .append("\n\t") + .append(getLowpanInterface().getLowpanIdentity()); + + for (LinkAddress addr : iface.getLinkAddresses()) { + sb.append("\n\t").append(addr); + } + + sb.append("\n"); + System.out.println(sb.toString()); + } + + private void runShowCredential() throws LowpanException, RemoteException { + LowpanInterface iface = getLowpanInterface(); + boolean raw = false; + String arg; + while ((arg = nextArg()) != null) { + if (arg.equals("--raw") || arg.equals("-r")) { + raw = true; + } else { + throwCommandError("Unrecognized argument \"" + arg + "\""); + } + } + + LowpanCredential credential = iface.getLowpanCredential(); + if (raw) { + System.out.println(HexDump.toHexString(credential.getMasterKey())); + } else { + System.out.println( + iface.getName() + "\t" + credential.toSensitiveString()); + } + } + + private void runListInterfaces() { + for (String name : mLowpanManager.getInterfaceList()) { + System.out.println(name); + } + } + + private void runNetScan() throws LowpanException, InterruptedException { + LowpanScanner scanner = getLowpanInterface().createScanner(); + String arg; + + while ((arg = nextArg()) != null) { + if (arg.equals("-c") || arg.equals("--channel")) { + scanner.addChannel(Integer.decode(nextArgRequired())); + } else { + throwCommandError("Unrecognized argument \"" + arg + "\""); + } + } + + Semaphore semaphore = new Semaphore(1); + + scanner.setCallback( + new LowpanScanner.Callback() { + @Override + public void onNetScanBeacon(LowpanBeaconInfo beacon) { + System.out.println(beacon.toString()); + } + + @Override + public void onScanFinished() { + semaphore.release(); + } + }); + + semaphore.acquire(); + scanner.startNetScan(); + + // Wait for our scan to complete. + if (semaphore.tryAcquire(1, 60L, TimeUnit.SECONDS)) { + semaphore.release(); + } else { + throwCommandError("Timeout while waiting for scan to complete."); + } + } + + private void runEnergyScan() throws LowpanException, InterruptedException { + LowpanScanner scanner = getLowpanInterface().createScanner(); + String arg; + + while ((arg = nextArg()) != null) { + if (arg.equals("-c") || arg.equals("--channel")) { + scanner.addChannel(Integer.decode(nextArgRequired())); + } else { + throwCommandError("Unrecognized argument \"" + arg + "\""); + } + } + + Semaphore semaphore = new Semaphore(1); + + scanner.setCallback( + new LowpanScanner.Callback() { + @Override + public void onEnergyScanResult(LowpanEnergyScanResult result) { + System.out.println(result.toString()); + } + + @Override + public void onScanFinished() { + semaphore.release(); + } + }); + + semaphore.acquire(); + scanner.startEnergyScan(); + + // Wait for our scan to complete. + if (semaphore.tryAcquire(1, 60L, TimeUnit.SECONDS)) { + semaphore.release(); + } else { + throwCommandError("Timeout while waiting for scan to complete."); + } + } +} diff --git a/command/lowpanctl b/command/lowpanctl new file mode 100755 index 0000000..188b9d6 --- /dev/null +++ b/command/lowpanctl @@ -0,0 +1,6 @@ +# Script to start "lowpan-command" on the device, which has a very rudimentary +# shell. +# +base=/system +export CLASSPATH=$base/framework/lowpan-command.jar +exec app_process $base/bin com.android.commands.lowpan.LowpanCtl "$@" diff --git a/libandroid_net_lowpan/Android.mk b/libandroid_net_lowpan/Android.mk new file mode 100644 index 0000000..a7bef1f --- /dev/null +++ b/libandroid_net_lowpan/Android.mk @@ -0,0 +1,35 @@ +# +# 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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := libandroid_net_lowpan +LOCAL_MODULE_TAGS := optional +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_SHARED_LIBRARIES += libbase +LOCAL_SHARED_LIBRARIES += libbinder +LOCAL_SHARED_LIBRARIES += libutils +LOCAL_SHARED_LIBRARIES += liblog +LOCAL_AIDL_INCLUDES += frameworks/native/aidl/binder +LOCAL_AIDL_INCLUDES += frameworks/base/lowpan/java +LOCAL_AIDL_INCLUDES += frameworks/base/core/java +LOCAL_SRC_FILES += $(call all-Iaidl-files-under, ../../../../base/lowpan/java/android/net/lowpan) +LOCAL_SRC_FILES += $(call all-cpp-files-under) +include $(BUILD_SHARED_LIBRARY) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/libandroid_net_lowpan/LowpanBeaconInfo.cpp b/libandroid_net_lowpan/LowpanBeaconInfo.cpp new file mode 100644 index 0000000..95746df --- /dev/null +++ b/libandroid_net_lowpan/LowpanBeaconInfo.cpp @@ -0,0 +1,218 @@ +/* + * 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. + */ + +#define LOG_TAG "LowpanBeaconInfo" + +#include <android/net/lowpan/LowpanBeaconInfo.h> + +#include <binder/Parcel.h> +#include <log/log.h> +#include <utils/Errors.h> + +using android::BAD_TYPE; +using android::BAD_VALUE; +using android::NO_ERROR; +using android::Parcel; +using android::status_t; +using android::UNEXPECTED_NULL; +using android::net::lowpan::LowpanBeaconInfo; +using namespace ::android::binder; + +namespace android { + +namespace net { + +namespace lowpan { + +#define RETURN_IF_FAILED(calledOnce) \ + { \ + status_t returnStatus = calledOnce; \ + if (returnStatus) { \ + ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ + return returnStatus; \ + } \ + } + +LowpanBeaconInfo::Builder::Builder() { +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::setName(const std::string& value) { + mIdentityBuilder.setName(value); + return *this; +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::setType(const std::string& value) { + mIdentityBuilder.setType(value); + return *this; +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::setType(const ::android::String16& value) { + mIdentityBuilder.setType(value); + return *this; +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::setXpanid(const std::vector<uint8_t>& value) { + mIdentityBuilder.setXpanid(value); + return *this; +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::setXpanid(const uint8_t* valuePtr, int32_t valueLen) { + mIdentityBuilder.setXpanid(valuePtr, valueLen); + return *this; +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::setPanid(int32_t value) { + mIdentityBuilder.setPanid(value); + return *this; +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::setChannel(int32_t value) { + mIdentityBuilder.setChannel(value); + return *this; +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::setLowpanIdentity(const LowpanIdentity& value) { + mIdentityBuilder.setLowpanIdentity(value); + return *this; +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::setRssi(int32_t value) { + mRssi = value; + return *this; +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::setLqi(int32_t value) { + mLqi = value; + return *this; +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::setBeaconAddress(const std::vector<uint8_t>& value) { + mBeaconAddress = value; + return *this; +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::setBeaconAddress(const uint8_t* valuePtr, int32_t valueLen) { + mBeaconAddress.clear(); + mBeaconAddress.insert(mBeaconAddress.end(), valuePtr, valuePtr + valueLen); + return *this; +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::setFlag(int32_t value) { + mFlags.insert(value); + return *this; +} + +LowpanBeaconInfo::Builder& LowpanBeaconInfo::Builder::clearFlag(int32_t value) { + mFlags.erase(value); + return *this; +} + +LowpanBeaconInfo LowpanBeaconInfo::Builder::build(void) const { + return LowpanBeaconInfo(*this); +} + +LowpanBeaconInfo::LowpanBeaconInfo(const LowpanBeaconInfo::Builder& builder) : + mIdentity(builder.mIdentityBuilder.build()), + mRssi(builder.mRssi), + mLqi(builder.mLqi), + mBeaconAddress(builder.mBeaconAddress), + mFlags(builder.mFlags) +{ +} + +status_t LowpanBeaconInfo::writeToParcel(Parcel* parcel) const { + /* + * Keep implementation in sync with writeToParcel() in + * frameworks/base/lowpan/java/android/net/android/net/lowpan/LowpanBeaconInfo.java. + */ + + RETURN_IF_FAILED(mIdentity.writeToParcel(parcel)); + RETURN_IF_FAILED(parcel->writeInt32(mRssi)); + RETURN_IF_FAILED(parcel->writeInt32(mLqi)); + RETURN_IF_FAILED(parcel->writeByteVector(mBeaconAddress)); + RETURN_IF_FAILED(parcel->writeInt32(mFlags.size())); + + std::set<int32_t>::const_iterator iter; + std::set<int32_t>::const_iterator end = mFlags.end(); + + for (iter = mFlags.begin(); iter != end; ++iter) { + RETURN_IF_FAILED(parcel->writeInt32(*iter)); + } + + return NO_ERROR; +} + +status_t LowpanBeaconInfo::readFromParcel(const Parcel* parcel) { + /* + * Keep implementation in sync with readFromParcel() in + * frameworks/base/lowpan/java/android/net/android/net/lowpan/LowpanBeaconInfo.java. + */ + + RETURN_IF_FAILED(mIdentity.readFromParcel(parcel)); + RETURN_IF_FAILED(parcel->readInt32(&mRssi)); + RETURN_IF_FAILED(parcel->readInt32(&mLqi)); + RETURN_IF_FAILED(parcel->readByteVector(&mBeaconAddress)); + + int32_t flagCount = 0; + + RETURN_IF_FAILED(parcel->readInt32(&flagCount)); + + if (flagCount < 0) { + ALOGE("Bad flag count"); + return BAD_VALUE; + } + + mFlags.clear(); + + while (flagCount--) { + int32_t flag = 0; + RETURN_IF_FAILED(parcel->readInt32(&flag)); + mFlags.insert(flag); + } + + return NO_ERROR; +} + +bool LowpanBeaconInfo::operator==(const LowpanBeaconInfo& rhs) +{ + if (mIdentity != rhs.mIdentity) { + return false; + } + + if (mRssi != rhs.mRssi) { + return false; + } + + if (mLqi != rhs.mLqi) { + return false; + } + + if (mBeaconAddress != rhs.mBeaconAddress) { + return false; + } + + if (mFlags != rhs.mFlags) { + return false; + } + + return true; +} + +} // namespace lowpan + +} // namespace net + +} // namespace android diff --git a/libandroid_net_lowpan/LowpanChannelInfo.cpp b/libandroid_net_lowpan/LowpanChannelInfo.cpp new file mode 100644 index 0000000..af4e7a2 --- /dev/null +++ b/libandroid_net_lowpan/LowpanChannelInfo.cpp @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#define LOG_TAG "LowpanChannelInfo" + +#include <android/net/lowpan/LowpanChannelInfo.h> + +#include <binder/Parcel.h> +#include <log/log.h> +#include <utils/Errors.h> + +using android::BAD_TYPE; +using android::BAD_VALUE; +using android::NO_ERROR; +using android::Parcel; +using android::status_t; +using android::UNEXPECTED_NULL; +using android::net::lowpan::LowpanChannelInfo; +using namespace ::android::binder; + +namespace android { + +namespace net { + +namespace lowpan { + +#define RETURN_IF_FAILED(calledOnce) \ + { \ + status_t returnStatus = calledOnce; \ + if (returnStatus) { \ + ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ + return returnStatus; \ + } \ + } + +status_t LowpanChannelInfo::writeToParcel(Parcel* parcel) const { + /* + * Keep implementation in sync with writeToParcel() in + * frameworks/base/lowpan/java/android/net/android/net/lowpan/LowpanChannelInfo.java. + */ + + RETURN_IF_FAILED(parcel->writeInt32(mIndex)); + RETURN_IF_FAILED(parcel->writeUtf8AsUtf16(mName)); + RETURN_IF_FAILED(parcel->writeFloat(mSpectrumCenterFrequency)); + RETURN_IF_FAILED(parcel->writeFloat(mSpectrumBandwidth)); + RETURN_IF_FAILED(parcel->writeInt32(mMaxTxPower)); + RETURN_IF_FAILED(parcel->writeBool(mIsMaskedByRegulatoryDomain)); + + return NO_ERROR; +} + +status_t LowpanChannelInfo::readFromParcel(const Parcel* parcel) { + /* + * Keep implementation in sync with readFromParcel() in + * frameworks/base/lowpan/java/android/net/android/net/lowpan/LowpanChannelInfo.java. + */ + + RETURN_IF_FAILED(parcel->readInt32(&mIndex)); + RETURN_IF_FAILED(parcel->readUtf8FromUtf16(&mName)); + RETURN_IF_FAILED(parcel->readFloat(&mSpectrumCenterFrequency)); + RETURN_IF_FAILED(parcel->readFloat(&mSpectrumBandwidth)); + RETURN_IF_FAILED(parcel->readInt32(&mMaxTxPower)); + RETURN_IF_FAILED(parcel->readBool(&mIsMaskedByRegulatoryDomain)); + + return NO_ERROR; +} + +bool LowpanChannelInfo::operator==(const LowpanChannelInfo& rhs) +{ + if (mIndex != rhs.mIndex) { + return false; + } + + if (mName != rhs.mName) { + return false; + } + + if (mSpectrumCenterFrequency != rhs.mSpectrumCenterFrequency) { + return false; + } + + if (mSpectrumBandwidth != rhs.mSpectrumBandwidth) { + return false; + } + + if (mMaxTxPower != rhs.mMaxTxPower) { + return false; + } + + if (mIsMaskedByRegulatoryDomain != rhs.mIsMaskedByRegulatoryDomain) { + return false; + } + + return true; +} + +} // namespace lowpan + +} // namespace net + +} // namespace android diff --git a/libandroid_net_lowpan/LowpanCredential.cpp b/libandroid_net_lowpan/LowpanCredential.cpp new file mode 100644 index 0000000..f0c6109 --- /dev/null +++ b/libandroid_net_lowpan/LowpanCredential.cpp @@ -0,0 +1,149 @@ +/* + * 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. + */ + +#define LOG_TAG "LowpanCredential" + +#include <android/net/lowpan/LowpanCredential.h> + +#include <binder/Parcel.h> +#include <log/log.h> +#include <utils/Errors.h> + +using android::BAD_TYPE; +using android::BAD_VALUE; +using android::NO_ERROR; +using android::Parcel; +using android::status_t; +using android::UNEXPECTED_NULL; +using android::net::lowpan::LowpanCredential; +using namespace ::android::binder; + +namespace android { + +namespace net { + +namespace lowpan { + +#define RETURN_IF_FAILED(calledOnce) \ + { \ + status_t returnStatus = calledOnce; \ + if (returnStatus) { \ + ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ + return returnStatus; \ + } \ + } + +LowpanCredential::LowpanCredential() : mMasterKeyIndex(UNSPECIFIED_MASTER_KEY_INDEX) { } + +status_t LowpanCredential::initMasterKey(LowpanCredential& out, const uint8_t* masterKeyBytes, int masterKeyLen, int masterKeyIndex) +{ + if (masterKeyLen < 0) { + return BAD_INDEX; + } else if (masterKeyLen > MASTER_KEY_MAX_SIZE) { + return BAD_INDEX; + } else if (masterKeyBytes == NULL) { + return BAD_VALUE; + } + + out.mMasterKey.clear(); + out.mMasterKey.insert(out.mMasterKey.end(), masterKeyBytes, masterKeyBytes + masterKeyLen); + out.mMasterKeyIndex = masterKeyIndex; + + return NO_ERROR; +} + +status_t LowpanCredential::initMasterKey(LowpanCredential& out, const uint8_t* masterKeyBytes, int masterKeyLen) +{ + return LowpanCredential::initMasterKey(out, masterKeyBytes, masterKeyLen, 0); +} + +status_t LowpanCredential::initMasterKey(LowpanCredential& out, const std::vector<uint8_t>& masterKey, int masterKeyIndex) +{ + return LowpanCredential::initMasterKey(out, &masterKey.front(), masterKey.size(), masterKeyIndex); +} + +status_t LowpanCredential::initMasterKey(LowpanCredential& out, const std::vector<uint8_t>& masterKey) +{ + return LowpanCredential::initMasterKey(out, masterKey, 0); +} + +bool LowpanCredential::isMasterKey() const { + return mMasterKey.size() > 0; +} + +bool LowpanCredential::getMasterKey(std::vector<uint8_t>* masterKey) const { + if (isMasterKey()) { + *masterKey = mMasterKey; + return true; + } + return false; +} + +bool LowpanCredential::getMasterKey(const uint8_t** masterKey, int* masterKeyLen) const { + if (isMasterKey()) { + if (masterKey) { + *masterKey = &mMasterKey.front(); + } + if (masterKeyLen) { + *masterKeyLen = mMasterKey.size(); + } + return true; + } + return false; +} + +int LowpanCredential::getMasterKeyIndex() const { + return mMasterKeyIndex; +} + +status_t LowpanCredential::writeToParcel(Parcel* parcel) const { + /* + * Keep implementation in sync with writeToParcel() in + * frameworks/base/lowpan/java/android/net/android/net/lowpan/LowpanCredential.java. + */ + RETURN_IF_FAILED(parcel->writeByteVector(mMasterKey)); + RETURN_IF_FAILED(parcel->writeInt32(mMasterKeyIndex)); + return NO_ERROR; +} + +status_t LowpanCredential::readFromParcel(const Parcel* parcel) { + /* + * Keep implementation in sync with readFromParcel() in + * frameworks/base/lowpan/java/android/net/android/net/lowpan/LowpanCredential.java. + */ + RETURN_IF_FAILED(parcel->readByteVector(&mMasterKey)); + RETURN_IF_FAILED(parcel->readInt32(&mMasterKeyIndex)); + return NO_ERROR; +} + +bool LowpanCredential::operator==(const LowpanCredential& rhs) +{ + if (mMasterKey != rhs.mMasterKey) { + return false; + } + + if (mMasterKeyIndex != rhs.mMasterKeyIndex) { + return false; + } + + return true; +} + +} // namespace lowpan + +} // namespace net + +} // namespace android diff --git a/libandroid_net_lowpan/LowpanIdentity.cpp b/libandroid_net_lowpan/LowpanIdentity.cpp new file mode 100644 index 0000000..0e64c20 --- /dev/null +++ b/libandroid_net_lowpan/LowpanIdentity.cpp @@ -0,0 +1,189 @@ +/* + * 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. + */ + +#define LOG_TAG "LowpanIdentity" + +#include <android/net/lowpan/LowpanIdentity.h> + +#include <binder/Parcel.h> +#include <log/log.h> +#include <utils/Errors.h> + +using android::BAD_TYPE; +using android::BAD_VALUE; +using android::NO_ERROR; +using android::Parcel; +using android::status_t; +using android::UNEXPECTED_NULL; +using android::net::lowpan::LowpanIdentity; +using namespace ::android::binder; + +namespace android { + +namespace net { + +namespace lowpan { + +#define RETURN_IF_FAILED(calledOnce) \ + { \ + status_t returnStatus = calledOnce; \ + if (returnStatus) { \ + ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ + return returnStatus; \ + } \ + } + +bool LowpanIdentity::getName(std::string* value) const { + if (value != NULL) { + *value = mName; + } + return true; +} +bool LowpanIdentity::getType(std::string* value) const { + if (value != NULL) { + *value = mType; + } + return true; +} +bool LowpanIdentity::getXpanid(std::vector<uint8_t>* value) const { + if (value != NULL) { + *value = mXpanid; + } + return true; +} +int32_t LowpanIdentity::getPanid(void) const { + return mPanid; +} +int32_t LowpanIdentity::getChannel(void) const { + return mChannel; +} + +LowpanIdentity::Builder::Builder() { +} + +LowpanIdentity::Builder& LowpanIdentity::Builder::setName(const std::string& value) { + mIdentity.mName = value; + return *this; +} + +LowpanIdentity::Builder& LowpanIdentity::Builder::setType(const std::string& value) { + mIdentity.mType = value; + return *this; +} + +LowpanIdentity::Builder& LowpanIdentity::Builder::setType(const ::android::String16& value) { + return setType(String8(value).string()); +} + +LowpanIdentity::Builder& LowpanIdentity::Builder::setXpanid(const std::vector<uint8_t>& value) { + mIdentity.mXpanid = value; + return *this; +} + +LowpanIdentity::Builder& LowpanIdentity::Builder::setXpanid(const uint8_t* valuePtr, int32_t valueLen) { + mIdentity.mXpanid.clear(); + mIdentity.mXpanid.insert(mIdentity.mXpanid.end(), valuePtr, valuePtr + valueLen); + return *this; +} + +LowpanIdentity::Builder& LowpanIdentity::Builder::setPanid(int32_t value) { + mIdentity.mPanid = value; + return *this; +} + +LowpanIdentity::Builder& LowpanIdentity::Builder::setChannel(int32_t value) { + mIdentity.mChannel = value; + return *this; +} + +LowpanIdentity::Builder& LowpanIdentity::Builder::setLowpanIdentity(const LowpanIdentity& value) { + mIdentity = value; + return *this; +} + +LowpanIdentity LowpanIdentity::Builder::build(void) const { + return mIdentity; +} + +LowpanIdentity::LowpanIdentity() : mPanid(UNSPECIFIED_PANID), mChannel(UNSPECIFIED_CHANNEL) { +} + +status_t LowpanIdentity::writeToParcel(Parcel* parcel) const { + /* + * Keep implementation in sync with writeToParcel() in + * frameworks/base/lowpan/java/android/net/android/net/lowpan/LowpanIdentity.java. + */ + + std::vector<int8_t> rawName(mName.begin(), mName.end()); + + RETURN_IF_FAILED(parcel->writeByteVector(rawName)); + RETURN_IF_FAILED(parcel->writeUtf8AsUtf16(mType)); + RETURN_IF_FAILED(parcel->writeByteVector(mXpanid)); + RETURN_IF_FAILED(parcel->writeInt32(mPanid)); + RETURN_IF_FAILED(parcel->writeInt32(mChannel)); + return NO_ERROR; +} + +status_t LowpanIdentity::readFromParcel(const Parcel* parcel) { + /* + * Keep implementation in sync with readFromParcel() in + * frameworks/base/lowpan/java/android/net/android/net/lowpan/LowpanIdentity.java. + */ + + std::vector<int8_t> rawName; + + RETURN_IF_FAILED(parcel->readByteVector(&rawName)); + + mName = std::string((const char*)&rawName.front(), rawName.size()); + + RETURN_IF_FAILED(parcel->readUtf8FromUtf16(&mType)); + RETURN_IF_FAILED(parcel->readByteVector(&mXpanid)); + RETURN_IF_FAILED(parcel->readInt32(&mPanid)); + RETURN_IF_FAILED(parcel->readInt32(&mChannel)); + return NO_ERROR; +} + +bool LowpanIdentity::operator==(const LowpanIdentity& rhs) +{ + const LowpanIdentity& lhs = *this; + + if (lhs.mName != rhs.mName) { + return false; + } + + if (lhs.mType != rhs.mType) { + return false; + } + + if (lhs.mXpanid != rhs.mXpanid) { + return false; + } + + if (lhs.mPanid != rhs.mPanid) { + return false; + } + + if (lhs.mChannel != rhs.mChannel) { + return false; + } + return true; +} + +} // namespace lowpan + +} // namespace net + +} // namespace android diff --git a/libandroid_net_lowpan/LowpanProvision.cpp b/libandroid_net_lowpan/LowpanProvision.cpp new file mode 100644 index 0000000..315ea54 --- /dev/null +++ b/libandroid_net_lowpan/LowpanProvision.cpp @@ -0,0 +1,121 @@ +/* + * 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. + */ + +#define LOG_TAG "LowpanProvision" + +#include <android/net/lowpan/LowpanProvision.h> + +#include <binder/Parcel.h> +#include <log/log.h> +#include <utils/Errors.h> + +using android::BAD_TYPE; +using android::BAD_VALUE; +using android::NO_ERROR; +using android::Parcel; +using android::status_t; +using android::UNEXPECTED_NULL; +using android::net::lowpan::LowpanProvision; +using namespace ::android::binder; + +namespace android { + +namespace net { + +namespace lowpan { + +#define RETURN_IF_FAILED(calledOnce) \ + { \ + status_t returnStatus = calledOnce; \ + if (returnStatus) { \ + ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ + return returnStatus; \ + } \ + } +LowpanProvision::LowpanProvision(const LowpanIdentity& identity, const LowpanCredential& credential) + : mIdentity(identity), mCredential(credential), mHasCredential(true) +{ +} + +LowpanProvision::LowpanProvision(const LowpanIdentity& identity) + : mIdentity(identity), mHasCredential(false) +{ +} + +const LowpanIdentity* LowpanProvision::getLowpanIdentity() const { + return &mIdentity; +} + +const LowpanCredential* LowpanProvision::getLowpanCredential() const { + return mHasCredential + ? &mCredential + : NULL; +} + +status_t LowpanProvision::writeToParcel(Parcel* parcel) const { + /* + * Keep implementation in sync with writeToParcel() in + * frameworks/base/lowpan/java/android/net/android/net/lowpan/LowpanProvision.java. + */ + + RETURN_IF_FAILED(mIdentity.writeToParcel(parcel)); + RETURN_IF_FAILED(parcel->writeBool(mHasCredential)); + + if (mHasCredential) { + RETURN_IF_FAILED(mCredential.writeToParcel(parcel)); + } + + return NO_ERROR; +} + +status_t LowpanProvision::readFromParcel(const Parcel* parcel) { + /* + * Keep implementation in sync with readFromParcel() in + * frameworks/base/lowpan/java/android/net/android/net/lowpan/LowpanProvision.java. + */ + + RETURN_IF_FAILED(mIdentity.readFromParcel(parcel)); + RETURN_IF_FAILED(parcel->readBool(&mHasCredential)); + + if (mHasCredential) { + RETURN_IF_FAILED(mCredential.readFromParcel(parcel)); + } + + return NO_ERROR; +} + +bool LowpanProvision::operator==(const LowpanProvision& rhs) +{ + if (mIdentity != rhs.mIdentity) { + return false; + } + + if (mHasCredential != rhs.mHasCredential) { + return false; + } + + if (mHasCredential && mCredential != rhs.mCredential) { + return false; + } + + return true; +} + +} // namespace lowpan + +} // namespace net + +} // namespace android diff --git a/libandroid_net_lowpan/include/android/net/lowpan/LowpanBeaconInfo.h b/libandroid_net_lowpan/include/android/net/lowpan/LowpanBeaconInfo.h new file mode 100644 index 0000000..9a971ef --- /dev/null +++ b/libandroid_net_lowpan/include/android/net/lowpan/LowpanBeaconInfo.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef ANDROID_LOWPAN_BEACON_INFO_H +#define ANDROID_LOWPAN_BEACON_INFO_H + +#include <binder/Parcelable.h> +#include <utils/String16.h> +#include <utils/StrongPointer.h> +#include <set> + +#include "LowpanIdentity.h" + +namespace android { + +namespace net { + +namespace lowpan { + +/* + * C++ implementation of the Java class android.net.lowpan.LowpanBeaconInfo + */ +class LowpanBeaconInfo : public Parcelable { +public: + static const int32_t FLAG_CAN_ASSIST = 1; + + class Builder; + LowpanBeaconInfo() = default; + virtual ~LowpanBeaconInfo() = default; + LowpanBeaconInfo(const LowpanBeaconInfo& x) = default; + + bool operator==(const LowpanBeaconInfo& rhs); + bool operator!=(const LowpanBeaconInfo& rhs) { return !(*this == rhs); } + +public: + // Overrides + status_t writeToParcel(Parcel* parcel) const override; + status_t readFromParcel(const Parcel* parcel) override; + +private: + LowpanBeaconInfo(const Builder& builder); + +private: + // Data + LowpanIdentity mIdentity; + int32_t mRssi; + int32_t mLqi; + std::vector<uint8_t> mBeaconAddress; + std::set<int32_t> mFlags; +}; + +class LowpanBeaconInfo::Builder { + friend class LowpanBeaconInfo; +public: + Builder(); + Builder& setName(const std::string& value); + Builder& setType(const std::string& value); + Builder& setType(const ::android::String16& value); + Builder& setXpanid(const std::vector<uint8_t>& value); + Builder& setXpanid(const uint8_t* valuePtr, int32_t valueLen); + Builder& setPanid(int32_t value); + Builder& setChannel(int32_t value); + Builder& setLowpanIdentity(const LowpanIdentity& value); + + Builder& setRssi(int32_t value); + Builder& setLqi(int32_t value); + Builder& setBeaconAddress(const std::vector<uint8_t>& value); + Builder& setBeaconAddress(const uint8_t* valuePtr, int32_t valueLen); + Builder& setFlag(int32_t value); + Builder& clearFlag(int32_t value); + + LowpanBeaconInfo build(void) const; +private: + LowpanIdentity::Builder mIdentityBuilder; + + int32_t mRssi; + int32_t mLqi; + std::vector<uint8_t> mBeaconAddress; + std::set<int32_t> mFlags; +}; + +} // namespace lowpan + +} // namespace net + +} // namespace android + +#endif // ANDROID_LOWPAN_BEACON_INFO_H diff --git a/libandroid_net_lowpan/include/android/net/lowpan/LowpanChannelInfo.h b/libandroid_net_lowpan/include/android/net/lowpan/LowpanChannelInfo.h new file mode 100644 index 0000000..e29820a --- /dev/null +++ b/libandroid_net_lowpan/include/android/net/lowpan/LowpanChannelInfo.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef ANDROID_LOWPAN_CHANNEL_INFO_H +#define ANDROID_LOWPAN_CHANNEL_INFO_H + +#include <binder/Parcelable.h> +#include <utils/String16.h> +#include <utils/StrongPointer.h> +#include <string> + +namespace android { + +namespace net { + +namespace lowpan { + +/* + * C++ implementation of the Java class android.net.lowpan.LowpanChannelInfo + */ +class LowpanChannelInfo : public Parcelable { +public: + LowpanChannelInfo() = default; + virtual ~LowpanChannelInfo() = default; + LowpanChannelInfo(const LowpanChannelInfo& x) = default; + + bool operator==(const LowpanChannelInfo& rhs); + bool operator!=(const LowpanChannelInfo& rhs) { return !(*this == rhs); } + +public: + // Overrides + status_t writeToParcel(Parcel* parcel) const override; + status_t readFromParcel(const Parcel* parcel) override; + +private: + // Data + int32_t mIndex; + std::string mName; + float mSpectrumCenterFrequency; + float mSpectrumBandwidth; + int32_t mMaxTxPower; + bool mIsMaskedByRegulatoryDomain; +}; + +} // namespace lowpan + +} // namespace net + +} // namespace android + +#endif // ANDROID_LOWPAN_CHANNEL_INFO_H diff --git a/libandroid_net_lowpan/include/android/net/lowpan/LowpanCredential.h b/libandroid_net_lowpan/include/android/net/lowpan/LowpanCredential.h new file mode 100644 index 0000000..c8ac90b --- /dev/null +++ b/libandroid_net_lowpan/include/android/net/lowpan/LowpanCredential.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#ifndef ANDROID_LOWPAN_CREDENTIAL_H +#define ANDROID_LOWPAN_CREDENTIAL_H + +#include <binder/Parcelable.h> +#include <utils/String16.h> +#include <utils/StrongPointer.h> + +namespace android { + +namespace net { + +namespace lowpan { + +/* + * C++ implementation of the Java class android.net.lowpan.LowpanCredential + */ +class LowpanCredential : public Parcelable { +public: + static const int32_t UNSPECIFIED_MASTER_KEY_INDEX = 0; + static const int MASTER_KEY_MAX_SIZE = 1048576; + + LowpanCredential(); + virtual ~LowpanCredential() = default; + LowpanCredential(const LowpanCredential& x) = default; + + static status_t initMasterKey(LowpanCredential& out, const std::vector<uint8_t>& masterKey, int32_t masterKeyIndex); + static status_t initMasterKey(LowpanCredential& out, const std::vector<uint8_t>& masterKey); + static status_t initMasterKey(LowpanCredential& out, const uint8_t* masterKeyBytes, int masterKeyLen, int32_t masterKeyIndex); + static status_t initMasterKey(LowpanCredential& out, const uint8_t* masterKeyBytes, int masterKeyLen); + + bool isMasterKey()const; + bool getMasterKey(std::vector<uint8_t>* masterKey)const; + bool getMasterKey(const uint8_t** masterKey, int* masterKeyLen)const; + int32_t getMasterKeyIndex()const; + + bool operator==(const LowpanCredential& rhs); + bool operator!=(const LowpanCredential& rhs) { return !(*this == rhs); } + +public: + // Overrides + status_t writeToParcel(Parcel* parcel) const override; + status_t readFromParcel(const Parcel* parcel) override; + +private: + // Data + std::vector<uint8_t> mMasterKey; + int32_t mMasterKeyIndex; +}; + +} // namespace lowpan + +} // namespace net + +} // namespace android + +#endif // ANDROID_LOWPAN_CREDENTIAL_H diff --git a/libandroid_net_lowpan/include/android/net/lowpan/LowpanIdentity.h b/libandroid_net_lowpan/include/android/net/lowpan/LowpanIdentity.h new file mode 100644 index 0000000..c82446a --- /dev/null +++ b/libandroid_net_lowpan/include/android/net/lowpan/LowpanIdentity.h @@ -0,0 +1,90 @@ +/* + * 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. + */ + +#ifndef ANDROID_LOWPAN_IDENTITY_H +#define ANDROID_LOWPAN_IDENTITY_H + +#include <binder/Parcelable.h> +#include <utils/String16.h> +#include <utils/StrongPointer.h> +#include <string> + +namespace android { + +namespace net { + +namespace lowpan { + +/* + * C++ implementation of the Java class android.net.lowpan.LowpanIdentity + */ +class LowpanIdentity : public Parcelable { +public: + class Builder; + static const int32_t UNSPECIFIED_PANID = 0xFFFFFFFF; + static const int32_t UNSPECIFIED_CHANNEL = -1; + + LowpanIdentity(); + virtual ~LowpanIdentity() = default; + LowpanIdentity(const LowpanIdentity& x) = default; + + bool operator==(const LowpanIdentity& rhs); + bool operator!=(const LowpanIdentity& rhs) { return !(*this == rhs); } + + bool getName(std::string* value) const; + bool getType(std::string* value) const; + bool getXpanid(std::vector<uint8_t>* value) const; + int32_t getPanid(void) const; + int32_t getChannel(void) const; + +public: + // Overrides + status_t writeToParcel(Parcel* parcel) const override; + status_t readFromParcel(const Parcel* parcel) override; + +private: + // Data + std::string mName; + std::string mType; + std::vector<uint8_t> mXpanid; + int32_t mPanid; + int32_t mChannel; +}; + +class LowpanIdentity::Builder { +public: + Builder(); + Builder& setName(const std::string& value); + Builder& setType(const std::string& value); + Builder& setType(const ::android::String16& value); + Builder& setXpanid(const std::vector<uint8_t>& value); + Builder& setXpanid(const uint8_t* valuePtr, int32_t valueLen); + Builder& setPanid(int32_t value); + Builder& setChannel(int32_t value); + Builder& setLowpanIdentity(const LowpanIdentity& value); + + LowpanIdentity build(void) const; +private: + LowpanIdentity mIdentity; +}; + +} // namespace lowpan + +} // namespace net + +} // namespace android + +#endif // ANDROID_LOWPAN_IDENTITY_H diff --git a/libandroid_net_lowpan/include/android/net/lowpan/LowpanProvision.h b/libandroid_net_lowpan/include/android/net/lowpan/LowpanProvision.h new file mode 100644 index 0000000..128994c --- /dev/null +++ b/libandroid_net_lowpan/include/android/net/lowpan/LowpanProvision.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#ifndef ANDROID_LOWPAN_PROVISION_H +#define ANDROID_LOWPAN_PROVISION_H + +#include <binder/Parcelable.h> +#include <utils/String16.h> +#include <utils/StrongPointer.h> + +#include "LowpanIdentity.h" +#include "LowpanCredential.h" + +namespace android { + +namespace net { + +namespace lowpan { + +/* + * C++ implementation of the Java class android.net.lowpan.LowpanProvision + */ +class LowpanProvision : public Parcelable { +public: + LowpanProvision() = default; + virtual ~LowpanProvision() = default; + LowpanProvision(const LowpanProvision& x) = default; + + bool operator==(const LowpanProvision& rhs); + bool operator!=(const LowpanProvision& rhs) { return !(*this == rhs); } + + LowpanProvision(const LowpanIdentity& identity, const LowpanCredential& credential); + LowpanProvision(const LowpanIdentity& identity); + + const LowpanIdentity* getLowpanIdentity() const; + const LowpanCredential* getLowpanCredential() const; + +public: + // Overrides + status_t writeToParcel(Parcel* parcel) const override; + status_t readFromParcel(const Parcel* parcel) override; + +private: + // Data + LowpanIdentity mIdentity; + LowpanCredential mCredential; + bool mHasCredential; +}; + +} // namespace lowpan + +} // namespace net + +} // namespace android + +#endif // ANDROID_LOWPAN_PROVISION_H diff --git a/libandroid_net_lowpan/tests/Android.mk b/libandroid_net_lowpan/tests/Android.mk new file mode 100644 index 0000000..ee44bc5 --- /dev/null +++ b/libandroid_net_lowpan/tests/Android.mk @@ -0,0 +1,114 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) + +# Make test APK +# ============================================================ +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +# This list is generated from the java source files in this module +# The list is a comma separated list of class names with * matching zero or more characters. +# Example: +# Input files: src/com/android/server/lowpan/Test.java src/com/android/server/lowpan/AnotherTest.java +# Generated exclude list: com.android.server.lowpan.Test*,com.android.server.lowpan.AnotherTest* + +# Filter all src files to just java files +local_java_files := $(filter %.java,$(LOCAL_SRC_FILES)) +# Transform java file names into full class names. +# This only works if the class name matches the file name and the directory structure +# matches the package. +local_classes := $(subst /,.,$(patsubst src/%.java,%,$(local_java_files))) +# Utility variables to allow replacing a space with a comma +comma:= , +empty:= +space:= $(empty) $(empty) +# Convert class name list to jacoco exclude list +# This appends a * to all classes and replace the space separators with commas. +# These patterns will match all classes in this module and their inner classes. +jacoco_exclude := $(subst $(space),$(comma),$(patsubst %,%*,$(local_classes))) + +jacoco_include := android.net.lowpan.* + +LOCAL_JACK_COVERAGE_INCLUDE_FILTER := $(jacoco_include) +LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-test \ + guava \ + mockito-target-minus-junit4 \ + frameworks-base-testutils \ + +LOCAL_JNI_SHARED_LIBRARIES += libframeworkslowpantestsjni +LOCAL_JNI_SHARED_LIBRARIES += libandroid_net_lowpan +LOCAL_JNI_SHARED_LIBRARIES += \ + libbacktrace \ + libbase \ + libbinder \ + libc++ \ + libcutils \ + liblog \ + liblzma \ + libnativehelper \ + libnetdaidl \ + libui \ + libunwind \ + libutils \ + libvndksupport \ + libcrypto \ + libhidl-gen-utils \ + libhidlbase \ + libhidltransport \ + libpackagelistparser \ + libpcre2 \ + libselinux \ + libtinyxml2 \ + libvintf \ + libhwbinder \ + android.hidl.token@1.0 + +LOCAL_JAVA_LIBRARIES := android.test.runner + +LOCAL_PACKAGE_NAME := FrameworksLowpanApiNativeTests +LOCAL_COMPATIBILITY_SUITE := device-tests + +LOCAL_CERTIFICATE := platform +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +include $(BUILD_PACKAGE) + +######################################################################### +# Build JNI Shared Library +######################################################################### + +LOCAL_PATH:= $(LOCAL_PATH)/jni + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_CFLAGS := -Wall -Wextra -Werror + +LOCAL_SRC_FILES := $(call all-cpp-files-under) + +LOCAL_SHARED_LIBRARIES += libandroid_net_lowpan +LOCAL_SHARED_LIBRARIES += libbinder +LOCAL_SHARED_LIBRARIES += liblog + +LOCAL_MODULE := libframeworkslowpantestsjni + +include $(BUILD_SHARED_LIBRARY) diff --git a/libandroid_net_lowpan/tests/AndroidManifest.xml b/libandroid_net_lowpan/tests/AndroidManifest.xml new file mode 100644 index 0000000..e9d71ef --- /dev/null +++ b/libandroid_net_lowpan/tests/AndroidManifest.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ 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 + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.net.lowpan.testnative"> + + <application> + <uses-library android:name="android.test.runner" /> + <activity android:label="LowpanTestDummyLabel" + android:name="LowpanTestDummyName"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + </application> + + <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="android.net.lowpan.testnative" + android:label="Frameworks LoWPAN API Native Tests"> + </instrumentation> + +</manifest> diff --git a/libandroid_net_lowpan/tests/AndroidTest.xml b/libandroid_net_lowpan/tests/AndroidTest.xml new file mode 100644 index 0000000..55e5e7f --- /dev/null +++ b/libandroid_net_lowpan/tests/AndroidTest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Runs Frameworks LoWPAN API Native Tests."> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="FrameworksLowpanApiTests.apk" /> + </target_preparer> + + <option name="test-suite-tag" value="apct" /> + <option name="test-tag" value="FrameworksLowpanApiNativeTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="android.net.lowpan.testnative" /> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" /> + </test> +</configuration> diff --git a/libandroid_net_lowpan/tests/README.md b/libandroid_net_lowpan/tests/README.md new file mode 100644 index 0000000..0711cc1 --- /dev/null +++ b/libandroid_net_lowpan/tests/README.md @@ -0,0 +1,50 @@ +# libandroid_net_lowpan Unit Tests +This package contains unit tests for libandroid_net_lowpan based on the +[Android Testing Support Library](http://developer.android.com/tools/testing-support-library/index.html). +The test cases are built using the [JUnit](http://junit.org/) and [Mockito](http://mockito.org/) +libraries. + +## Running Tests +The easiest way to run tests is simply run + +``` +frameworks/opt/net/lowpan/libandroid_net_lowpan/tests/runtests.sh +``` + +`runtests.sh` will build the test project and all of its dependencies and push the APK to the +connected device. It will then run the tests on the device. + +To pick up changes in framework/base, you will need to: +1. rebuild the framework library 'make -j32' +2. sync over the updated library to the device 'adb sync' +3. restart framework on the device 'adb shell stop' then 'adb shell start' + +To enable syncing data to the device for first time after clean reflash: +1. adb disable-verity +2. adb reboot +3. adb remount + +See below for a few examples of options to limit which tests are run. +See the +[AndroidJUnitRunner Documentation](https://developer.android.com/reference/android/support/test/runner/AndroidJUnitRunner.html) +for more details on the supported options. + +``` +runtests.sh -e package android.net.lowpan +runtests.sh -e class android.net.lowpan.LowpanIdentityTest +``` + +If you manually build and push the test APK to the device you can run tests using + +``` +adb shell am instrument -w 'android.net.lowpan.testnative/android.support.test.runner.AndroidJUnitRunner' +``` + +## Adding Tests +Tests can be added by adding classes to the src directory. JUnit4 style test cases can +be written by simply annotating test methods with `org.junit.Test`. + +## Debugging Tests +If you are trying to debug why tests are not doing what you expected, you can add android log +statements and use logcat to view them. The beginning and end of every tests is automatically logged +with the tag `TestRunner`. diff --git a/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanBeaconInfoTest.java b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanBeaconInfoTest.java new file mode 100644 index 0000000..6dbb3ed --- /dev/null +++ b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanBeaconInfoTest.java @@ -0,0 +1,134 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertArrayEquals; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class LowpanBeaconInfoTest { + + static { + System.loadLibrary("frameworkslowpantestsjni"); + } + + private static native byte[] readAndWriteNative(byte[] inParcel); + + public void testNativeParcelUnparcel(LowpanBeaconInfo original) { + byte[] inParcel = marshall(original); + byte[] outParcel = readAndWriteNative(inParcel); + LowpanBeaconInfo roundTrip = unmarshall(outParcel); + + assertEquals(original, roundTrip); + assertArrayEquals(inParcel, outParcel); + } + + @Test + public void testNativeParcelUnparcel() { + testNativeParcelUnparcel( + new LowpanBeaconInfo.Builder() + .setName("TestNet1") + .setPanid(0x1234) + .setXpanid( + new byte[] { + (byte) 0x00, + (byte) 0x11, + (byte) 0x22, + (byte) 0x33, + (byte) 0x44, + (byte) 0x55, + (byte) 0x66, + (byte) 0x77 + }) + .setType(LowpanInterface.NETWORK_TYPE_THREAD_V1) + .setChannel(15) + .setBeaconAddress( + new byte[] { + (byte) 0x88, + (byte) 0x99, + (byte) 0xaa, + (byte) 0xbb, + (byte) 0xcc, + (byte) 0xdd, + (byte) 0xee, + (byte) 0xff + }) + .build()); + + testNativeParcelUnparcel( + new LowpanBeaconInfo.Builder() + .setName("TestNet2") + .setPanid(0x5678) + .setXpanid( + new byte[] { + (byte) 0x88, + (byte) 0x99, + (byte) 0xaa, + (byte) 0xbb, + (byte) 0xcc, + (byte) 0xdd, + (byte) 0xee, + (byte) 0xff + }) + .setType("bork-bork-bork") + .setChannel(16) + .setBeaconAddress( + new byte[] { + (byte) 0x00, + (byte) 0x11, + (byte) 0x22, + (byte) 0x33, + (byte) 0x44, + (byte) 0x55, + (byte) 0x66, + (byte) 0x77 + }) + .setFlag(LowpanBeaconInfo.FLAG_CAN_ASSIST) + .build()); + } + + /** + * Write a {@link LowpanBeaconInfo} into an empty parcel and return the underlying data. + * + * @see unmarshall(byte[]) + */ + private static byte[] marshall(LowpanBeaconInfo addr) { + Parcel p = Parcel.obtain(); + addr.writeToParcel(p, /* flags */ 0); + p.setDataPosition(0); + return p.marshall(); + } + + /** + * Read raw bytes into a parcel, and read a {@link LowpanBeaconInfo} back out of them. + * + * @see marshall(LowpanBeaconInfo) + */ + private static LowpanBeaconInfo unmarshall(byte[] data) { + Parcel p = Parcel.obtain(); + p.unmarshall(data, 0, data.length); + p.setDataPosition(0); + return LowpanBeaconInfo.CREATOR.createFromParcel(p); + } +} diff --git a/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanChannelInfoTest.java b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanChannelInfoTest.java new file mode 100644 index 0000000..eac4398 --- /dev/null +++ b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanChannelInfoTest.java @@ -0,0 +1,78 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertArrayEquals; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class LowpanChannelInfoTest { + + static { + System.loadLibrary("frameworkslowpantestsjni"); + } + + private static native byte[] readAndWriteNative(byte[] inParcel); + + public void testNativeParcelUnparcel(LowpanChannelInfo original) { + byte[] inParcel = marshall(original); + byte[] outParcel = readAndWriteNative(inParcel); + LowpanChannelInfo roundTrip = unmarshall(outParcel); + + assertEquals(original, roundTrip); + assertArrayEquals(inParcel, outParcel); + } + + @Test + public void testNativeParcelUnparcel() { + int i; + for (i = 1; i < 26; i++) { + testNativeParcelUnparcel(LowpanChannelInfo.getChannelInfoForIeee802154Page0(i)); + } + } + + /** + * Write a {@link LowpanChannelInfo} into an empty parcel and return the underlying data. + * + * @see unmarshall(byte[]) + */ + private static byte[] marshall(LowpanChannelInfo addr) { + Parcel p = Parcel.obtain(); + addr.writeToParcel(p, /* flags */ 0); + p.setDataPosition(0); + return p.marshall(); + } + + /** + * Read raw bytes into a parcel, and read a {@link LowpanChannelInfo} back out of them. + * + * @see marshall(LowpanChannelInfo) + */ + private static LowpanChannelInfo unmarshall(byte[] data) { + Parcel p = Parcel.obtain(); + p.unmarshall(data, 0, data.length); + p.setDataPosition(0); + return LowpanChannelInfo.CREATOR.createFromParcel(p); + } +} diff --git a/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanCredentialTest.java b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanCredentialTest.java new file mode 100644 index 0000000..26e3334 --- /dev/null +++ b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanCredentialTest.java @@ -0,0 +1,92 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertArrayEquals; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class LowpanCredentialTest { + + static { + System.loadLibrary("frameworkslowpantestsjni"); + } + + private static native byte[] readAndWriteNative(byte[] inParcel); + + public void testNativeParcelUnparcel(LowpanCredential original) { + byte[] inParcel = marshall(original); + byte[] outParcel = readAndWriteNative(inParcel); + LowpanCredential roundTrip = unmarshall(outParcel); + + assertEquals(original, roundTrip); + assertArrayEquals(inParcel, outParcel); + } + + @Test + public void testNativeParcelUnparcel() { + testNativeParcelUnparcel( + LowpanCredential.createMasterKey( + new byte[] { + (byte) 0x88, + (byte) 0x99, + (byte) 0xaa, + (byte) 0xbb, + (byte) 0xcc, + (byte) 0xdd, + (byte) 0xee, + (byte) 0xff + })); + testNativeParcelUnparcel( + LowpanCredential.createMasterKey( + new byte[] { + (byte) 0x88, (byte) 0x99, (byte) 0xaa, (byte) 0xbb, (byte) 0xcc + }, + 15)); + } + + /** + * Write a {@link LowpanCredential} into an empty parcel and return the underlying data. + * + * @see unmarshall(byte[]) + */ + private static byte[] marshall(LowpanCredential addr) { + Parcel p = Parcel.obtain(); + addr.writeToParcel(p, /* flags */ 0); + p.setDataPosition(0); + return p.marshall(); + } + + /** + * Read raw bytes into a parcel, and read a {@link LowpanCredential} back out of them. + * + * @see marshall(LowpanCredential) + */ + private static LowpanCredential unmarshall(byte[] data) { + Parcel p = Parcel.obtain(); + p.unmarshall(data, 0, data.length); + p.setDataPosition(0); + return LowpanCredential.CREATOR.createFromParcel(p); + } +} diff --git a/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanIdentityTest.java b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanIdentityTest.java new file mode 100644 index 0000000..1242e55 --- /dev/null +++ b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanIdentityTest.java @@ -0,0 +1,199 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class LowpanIdentityTest { + + static { + System.loadLibrary("frameworkslowpantestsjni"); + } + + private static native byte[] readAndWriteNative(byte[] inParcel); + + public void testNativeParcelUnparcel(LowpanIdentity original) { + byte[] inParcel = marshall(original); + byte[] outParcel = readAndWriteNative(inParcel); + LowpanIdentity roundTrip = unmarshall(outParcel); + + assertEquals(original, roundTrip); + assertEquals(original.hashCode(), roundTrip.hashCode()); + assertEquals(original.getName(), roundTrip.getName()); + assertArrayEquals(inParcel, outParcel); + } + + @Test + public void testNativeParcelUnparcel1() { + testNativeParcelUnparcel( + new LowpanIdentity.Builder() + .setName("TestNet1") + .setPanid(0x1234) + .setXpanid( + new byte[] { + (byte) 0x00, + (byte) 0x11, + (byte) 0x22, + (byte) 0x33, + (byte) 0x44, + (byte) 0x55, + (byte) 0x66, + (byte) 0x77 + }) + .setType(LowpanInterface.NETWORK_TYPE_THREAD_V1) + .setChannel(15) + .build()); + } + + @Test + public void testNativeParcelUnparcel2() { + testNativeParcelUnparcel( + new LowpanIdentity.Builder() + .setName("TestNet2") + .setPanid(0x5678) + .setXpanid( + new byte[] { + (byte) 0x88, + (byte) 0x99, + (byte) 0xaa, + (byte) 0xbb, + (byte) 0xcc, + (byte) 0xdd, + (byte) 0xee, + (byte) 0xff + }) + .setType("bork-bork-bork") + .setChannel(16) + .build()); + } + + @Test + public void testNativeParcelUnparcel3() { + testNativeParcelUnparcel(new LowpanIdentity.Builder().setName("TestNet3").build()); + } + + @Test + public void testNativeParcelUnparcel4() { + testNativeParcelUnparcel(new LowpanIdentity.Builder().build()); + } + + @Test + public void testNativeParcelUnparcel5() { + testNativeParcelUnparcel( + new LowpanIdentity.Builder() + .setRawName( + new byte[] { + (byte) 0x66, + (byte) 0x6F, + (byte) 0x6F, + (byte) 0xC2, + (byte) 0xAD, + (byte) 0xCD, + (byte) 0x8F, + (byte) 0xE1, + (byte) 0xA0, + (byte) 0x86, + (byte) 0xE1, + (byte) 0xA0, + (byte) 0x8B + }) + .build()); + } + + @Test + public void testStringPrep1() { + LowpanIdentity identity = + new LowpanIdentity.Builder() + .setRawName( + new byte[] { + (byte) 0x66, + (byte) 0x6F, + (byte) 0x6F, + (byte) 0x20, + (byte) 0xC2, + (byte) 0xAD, + (byte) 0xCD, + (byte) 0x8F, + (byte) 0xE1, + (byte) 0xA0, + (byte) 0x86, + (byte) 0xE1, + (byte) 0xA0, + (byte) 0x8B + }) + .build(); + + assertFalse(identity.isNameValid()); + } + + @Test + public void testStringPrep2() { + LowpanIdentity identity = + new LowpanIdentity.Builder() + .setRawName( + new byte[] { + (byte) 0x66, (byte) 0x6F, (byte) 0x6F, (byte) 0x20, (byte) 0x6F + }) + .build(); + + assertEquals("foo o", identity.getName()); + assertTrue(identity.isNameValid()); + } + + @Test + public void testStringPrep3() { + LowpanIdentity identity = new LowpanIdentity.Builder().setName("foo o").build(); + + assertTrue(identity.isNameValid()); + assertEquals("foo o", identity.getName()); + } + + /** + * Write a {@link LowpanIdentity} into an empty parcel and return the underlying data. + * + * @see unmarshall(byte[]) + */ + private static byte[] marshall(LowpanIdentity addr) { + Parcel p = Parcel.obtain(); + addr.writeToParcel(p, /* flags */ 0); + p.setDataPosition(0); + return p.marshall(); + } + + /** + * Read raw bytes into a parcel, and read a {@link LowpanIdentity} back out of them. + * + * @see marshall(LowpanIdentity) + */ + private static LowpanIdentity unmarshall(byte[] data) { + Parcel p = Parcel.obtain(); + p.unmarshall(data, 0, data.length); + p.setDataPosition(0); + return LowpanIdentity.CREATOR.createFromParcel(p); + } +} diff --git a/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanProvisionTest.java b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanProvisionTest.java new file mode 100644 index 0000000..3be2f26 --- /dev/null +++ b/libandroid_net_lowpan/tests/java/android/net/lowpan/LowpanProvisionTest.java @@ -0,0 +1,128 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertArrayEquals; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class LowpanProvisionTest { + + static { + System.loadLibrary("frameworkslowpantestsjni"); + } + + private static native byte[] readAndWriteNative(byte[] inParcel); + + public void testNativeParcelUnparcel(LowpanProvision original) { + byte[] inParcel = marshall(original); + byte[] outParcel = readAndWriteNative(inParcel); + LowpanProvision roundTrip = unmarshall(outParcel); + + assertEquals(original, roundTrip); + assertArrayEquals(inParcel, outParcel); + } + + @Test + public void testNativeParcelUnparcel() { + testNativeParcelUnparcel( + new LowpanProvision.Builder() + .setLowpanIdentity( + new LowpanIdentity.Builder() + .setName("TestNet1") + .setPanid(0x1234) + .setXpanid( + new byte[] { + (byte) 0x00, + (byte) 0x11, + (byte) 0x22, + (byte) 0x33, + (byte) 0x44, + (byte) 0x55, + (byte) 0x66, + (byte) 0x77 + }) + .setType(LowpanInterface.NETWORK_TYPE_THREAD_V1) + .setChannel(15) + .build()) + .build()); + testNativeParcelUnparcel( + new LowpanProvision.Builder() + .setLowpanIdentity( + new LowpanIdentity.Builder() + .setName("TestNet2") + .setPanid(0x5678) + .setXpanid( + new byte[] { + (byte) 0x88, + (byte) 0x99, + (byte) 0xaa, + (byte) 0xbb, + (byte) 0xcc, + (byte) 0xdd, + (byte) 0xee, + (byte) 0xff + }) + .setType("bork-bork-bork") + .setChannel(16) + .build()) + .setLowpanCredential( + LowpanCredential.createMasterKey( + new byte[] { + (byte) 0x88, + (byte) 0x99, + (byte) 0xaa, + (byte) 0xbb, + (byte) 0xcc, + (byte) 0xdd, + (byte) 0xee, + (byte) 0xff + })) + .build()); + } + + /** + * Write a {@link LowpanProvision} into an empty parcel and return the underlying data. + * + * @see unmarshall(byte[]) + */ + private static byte[] marshall(LowpanProvision addr) { + Parcel p = Parcel.obtain(); + addr.writeToParcel(p, /* flags */ 0); + p.setDataPosition(0); + return p.marshall(); + } + + /** + * Read raw bytes into a parcel, and read a {@link LowpanProvision} back out of them. + * + * @see marshall(LowpanProvision) + */ + private static LowpanProvision unmarshall(byte[] data) { + Parcel p = Parcel.obtain(); + p.unmarshall(data, 0, data.length); + p.setDataPosition(0); + return LowpanProvision.CREATOR.createFromParcel(p); + } +} diff --git a/libandroid_net_lowpan/tests/jni/LowpanBeaconInfoTest.cpp b/libandroid_net_lowpan/tests/jni/LowpanBeaconInfoTest.cpp new file mode 100644 index 0000000..efc5f18 --- /dev/null +++ b/libandroid_net_lowpan/tests/jni/LowpanBeaconInfoTest.cpp @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#include <memory> + +#include <binder/Parcel.h> + +#include "LowpanBeaconInfoTest.h" + +using android::net::lowpan::LowpanBeaconInfo; + +/** + * Reads exactly one LowpanBeaconInfo from 'parcelData' assuming that it is a Parcel. Any bytes afterward + * are ignored. + */ +static LowpanBeaconInfo unmarshall(JNIEnv* env, jbyteArray parcelData) { + const int length = env->GetArrayLength(parcelData); + + std::unique_ptr<uint8_t> bytes(new uint8_t[length]); + env->GetByteArrayRegion(parcelData, 0, length, reinterpret_cast<jbyte*>(bytes.get())); + + android::Parcel p; + p.setData(bytes.get(), length); + + LowpanBeaconInfo value; + value.readFromParcel(&p); + return value; +} + +/** + * Creates a Java byte[] array and writes the contents of 'addr' to it as a Parcel containing + * exactly one object. + * + * Every LowpanBeaconInfo maps to a unique parcel object, so both 'marshall(e, unmarshall(e, x))' and + * 'unmarshall(e, marshall(e, x))' should be fixed points. + */ +static jbyteArray marshall(JNIEnv* env, const LowpanBeaconInfo& addr) { + android::Parcel p; + addr.writeToParcel(&p); + const int length = p.dataSize(); + + jbyteArray parcelData = env->NewByteArray(length); + env->SetByteArrayRegion(parcelData, 0, length, reinterpret_cast<const jbyte*>(p.data())); + + return parcelData; +} + +extern "C" +JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanBeaconInfoTest_readAndWriteNative(JNIEnv* env, jclass, + jbyteArray inParcel) { + const LowpanBeaconInfo value = unmarshall(env, inParcel); + return marshall(env, value); +} diff --git a/libandroid_net_lowpan/tests/jni/LowpanBeaconInfoTest.h b/libandroid_net_lowpan/tests/jni/LowpanBeaconInfoTest.h new file mode 100644 index 0000000..1ba8eaf --- /dev/null +++ b/libandroid_net_lowpan/tests/jni/LowpanBeaconInfoTest.h @@ -0,0 +1,27 @@ +/* + * 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. + */ + +#ifndef _ANDROID_NET_LOWPANBEACONINFOTEST_H_ +#define _ANDROID_NET_LOWPANBEACONINFOTEST_H_ + +#include <jni.h> +#include <android/net/lowpan/LowpanBeaconInfo.h> + +extern "C" +JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanBeaconInfoTest_readAndWriteNative(JNIEnv* env, jclass, + jbyteArray inParcel); + +#endif // _ANDROID_NET_LOWPANBEACONINFOTEST_H_ diff --git a/libandroid_net_lowpan/tests/jni/LowpanChannelInfoTest.cpp b/libandroid_net_lowpan/tests/jni/LowpanChannelInfoTest.cpp new file mode 100644 index 0000000..03bb72a --- /dev/null +++ b/libandroid_net_lowpan/tests/jni/LowpanChannelInfoTest.cpp @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#include <memory> + +#include <binder/Parcel.h> + +#include "LowpanChannelInfoTest.h" + +using android::net::lowpan::LowpanChannelInfo; + +/** + * Reads exactly one LowpanChannelInfo from 'parcelData' assuming that it is a Parcel. Any bytes afterward + * are ignored. + */ +static LowpanChannelInfo unmarshall(JNIEnv* env, jbyteArray parcelData) { + const int length = env->GetArrayLength(parcelData); + + std::unique_ptr<uint8_t> bytes(new uint8_t[length]); + env->GetByteArrayRegion(parcelData, 0, length, reinterpret_cast<jbyte*>(bytes.get())); + + android::Parcel p; + p.setData(bytes.get(), length); + + LowpanChannelInfo value; + value.readFromParcel(&p); + return value; +} + +/** + * Creates a Java byte[] array and writes the contents of 'addr' to it as a Parcel containing + * exactly one object. + * + * Every LowpanChannelInfo maps to a unique parcel object, so both 'marshall(e, unmarshall(e, x))' and + * 'unmarshall(e, marshall(e, x))' should be fixed points. + */ +static jbyteArray marshall(JNIEnv* env, const LowpanChannelInfo& addr) { + android::Parcel p; + addr.writeToParcel(&p); + const int length = p.dataSize(); + + jbyteArray parcelData = env->NewByteArray(length); + env->SetByteArrayRegion(parcelData, 0, length, reinterpret_cast<const jbyte*>(p.data())); + + return parcelData; +} + +extern "C" +JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanChannelInfoTest_readAndWriteNative(JNIEnv* env, jclass, + jbyteArray inParcel) { + const LowpanChannelInfo value = unmarshall(env, inParcel); + return marshall(env, value); +} diff --git a/libandroid_net_lowpan/tests/jni/LowpanChannelInfoTest.h b/libandroid_net_lowpan/tests/jni/LowpanChannelInfoTest.h new file mode 100644 index 0000000..3b29a90 --- /dev/null +++ b/libandroid_net_lowpan/tests/jni/LowpanChannelInfoTest.h @@ -0,0 +1,27 @@ +/* + * 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. + */ + +#ifndef _ANDROID_NET_LOWPANCHANNELINFOTEST_H_ +#define _ANDROID_NET_LOWPANCHANNELINFOTEST_H_ + +#include <jni.h> +#include <android/net/lowpan/LowpanChannelInfo.h> + +extern "C" +JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanChannelInfoTest_readAndWriteNative(JNIEnv* env, jclass, + jbyteArray inParcel); + +#endif // _ANDROID_NET_LOWPANCHANNELINFOTEST_H_ diff --git a/libandroid_net_lowpan/tests/jni/LowpanCredentialTest.cpp b/libandroid_net_lowpan/tests/jni/LowpanCredentialTest.cpp new file mode 100644 index 0000000..fc860b2 --- /dev/null +++ b/libandroid_net_lowpan/tests/jni/LowpanCredentialTest.cpp @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#include <memory> + +#include <binder/Parcel.h> + +#include "LowpanCredentialTest.h" + +using android::net::lowpan::LowpanCredential; + +/** + * Reads exactly one LowpanCredential from 'parcelData' assuming that it is a Parcel. Any bytes afterward + * are ignored. + */ +static LowpanCredential unmarshall(JNIEnv* env, jbyteArray parcelData) { + const int length = env->GetArrayLength(parcelData); + + std::unique_ptr<uint8_t> bytes(new uint8_t[length]); + env->GetByteArrayRegion(parcelData, 0, length, reinterpret_cast<jbyte*>(bytes.get())); + + android::Parcel p; + p.setData(bytes.get(), length); + + LowpanCredential value; + value.readFromParcel(&p); + return value; +} + +/** + * Creates a Java byte[] array and writes the contents of 'addr' to it as a Parcel containing + * exactly one object. + * + * Every LowpanCredential maps to a unique parcel object, so both 'marshall(e, unmarshall(e, x))' and + * 'unmarshall(e, marshall(e, x))' should be fixed points. + */ +static jbyteArray marshall(JNIEnv* env, const LowpanCredential& addr) { + android::Parcel p; + addr.writeToParcel(&p); + const int length = p.dataSize(); + + jbyteArray parcelData = env->NewByteArray(length); + env->SetByteArrayRegion(parcelData, 0, length, reinterpret_cast<const jbyte*>(p.data())); + + return parcelData; +} + +extern "C" +JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanCredentialTest_readAndWriteNative(JNIEnv* env, jclass, + jbyteArray inParcel) { + const LowpanCredential value = unmarshall(env, inParcel); + return marshall(env, value); +} diff --git a/libandroid_net_lowpan/tests/jni/LowpanCredentialTest.h b/libandroid_net_lowpan/tests/jni/LowpanCredentialTest.h new file mode 100644 index 0000000..9dd9889 --- /dev/null +++ b/libandroid_net_lowpan/tests/jni/LowpanCredentialTest.h @@ -0,0 +1,27 @@ +/* + * 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. + */ + +#ifndef _ANDROID_NET_LOWPANCREDENTIALTEST_H_ +#define _ANDROID_NET_LOWPANCREDENTIALTEST_H_ + +#include <jni.h> +#include <android/net/lowpan/LowpanCredential.h> + +extern "C" +JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanCredentialTest_readAndWriteNative(JNIEnv* env, jclass, + jbyteArray inParcel); + +#endif // _ANDROID_NET_LOWPANCREDENTIALTEST_H_ diff --git a/libandroid_net_lowpan/tests/jni/LowpanIdentityTest.cpp b/libandroid_net_lowpan/tests/jni/LowpanIdentityTest.cpp new file mode 100644 index 0000000..1a9ad33 --- /dev/null +++ b/libandroid_net_lowpan/tests/jni/LowpanIdentityTest.cpp @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#include <memory> + +#include <binder/Parcel.h> + +#include "LowpanIdentityTest.h" + +using android::net::lowpan::LowpanIdentity; + +/** + * Reads exactly one LowpanIdentity from 'parcelData' assuming that it is a Parcel. Any bytes afterward + * are ignored. + */ +static LowpanIdentity unmarshall(JNIEnv* env, jbyteArray parcelData) { + const int length = env->GetArrayLength(parcelData); + + std::unique_ptr<uint8_t> bytes(new uint8_t[length]); + env->GetByteArrayRegion(parcelData, 0, length, reinterpret_cast<jbyte*>(bytes.get())); + + android::Parcel p; + p.setData(bytes.get(), length); + + LowpanIdentity value; + value.readFromParcel(&p); + return value; +} + +/** + * Creates a Java byte[] array and writes the contents of 'addr' to it as a Parcel containing + * exactly one object. + * + * Every LowpanIdentity maps to a unique parcel object, so both 'marshall(e, unmarshall(e, x))' and + * 'unmarshall(e, marshall(e, x))' should be fixed points. + */ +static jbyteArray marshall(JNIEnv* env, const LowpanIdentity& addr) { + android::Parcel p; + addr.writeToParcel(&p); + const int length = p.dataSize(); + + jbyteArray parcelData = env->NewByteArray(length); + env->SetByteArrayRegion(parcelData, 0, length, reinterpret_cast<const jbyte*>(p.data())); + + return parcelData; +} + +extern "C" +JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanIdentityTest_readAndWriteNative(JNIEnv* env, jclass, + jbyteArray inParcel) { + const LowpanIdentity value = unmarshall(env, inParcel); + return marshall(env, value); +} diff --git a/libandroid_net_lowpan/tests/jni/LowpanIdentityTest.h b/libandroid_net_lowpan/tests/jni/LowpanIdentityTest.h new file mode 100644 index 0000000..1d2c465 --- /dev/null +++ b/libandroid_net_lowpan/tests/jni/LowpanIdentityTest.h @@ -0,0 +1,27 @@ +/* + * 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. + */ + +#ifndef _ANDROID_NET_LOWPANIDENTITYTEST_H_ +#define _ANDROID_NET_LOWPANIDENTITYTEST_H_ + +#include <jni.h> +#include <android/net/lowpan/LowpanIdentity.h> + +extern "C" +JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanIdentityTest_readAndWriteNative(JNIEnv* env, jclass, + jbyteArray inParcel); + +#endif // _ANDROID_NET_LOWPANIDENTITYTEST_H_ diff --git a/libandroid_net_lowpan/tests/jni/LowpanProvisionTest.cpp b/libandroid_net_lowpan/tests/jni/LowpanProvisionTest.cpp new file mode 100644 index 0000000..95f64b6 --- /dev/null +++ b/libandroid_net_lowpan/tests/jni/LowpanProvisionTest.cpp @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#include <memory> + +#include <binder/Parcel.h> + +#include "LowpanProvisionTest.h" + +using android::net::lowpan::LowpanProvision; + +/** + * Reads exactly one LowpanProvision from 'parcelData' assuming that it is a Parcel. Any bytes afterward + * are ignored. + */ +static LowpanProvision unmarshall(JNIEnv* env, jbyteArray parcelData) { + const int length = env->GetArrayLength(parcelData); + + std::unique_ptr<uint8_t> bytes(new uint8_t[length]); + env->GetByteArrayRegion(parcelData, 0, length, reinterpret_cast<jbyte*>(bytes.get())); + + android::Parcel p; + p.setData(bytes.get(), length); + + LowpanProvision value; + value.readFromParcel(&p); + return value; +} + +/** + * Creates a Java byte[] array and writes the contents of 'addr' to it as a Parcel containing + * exactly one object. + * + * Every LowpanProvision maps to a unique parcel object, so both 'marshall(e, unmarshall(e, x))' and + * 'unmarshall(e, marshall(e, x))' should be fixed points. + */ +static jbyteArray marshall(JNIEnv* env, const LowpanProvision& addr) { + android::Parcel p; + addr.writeToParcel(&p); + const int length = p.dataSize(); + + jbyteArray parcelData = env->NewByteArray(length); + env->SetByteArrayRegion(parcelData, 0, length, reinterpret_cast<const jbyte*>(p.data())); + + return parcelData; +} + +extern "C" +JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanProvisionTest_readAndWriteNative(JNIEnv* env, jclass, + jbyteArray inParcel) { + const LowpanProvision value = unmarshall(env, inParcel); + return marshall(env, value); +} diff --git a/libandroid_net_lowpan/tests/jni/LowpanProvisionTest.h b/libandroid_net_lowpan/tests/jni/LowpanProvisionTest.h new file mode 100644 index 0000000..49211b5 --- /dev/null +++ b/libandroid_net_lowpan/tests/jni/LowpanProvisionTest.h @@ -0,0 +1,27 @@ +/* + * 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. + */ + +#ifndef _ANDROID_NET_LOWPANPROVISIONTEST_H_ +#define _ANDROID_NET_LOWPANPROVISIONTEST_H_ + +#include <jni.h> +#include <android/net/lowpan/LowpanProvision.h> + +extern "C" +JNIEXPORT jbyteArray Java_android_net_lowpan_LowpanProvisionTest_readAndWriteNative(JNIEnv* env, jclass, + jbyteArray inParcel); + +#endif // _ANDROID_NET_LOWPANPROVISIONTEST_H_ diff --git a/libandroid_net_lowpan/tests/runtests.sh b/libandroid_net_lowpan/tests/runtests.sh new file mode 100755 index 0000000..c3cc4da --- /dev/null +++ b/libandroid_net_lowpan/tests/runtests.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +if [ -z $ANDROID_BUILD_TOP ]; then + echo "You need to source and lunch before you can use this script" + exit 1 +fi + +echo "Running tests" + +set -e # fail early + +echo "+ mmma -j32 $ANDROID_BUILD_TOP/frameworks/opt/net/lowpan/libandroid_net_lowpan/tests" +# NOTE Don't actually run the command above since this shell doesn't inherit functions from the +# caller. +make -j32 -C $ANDROID_BUILD_TOP -f build/core/main.mk MODULES-IN-frameworks-opt-net-lowpan-libandroid_net_lowpan-tests + +set -x # print commands + +adb root +adb wait-for-device + +adb install -r -g "$OUT/data/app/FrameworksLowpanApiNativeTests/FrameworksLowpanApiNativeTests.apk" + +adb shell am instrument -w "$@" 'android.net.lowpan.testnative/android.support.test.runner.AndroidJUnitRunner' diff --git a/service/Android.mk b/service/Android.mk new file mode 100644 index 0000000..ff96a6c --- /dev/null +++ b/service/Android.mk @@ -0,0 +1,29 @@ +# +# 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. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := lowpan-service +LOCAL_MODULE_TAGS := +LOCAL_REQUIRED_MODULES := services +LOCAL_JAVA_LIBRARIES := services +LOCAL_SRC_FILES += java/com/android/server/lowpan/LowpanInterfaceTracker.java +LOCAL_SRC_FILES += java/com/android/server/lowpan/LowpanService.java +LOCAL_SRC_FILES += java/com/android/server/lowpan/LowpanServiceImpl.java +include $(BUILD_JAVA_LIBRARY) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/service/java/com/android/server/lowpan/LowpanInterfaceTracker.java b/service/java/com/android/server/lowpan/LowpanInterfaceTracker.java new file mode 100644 index 0000000..89b058c --- /dev/null +++ b/service/java/com/android/server/lowpan/LowpanInterfaceTracker.java @@ -0,0 +1,637 @@ +/* + * 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 com.android.server.lowpan; + +import android.annotation.NonNull; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.NetworkAgent; +import android.net.NetworkCapabilities; +import android.net.NetworkFactory; +import android.net.NetworkInfo; +import android.net.NetworkInfo.DetailedState; +import android.net.ip.IpManager; +import android.net.ip.IpManager.InitialConfiguration; +import android.net.ip.IpManager.ProvisioningConfiguration; +import android.net.lowpan.ILowpanInterface; +import android.net.lowpan.LowpanException; +import android.net.lowpan.LowpanInterface; +import android.net.lowpan.LowpanRuntimeException; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.util.Log; +import com.android.internal.util.HexDump; +import com.android.internal.util.Protocol; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; + +/** Tracks connectivity of a LoWPAN interface. */ +class LowpanInterfaceTracker extends StateMachine { + + // Misc Constants + + /** Network type string for NetworkInfo */ + private static final String NETWORK_TYPE = "LoWPAN"; + + /** Tag used for logging */ + private static final String TAG = "LowpanInterfaceTracker"; + + /** + * Maximum network score for LoWPAN networks. + * + * <p>TODO: Research if 30 is an appropriate value. + */ + private static final int NETWORK_SCORE = 30; + + /** Internal debugging flag. */ + private static final boolean DBG = true; + + /** Number of state machine log records. */ + public static final short NUM_LOG_RECS_NORMAL = 100; + + // Message Code Enumeration Constants + + /** The base for LoWPAN message codes */ + static final int BASE = Protocol.BASE_LOWPAN; + + static final int CMD_REGISTER = BASE + 1; + static final int CMD_UNREGISTER = BASE + 2; + static final int CMD_START_NETWORK = BASE + 3; + static final int CMD_STOP_NETWORK = BASE + 4; + static final int CMD_STATE_CHANGE = BASE + 5; + static final int CMD_LINK_PROPERTIES_CHANGE = BASE + 6; + static final int CMD_UNWANTED = BASE + 7; + static final int CMD_PROVISIONING_SUCCESS = BASE + 8; + static final int CMD_PROVISIONING_FAILURE = BASE + 9; + + // Services and interfaces + + ILowpanInterface mILowpanInterface; + private LowpanInterface mLowpanInterface; + private NetworkAgent mNetworkAgent; + private NetworkFactory mNetworkFactory; + private final IpManager mIpManager; + private final IpManager.Callback mIpManagerCallback = new IpManagerCallback(); + + // Instance Variables + + private String mInterfaceName; + private String mHwAddr; + private Context mContext; + private NetworkInfo mNetworkInfo; + private LinkProperties mLinkProperties; + private final NetworkCapabilities mNetworkCapabilities = new NetworkCapabilities(); + private String mState = ""; + + // State machine state instances + + final DefaultState mDefaultState = new DefaultState(); + final NormalState mNormalState = new NormalState(); + final InitState mInitState = new InitState(); + final OfflineState mOfflineState = new OfflineState(); + final CommissioningState mCommissioningState = new CommissioningState(); + final AttachingState mAttachingState = new AttachingState(); + final AttachedState mAttachedState = new AttachedState(); + final ObtainingIpState mObtainingIpState = new ObtainingIpState(); + final FaultState mFaultState = new FaultState(); + final ConnectedState mConnectedState = new ConnectedState(); + + private LocalLowpanCallback mLocalLowpanCallback = new LocalLowpanCallback(); + + // Misc Private Classes + + private class LocalLowpanCallback extends LowpanInterface.Callback { + @Override + public void onEnabledChanged(boolean value) {} + + @Override + public void onUpChanged(boolean value) {} + + @Override + public void onConnectedChanged(boolean value) {} + + @Override + public void onStateChanged(@NonNull String state) { + LowpanInterfaceTracker.this.sendMessage(CMD_STATE_CHANGE, state); + } + } + + class IpManagerCallback extends IpManager.Callback { + @Override + public void onProvisioningSuccess(LinkProperties newLp) { + LowpanInterfaceTracker.this.sendMessage(CMD_PROVISIONING_SUCCESS, newLp); + } + + @Override + public void onProvisioningFailure(LinkProperties newLp) { + LowpanInterfaceTracker.this.sendMessage(CMD_PROVISIONING_FAILURE, newLp); + } + + @Override + public void onLinkPropertiesChange(LinkProperties newLp) { + LowpanInterfaceTracker.this.sendMessage(CMD_LINK_PROPERTIES_CHANGE, newLp); + } + } + + // State Definitions + + class InitState extends State { + @Override + public void enter() {} + + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_REGISTER: + if (DBG) { + Log.i(TAG, "CMD_REGISTER"); + } + transitionTo(mDefaultState); + break; + + default: + return NOT_HANDLED; + } + return HANDLED; + } + + @Override + public void exit() {} + } + + class DefaultState extends State { + @Override + public void enter() { + if (DBG) { + Log.i(TAG, "DefaultState.enter()"); + } + + mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_NONE, 0, NETWORK_TYPE, ""); + mNetworkInfo.setIsAvailable(true); + + mLowpanInterface.registerCallback(mLocalLowpanCallback); + + mState = ""; + + sendMessage(CMD_STATE_CHANGE, mLowpanInterface.getState()); + } + + @Override + public boolean processMessage(Message message) { + boolean retValue = NOT_HANDLED; + + switch (message.what) { + case CMD_UNREGISTER: + transitionTo(mInitState); + retValue = HANDLED; + break; + + case CMD_START_NETWORK: + if (DBG) { + Log.i(TAG, "CMD_START_NETWORK"); + } + break; + + case CMD_STOP_NETWORK: + if (DBG) { + Log.i(TAG, "CMD_START_NETWORK"); + } + break; + + case CMD_STATE_CHANGE: + if (!mState.equals(message.obj)) { + if (DBG) { + Log.i( + TAG, + "LowpanInterface changed state from \"" + + mState + + "\" to \"" + + message.obj.toString() + + "\"."); + } + mState = (String) message.obj; + switch (mState) { + case LowpanInterface.STATE_OFFLINE: + transitionTo(mOfflineState); + break; + case LowpanInterface.STATE_COMMISSIONING: + transitionTo(mCommissioningState); + break; + case LowpanInterface.STATE_ATTACHING: + transitionTo(mAttachingState); + break; + case LowpanInterface.STATE_ATTACHED: + transitionTo(mObtainingIpState); + break; + case LowpanInterface.STATE_FAULT: + transitionTo(mFaultState); + break; + } + } + retValue = HANDLED; + break; + } + return retValue; + } + + @Override + public void exit() { + + mLowpanInterface.unregisterCallback(mLocalLowpanCallback); + } + } + + class NormalState extends State { + @Override + public void enter() { + if (DBG) { + Log.i(TAG, "NormalState.enter()"); + } + + if (mHwAddr == null) { + byte[] hwAddr = null; + try { + hwAddr = mLowpanInterface.getService().getMacAddress(); + + } catch (RemoteException | ServiceSpecificException x) { + // Don't let misbehavior of an interface service + // crash the system service. + Log.e(TAG, x.toString()); + transitionTo(mFaultState); + } + + if (hwAddr != null) { + mHwAddr = HexDump.toHexString(hwAddr); + } + } + + mNetworkFactory.register(); + } + + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_UNWANTED: + if (mNetworkAgent == message.obj) { + if (DBG) { + Log.i(TAG, "UNWANTED."); + } + + // TODO: Figure out how to properly handle this. + + shutdownNetworkAgent(); + } + break; + + case CMD_LINK_PROPERTIES_CHANGE: + mLinkProperties = (LinkProperties) message.obj; + if (DBG) { + Log.i(TAG, "Got LinkProperties: " + mLinkProperties); + } + if (mNetworkAgent != null) { + mNetworkAgent.sendLinkProperties(mLinkProperties); + } + break; + + case CMD_PROVISIONING_FAILURE: + Log.i(TAG, "Provisioning Failure: " + message.obj.toString()); + break; + } + + return NOT_HANDLED; + } + + @Override + public void exit() { + shutdownNetworkAgent(); + mNetworkFactory.unregister(); + } + } + + class OfflineState extends State { + @Override + public void enter() { + shutdownNetworkAgent(); + mNetworkInfo.setIsAvailable(true); + } + + @Override + public boolean processMessage(Message message) { + return NOT_HANDLED; + } + + @Override + public void exit() {} + } + + class CommissioningState extends State { + @Override + public void enter() {} + + @Override + public boolean processMessage(Message message) { + return NOT_HANDLED; + } + + @Override + public void exit() {} + } + + class AttachingState extends State { + @Override + public void enter() { + mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, mHwAddr); + mNetworkInfo.setIsAvailable(true); + bringUpNetworkAgent(); + mNetworkAgent.sendNetworkInfo(mNetworkInfo); + } + + @Override + public boolean processMessage(Message message) { + return NOT_HANDLED; + } + + @Override + public void exit() {} + } + + class AttachedState extends State { + @Override + public void enter() { + bringUpNetworkAgent(); + mNetworkInfo.setIsAvailable(true); + } + + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_STATE_CHANGE: + if (!mState.equals(message.obj)) { + if (!LowpanInterface.STATE_ATTACHED.equals(message.obj)) { + return NOT_HANDLED; + } + } + break; + + default: + return NOT_HANDLED; + } + return HANDLED; + } + + @Override + public void exit() { + mNetworkInfo.setIsAvailable(false); + } + } + + class ObtainingIpState extends State { + @Override + public void enter() { + InitialConfiguration initialConfiguration = new InitialConfiguration(); + + try { + for (LinkAddress address : mLowpanInterface.getLinkAddresses()) { + if (DBG) { + Log.i(TAG, "Adding link address: " + address); + } + + initialConfiguration.ipAddresses.add(address); + + IpPrefix prefix = new IpPrefix(address.getAddress(), address.getPrefixLength()); + + initialConfiguration.directlyConnectedRoutes.add(prefix); + } + + for (IpPrefix prefix : mLowpanInterface.getLinkNetworks()) { + if (DBG) { + Log.i(TAG, "Adding directly connected route: " + prefix); + } + + initialConfiguration.directlyConnectedRoutes.add(prefix); + } + + } catch (LowpanException | LowpanRuntimeException x) { + Log.e(TAG, "Exception while populating InitialConfiguration: " + x); + transitionTo(mFaultState); + return; + + } catch (RuntimeException x) { + if (x.getCause() instanceof RemoteException) { + // Don't let misbehavior of an interface service + // crash the system service. + Log.e(TAG, x.toString()); + transitionTo(mFaultState); + + } else { + // This exception wasn't remote in origin, so we rethrow. + throw x; + } + } + + if (!initialConfiguration.isValid()) { + Log.e(TAG, "Invalid initial configuration: " + initialConfiguration); + transitionTo(mFaultState); + return; + } + + if (DBG) { + Log.d(TAG, "Using Initial configuration: " + initialConfiguration); + } + + final ProvisioningConfiguration.Builder builder = + mIpManager.buildProvisioningConfiguration(); + + builder.withInitialConfiguration(initialConfiguration).withProvisioningTimeoutMs(0); + + // LoWPAN networks generally don't have internet connectivity, + // so the reachability monitor would almost always fail. + builder.withoutIpReachabilityMonitor(); + + // We currently only support IPv6 on LoWPAN networks, although + // theoretically we could make this determination by examining + // the InitialConfiguration for any IPv4 addresses. + builder.withoutIPv4(); + + mIpManager.startProvisioning(builder.build()); + + mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr); + mNetworkAgent.sendNetworkInfo(mNetworkInfo); + } + + @Override + public boolean processMessage(Message message) { + + switch (message.what) { + case CMD_PROVISIONING_SUCCESS: + Log.i(TAG, "Provisioning Success: " + message.obj.toString()); + transitionTo(mConnectedState); + return HANDLED; + } + return NOT_HANDLED; + } + + @Override + public void exit() {} + } + + class ConnectedState extends State { + @Override + public void enter() { + mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr); + mNetworkAgent.sendNetworkInfo(mNetworkInfo); + mNetworkAgent.sendNetworkScore(NETWORK_SCORE); + } + + @Override + public boolean processMessage(Message message) { + return NOT_HANDLED; + } + + @Override + public void exit() { + if (mNetworkAgent != null) { + mNetworkAgent.sendNetworkScore(0); + } + } + } + + class FaultState extends State { + @Override + public void enter() {} + + @Override + public boolean processMessage(Message message) { + return NOT_HANDLED; + } + + @Override + public void exit() {} + } + + public LowpanInterfaceTracker(Context context, ILowpanInterface ifaceService, Looper looper) { + super(TAG, looper); + + if (DBG) { + Log.i(TAG, "LowpanInterfaceTracker() begin"); + } + + setDbg(DBG); + setLogRecSize(NUM_LOG_RECS_NORMAL); + setLogOnlyTransitions(false); + + mILowpanInterface = ifaceService; + mLowpanInterface = new LowpanInterface(context, ifaceService, looper); + mContext = context; + + mInterfaceName = mLowpanInterface.getName(); + + mLinkProperties = new LinkProperties(); + mLinkProperties.setInterfaceName(mInterfaceName); + + // Initialize capabilities + mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_LOWPAN); + mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + mNetworkCapabilities.setLinkUpstreamBandwidthKbps(100); + mNetworkCapabilities.setLinkDownstreamBandwidthKbps(100); + + // CHECKSTYLE:OFF IndentationCheck + addState(mInitState); + addState(mDefaultState); + addState(mFaultState, mDefaultState); + addState(mNormalState, mDefaultState); + addState(mOfflineState, mNormalState); + addState(mCommissioningState, mNormalState); + addState(mAttachingState, mNormalState); + addState(mAttachedState, mNormalState); + addState(mObtainingIpState, mAttachedState); + addState(mConnectedState, mAttachedState); + // CHECKSTYLE:ON IndentationCheck + + setInitialState(mInitState); + + mNetworkFactory = + new NetworkFactory(looper, context, NETWORK_TYPE, mNetworkCapabilities) { + @Override + protected void startNetwork() { + LowpanInterfaceTracker.this.sendMessage(CMD_START_NETWORK); + } + + @Override + protected void stopNetwork() { + LowpanInterfaceTracker.this.sendMessage(CMD_STOP_NETWORK); + } + }; + + mIpManager = new IpManager(mContext, mInterfaceName, mIpManagerCallback); + + start(); + + if (DBG) { + Log.i(TAG, "LowpanInterfaceTracker() end"); + } + } + + private void shutdownNetworkAgent() { + mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); + mNetworkInfo.setIsAvailable(false); + + if (mNetworkAgent != null) { + mNetworkAgent.sendNetworkScore(0); + mNetworkAgent.sendNetworkInfo(mNetworkInfo); + } + + mNetworkAgent = null; + } + + private void bringUpNetworkAgent() { + if (mNetworkAgent == null) { + mNetworkAgent = + new NetworkAgent( + mNetworkFactory.getLooper(), + mContext, + NETWORK_TYPE, + mNetworkInfo, + mNetworkCapabilities, + mLinkProperties, + NETWORK_SCORE) { + public void unwanted() { + LowpanInterfaceTracker.this.sendMessage(CMD_UNWANTED, this); + }; + }; + } + } + + public void register() { + if (DBG) { + Log.i(TAG, "register()"); + } + sendMessage(CMD_REGISTER); + } + + public void unregister() { + if (DBG) { + Log.i(TAG, "unregister()"); + } + sendMessage(CMD_UNREGISTER); + } +} diff --git a/service/java/com/android/server/lowpan/LowpanService.java b/service/java/com/android/server/lowpan/LowpanService.java new file mode 100644 index 0000000..910dff0 --- /dev/null +++ b/service/java/com/android/server/lowpan/LowpanService.java @@ -0,0 +1,45 @@ +/* + * 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 com.android.server.lowpan; + +import android.content.Context; +import android.net.lowpan.ILowpanManager; +import android.util.Log; +import com.android.server.SystemService; + +public final class LowpanService extends SystemService { + private static final String TAG = LowpanService.class.getSimpleName(); + private final LowpanServiceImpl mImpl; + + public LowpanService(Context context) { + super(context); + mImpl = new LowpanServiceImpl(context); + } + + @Override + public void onStart() { + Log.i(TAG, "Registering " + ILowpanManager.LOWPAN_SERVICE_NAME); + publishBinderService(ILowpanManager.LOWPAN_SERVICE_NAME, mImpl); + } + + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + mImpl.checkAndStartLowpan(); + } + } +} diff --git a/service/java/com/android/server/lowpan/LowpanServiceImpl.java b/service/java/com/android/server/lowpan/LowpanServiceImpl.java new file mode 100644 index 0000000..07677e3 --- /dev/null +++ b/service/java/com/android/server/lowpan/LowpanServiceImpl.java @@ -0,0 +1,284 @@ +/* + * 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 com.android.server.lowpan; + +import android.content.Context; +import android.net.lowpan.ILowpanInterface; +import android.net.lowpan.ILowpanManager; +import android.net.lowpan.ILowpanManagerListener; +import android.os.Binder; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.util.Log; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * LowpanService handles remote LoWPAN operation requests by implementing the ILowpanManager + * interface. + * + * @hide + */ +public class LowpanServiceImpl extends ILowpanManager.Stub { + private static final String TAG = LowpanServiceImpl.class.getSimpleName(); + private final Set<ILowpanManagerListener> mListenerSet = new HashSet<>(); + private final Map<String, LowpanInterfaceTracker> mInterfaceMap = new HashMap<>(); + private final Context mContext; + private final HandlerThread mHandlerThread = new HandlerThread("LowpanServiceThread"); + private final AtomicBoolean mStarted = new AtomicBoolean(false); + + public LowpanServiceImpl(Context context) { + mContext = context; + } + + public Looper getLooper() { + Looper looper = mHandlerThread.getLooper(); + if (looper == null) { + mHandlerThread.start(); + looper = mHandlerThread.getLooper(); + } + + return looper; + } + + public void checkAndStartLowpan() { + synchronized (mInterfaceMap) { + if (mStarted.compareAndSet(false, true)) { + for (Map.Entry<String, LowpanInterfaceTracker> entry : mInterfaceMap.entrySet()) { + entry.getValue().register(); + } + } + } + + // TODO: Bring up any daemons(like wpantund)? + } + + private void enforceAccessPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_LOWPAN_STATE, "LowpanService"); + } + + private void enforceManagePermission() { + // TODO: Change to android.Manifest.permission.MANAGE_lowpanInterfaceS + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_LOWPAN_STATE, "LowpanService"); + } + + public ILowpanInterface getInterface(String name) { + ILowpanInterface iface = null; + + enforceAccessPermission(); + + synchronized (mInterfaceMap) { + LowpanInterfaceTracker tracker = mInterfaceMap.get(name); + if (tracker != null) { + iface = tracker.mILowpanInterface; + } + } + + return iface; + } + + public String[] getInterfaceList() { + enforceAccessPermission(); + synchronized (mInterfaceMap) { + return mInterfaceMap.keySet().toArray(new String[mInterfaceMap.size()]); + } + } + + private void onInterfaceRemoved(ILowpanInterface lowpanInterface, String name) { + Log.i(TAG, "Removed LoWPAN interface `" + name + "` (" + lowpanInterface.toString() + ")"); + synchronized (mListenerSet) { + for (ILowpanManagerListener listener : mListenerSet) { + try { + listener.onInterfaceRemoved(lowpanInterface); + } catch (RemoteException | ServiceSpecificException x) { + // Don't let misbehavior of a listener + // crash the system service. + Log.e(TAG, "Exception caught: " + x); + + // TODO: Consider removing the listener...? + } + } + } + } + + private void onInterfaceAdded(ILowpanInterface lowpanInterface, String name) { + Log.i(TAG, "Added LoWPAN interface `" + name + "` (" + lowpanInterface.toString() + ")"); + synchronized (mListenerSet) { + for (ILowpanManagerListener listener : mListenerSet) { + try { + listener.onInterfaceAdded(lowpanInterface); + } catch (RemoteException | ServiceSpecificException x) { + // Don't let misbehavior of a listener + // crash the system service. + Log.e(TAG, "Exception caught: " + x); + + // TODO: Consider removing the listener...? + } + } + } + } + + public void addInterface(ILowpanInterface lowpanInterface) { + enforceManagePermission(); + + final String name; + + try { + // We allow blocking calls to get the name of the interface. + Binder.allowBlocking(lowpanInterface.asBinder()); + + name = lowpanInterface.getName(); + lowpanInterface + .asBinder() + .linkToDeath( + new IBinder.DeathRecipient() { + @Override + public void binderDied() { + Log.w( + TAG, + "LoWPAN interface `" + + name + + "` (" + + lowpanInterface.toString() + + ") died."); + removeInterface(lowpanInterface); + } + }, + 0); + + } catch (RemoteException | ServiceSpecificException x) { + // Don't let misbehavior of an interface + // crash the system service. + Log.e(TAG, "Exception caught: " + x); + return; + } + + final LowpanInterfaceTracker previous; + final LowpanInterfaceTracker agent; + + synchronized (mInterfaceMap) { + previous = mInterfaceMap.get(name); + + agent = new LowpanInterfaceTracker(mContext, lowpanInterface, getLooper()); + + mInterfaceMap.put(name, agent); + } + + if (previous != null) { + previous.unregister(); + onInterfaceRemoved(previous.mILowpanInterface, name); + } + + if (mStarted.get()) { + agent.register(); + } + + onInterfaceAdded(lowpanInterface, name); + } + + private void removeInterfaceByName(String name) { + final ILowpanInterface lowpanInterface; + + enforceManagePermission(); + + if (name == null) { + return; + } + + final LowpanInterfaceTracker agent; + + synchronized (mInterfaceMap) { + agent = mInterfaceMap.get(name); + + if (agent == null) { + return; + } + + lowpanInterface = agent.mILowpanInterface; + + if (mStarted.get()) { + agent.unregister(); + } + + mInterfaceMap.remove(name); + } + + onInterfaceRemoved(lowpanInterface, name); + } + + public void removeInterface(ILowpanInterface lowpanInterface) { + String name = null; + + try { + name = lowpanInterface.getName(); + } catch (RemoteException | ServiceSpecificException x) { + // Directly fetching the name failed, so fall back to + // a reverse lookup. + synchronized (mInterfaceMap) { + for (Map.Entry<String, LowpanInterfaceTracker> entry : mInterfaceMap.entrySet()) { + if (entry.getValue().mILowpanInterface == lowpanInterface) { + name = entry.getKey(); + break; + } + } + } + } + + removeInterfaceByName(name); + } + + public void addListener(ILowpanManagerListener listener) { + enforceAccessPermission(); + synchronized (mListenerSet) { + if (!mListenerSet.contains(listener)) { + try { + listener.asBinder() + .linkToDeath( + new IBinder.DeathRecipient() { + @Override + public void binderDied() { + synchronized (mListenerSet) { + mListenerSet.remove(listener); + } + } + }, + 0); + mListenerSet.add(listener); + } catch (RemoteException x) { + // We only get this exception if listener has already died. + Log.e(TAG, "Exception caught: " + x); + } + } + } + } + + public void removeListener(ILowpanManagerListener listener) { + enforceAccessPermission(); + synchronized (mListenerSet) { + mListenerSet.remove(listener); + // TODO: Shouldn't we be unlinking from the death notification? + } + } +} diff --git a/tests/commandtest.sh b/tests/commandtest.sh new file mode 100755 index 0000000..fb0fa0e --- /dev/null +++ b/tests/commandtest.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +cd "`dirname $0`" + +die () { + set +x # Turn off printing commands + echo "" + echo " *** fatal error: $*" + exit 1 +} + +if [ -z $ANDROID_BUILD_TOP ]; then + echo "You need to source and lunch before you can use this script" + exit 1 +fi + +./prepdevice.sh || die "Unable to prepare device" + +sleep 2 + +echo "Running tests. . ." + +set -x # print commands + +adb shell killall wpantund 2> /dev/null + +adb shell wpantund -s 'system:ot-ncp\ 1' -o Config:Daemon:ExternalNetifManagement 1 & +WPANTUND_PID=$! +trap "kill -HUP $WPANTUND_PID 2> /dev/null" EXIT INT TERM + +sleep 2 + +kill -0 $WPANTUND_PID || die "wpantund failed to start" + +sleep 2 + +adb shell lowpanctl status || die +adb shell lowpanctl form blahnet || die +adb shell lowpanctl status || die +adb shell ifconfig wpan0 || die + +set +x # Turn off printing commands + +echo Finished. + diff --git a/tests/prepdevice.sh b/tests/prepdevice.sh new file mode 100755 index 0000000..027d64d --- /dev/null +++ b/tests/prepdevice.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +die () { + set +x # Turn off printing commands + echo "error: $*" + exit 1 +} + +if [ -z $ANDROID_BUILD_TOP ]; then + echo "You need to source and lunch before you can use this script" + exit 1 +fi + +echo "Preparing device for LowpanService tests..." + +make -j32 -C $ANDROID_BUILD_TOP -f build/core/main.mk \ + MODULES-IN-frameworks-opt-net-lowpan-service \ + MODULES-IN-frameworks-opt-net-lowpan-command \ + MODULES-IN-external-wpantund \ + MODULES-IN-external-openthread \ + || die "Build failed" + +set -x # print commands + +cp ${ANDROID_BUILD_TOP}/frameworks/native/data/etc/android.hardware.lowpan.xml ${ANDROID_PRODUCT_OUT}/system/etc/permissions/android.hardware.lowpan.xml + +adb root || die +adb wait-for-device || die +adb remount || die +adb shell stop || die +adb disable-verity +adb sync || die +adb shell start || die + +sleep 2 + +echo Device is ready. + |