Merge "persist: add the default numbers property array and random number generation"
diff --git a/Makefile b/Makefile
index 8fd9dba..b087d4c 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 CFLAGS = -Wall -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable -g  -I. -DEXECUTABLE
-CPPFLAGS = -Wall -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable -g  -I. -DEXECUTABLE -D_UNITTEST_
+CPPFLAGS = -Wall -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable -g  -I. -DEXECUTABLE -D_UNITTEST_ -DPREF_PATH=\"/tmp/bt.newblue.prefs\"
 
 TEST := new_blue_test
 
@@ -36,7 +36,9 @@
 	$(CC) $(CFLAGS) -o $@ -g $(OBJ) $(OBJ_TEST) -ldl -lpthread -lrt
 
 OBJ_UNITTEST = \
-	tests/sm_unittest.o
+	tests/unittest.o \
+	tests/sm_unittest.o \
+	tests/persist_unittest.o
 
 new_blue_unittest: $(OBJ_UNITTEST) $(OBJ)
 	$(CXX) $(CPPFLAGS) -o $@ $^ -lgtest -ldl -lpthread
diff --git a/config.h b/config.h
index c14a513..09ba579 100644
--- a/config.h
+++ b/config.h
@@ -1,3 +1,8 @@
+/*
+ * Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
 #ifndef _CONFIG_H_
 #define _CONFIG_H_
 
@@ -35,11 +40,13 @@
 /*
  * Where to store stack config/state
  */
+#ifndef PREF_PATH
 #ifdef ANDROID
 #define PREF_PATH                           "/data/data/com.android.bluetooth/bt.newblue.prefs"
 #else
 #define PREF_PATH                           "bt.newblue.prefs"
 #endif
+#endif
 
 /*
  * AAPI will try to queue this much per socket before shutting off flow
diff --git a/persist.c b/persist.c
index df0bc37..5b17ac1 100644
--- a/persist.c
+++ b/persist.c
@@ -1,3 +1,8 @@
+/*
+ * Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <stdlib.h>
@@ -87,6 +92,8 @@
 /* fwd decls */
 static bool persistPropListLoad(struct persistProp **headP, struct persistProp **tailP, int fd, uint64_t len);
 static bool persistSetPropInList(struct persistProp **headP, struct persistProp **tailP, uint32_t name, uint32_t type, const void *buf, uint32_t len, bool replaceExisting);
+static bool persistAddNumber(struct persistProp **headP, struct persistProp **tailP, uint8_t numType, uint64_t num);
+static struct persistProp* persistFindNum(struct persistProp *head, uint8_t numType);
 
 /*
  * FUNCTION: persistGetRealtime
@@ -446,6 +453,48 @@
 }
 
 /*
+ * FUNCTION: persistGenRandomBytes
+ * USE:      Generate random bytes
+ * PARAMS:   out - where the random bytes are stored
+ *           len - the size of desired length of random bytes
+ * RETURN:   success with the valid output
+ * NOTES:
+ */
+static bool persistGenRandomBytes(uint8_t *out, uint8_t len)
+{
+    int fd;
+    bool ret;
+
+    fd = open("/dev/urandom", O_RDONLY);
+    if (fd == -1) {
+        logw("Failed to open urandom\n");
+        return false;
+    }
+
+    ret = r_read(fd, out, len);
+    close(fd);
+
+    if (!ret) {
+        logw("Failed to read urandom\n");
+        return false;
+    }
+    return true;
+}
+
+/*
+ * FUNCTION: persistGenRand
+ * USE:      Generate a random 64-bit number
+ * PARAMS:   out - where the random number is be stored
+ * RETURN:   success with the valid output
+ * NOTES:
+ */
+static bool persistGenRand(uint64_t *out) {
+    if (!out)
+        return false;
+    return persistGenRandomBytes((uint8_t *)out, sizeof(*out));
+}
+
+/*
  * FUNCTION: persistAddKey
  * USE:      Add a key of the given type and add it to the given list (and optionally generate it)
  * PARAMS:   headP - list head stored here
@@ -458,27 +507,14 @@
 static bool persistAddKey(struct persistProp **headP, struct persistProp **tailP, uint8_t keyType, const uint8_t *key)
 {
     struct persistKey pkey;
-    bool ret;
-    int fd;
 
     pkey.keyType = keyType;
 
     if (key)
         memcpy(pkey.key, key, sizeof(pkey.key));
     else {
-        fd = open("/dev/urandom", O_RDONLY);
-        if (fd == -1) {
-            logw("Failed to open urandom\n");
+        if (!persistGenRandomBytes(pkey.key, HCI_LE_KEY_LEN))
             return false;
-        }
-
-        ret = r_read(fd, pkey.key, sizeof(pkey.key));
-        close(fd);
-
-        if (!ret) {
-            logw("Failed to read urandom\n");
-            return false;
-        }
     }
 
     return persistSetPropInList(headP, tailP, PERS_NAME_KEY, PERS_TYPE_BYTE_ARRAY, &pkey, sizeof(pkey), false);
@@ -494,7 +530,7 @@
 static void persistLoadRequiredProps(void)
 {
     static const char defaultName[] = "NB.t";
-    struct persistProp *keylist, *key;
+    struct persistProp *keylist, *key, *numList;
 
     if (!persistPropFind(mPropsHead, PERS_NAME_DEVICE_NAME, PERS_TYPE_BYTE_ARRAY))
         if (!persistPropAddByteArr(&mPropsHead, &mPropsTail, PERS_NAME_DEVICE_NAME, strlen(defaultName), defaultName))
@@ -537,6 +573,23 @@
             if (!persistAddKey(&keyArr->head, &keyArr->tail, KEY_TYPE_DHK, NULL))
                 loge("failed to create & store DHK\n");
     }
+
+    numList = persistPropFind(mPropsHead, PERS_NAME_NUMBERS, PERS_TYPE_ARRAY);
+    if (!numList)
+        numList = persistPropAddEmptyArray(&mPropsHead, &mPropsTail, PERS_NAME_NUMBERS);
+    if (!numList)
+        loge("Failed to add device numbers default prop\n");
+    else {
+        struct persistArrayInRam *numArr = (struct persistArrayInRam*)(numList + 1);
+        uint64_t num = 0;
+
+        if (!persistFindNum(numArr->head, PERSIST_NUM_TYPE_SM_RANDOM)) {
+            if (!persistGenRand(&num))
+                loge("failed to generate SM Random\n");
+            else if (!persistAddNumber(&numArr->head, &numArr->tail, PERSIST_NUM_TYPE_SM_RANDOM, num))
+                loge("failed to create & store SM Random\n");
+        }
+    }
 }
 
 /*
@@ -652,7 +705,6 @@
     struct persistProp *t;
     int fd;
 
-
     pthread_mutex_lock(&mLock);
     utilSetBE32(&hdr.magic, PERSIST_MAGIC);
     utilSetBE32(&hdr.version, PERSIST_VERSION_CUR);
@@ -1062,7 +1114,7 @@
  * FUNCTION: persistGetKnownDev
  * USE:      Find info given a mac address
  * PARAMS:   addr - the address to look up
- *           name - name gets stored here (shoudl fit at least HCI_DEV_NAME_LEN bytes). may be null
+ *           name - name gets stored here (should fit at least HCI_DEV_NAME_LEN bytes). may be null
  *           nameLen - name's length gets stored here. may be null
  *           devCls - device class gets stored here. may be null
  * RETURN:   true if device was found (not a promise that any data was returned)
@@ -1177,7 +1229,7 @@
         logw("Failed to save device name\n");
 
     if (haveName) {
-        /* never replace an existing name with a shortened one. Always replce with a full one */
+        /* never replace an existing name with a shortened one. Always replace with a full one */
         struct persistArrayInRam *devItems;
 
         devItems = (struct persistArrayInRam*)(dev + 1);
@@ -1196,6 +1248,10 @@
         if (!persistPropAddEmptyArray(&devArr->head, &devArr->tail, PERS_NAME_KEYS))
             logw("Failed to add the missing keys container to device\n");
 
+    if (!persistPropFind(devArr->head, PERS_NAME_NUMBERS, PERS_TYPE_ARRAY))
+        if (!persistPropAddEmptyArray(&devArr->head, &devArr->tail, PERS_NAME_NUMBERS))
+            logw("Failed to add the missing numbers container to device\n");
+
 out:
     return dev;
 }
diff --git a/persist.h b/persist.h
index f72c5fa..e38b9f4 100644
--- a/persist.h
+++ b/persist.h
@@ -1,3 +1,8 @@
+/*
+ * Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
 #ifndef _PERSIST_H_
 #define _PERSIST_H_
 
@@ -39,8 +44,8 @@
 bool persistGetDevKey(const struct bt_addr *addr, uint8_t keyType, uint8_t *key);       //addr = NULL for our own keys, all keys are HCI_LINK_KEY_LEN bytes long
 bool persistDelDevKeys(const struct bt_addr *addr);                                     //addr = NULL for our own keys, all keys are HCI_LINK_KEY_LEN bytes long
 
-bool persistAddDevNumber(const struct bt_addr *addr, uint8_t numType, uint64_t num);     //addr = NULL for our own numbers
-bool persistGetDevNumber(const struct bt_addr *addr, uint8_t keyType, uint64_t *num);   //addr = NULL for our own numbers
+bool persistAddDevNumber(const struct bt_addr *addr, uint8_t numType, uint64_t num);    //addr = NULL for our own numbers
+bool persistGetDevNumber(const struct bt_addr *addr, uint8_t numType, uint64_t *num);   //addr = NULL for our own numbers
 bool persistDelDevNumbers(const struct bt_addr *addr);                                  //addr = NULL for our own numbers
 
 
diff --git a/tests/persist_unittest.cc b/tests/persist_unittest.cc
new file mode 100644
index 0000000..4abd387
--- /dev/null
+++ b/tests/persist_unittest.cc
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include <string.h>
+
+#include <gtest/gtest.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "../hci.h"
+#include "../persist.h"
+
+#if defined(__cplusplus)
+}
+#endif
+
+namespace {
+
+class PersistTestSuite: public testing::Test {
+  public:
+    const struct bt_addr *getDevOneAddr() { return &devOneAddr; }
+    const struct bt_addr *getDevTwoAddr() { return &devTwoAddr; }
+    std::string getDevOneName() { return devOneName; }
+    std::string getDevTwoName() { return devTwoName; }
+
+  protected:
+    virtual void SetUp() {
+        int addrOne[BT_MAC_LEN] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+        int addrTwo[BT_MAC_LEN] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
+        uint32_t nameOneLen = devOneName.size() + 1;
+        uint32_t nameTwoLen = devTwoName.size() + 1;
+
+        persistLoad();
+
+        devOneName = "Name of DevOne";
+        devTwoName = "Name of DevTwo";
+        devOneAddr.type = BT_ADDR_TYPE_LE_RANDOM;
+        memcpy(devOneAddr.addr, addrOne, sizeof(devOneAddr.addr));
+        devTwoAddr.type = BT_ADDR_TYPE_LE_RANDOM;
+        memcpy(devTwoAddr.addr, addrTwo, sizeof(devTwoAddr.addr));
+
+        persistAddKnownDev(&devOneAddr, devOneName.c_str(), &nameOneLen, true, NULL);
+        persistAddKnownDev(&devTwoAddr, devTwoName.c_str(), &nameTwoLen, true, NULL);
+    }
+    virtual void TearDown() {
+        unlink(PREF_PATH);
+    }
+
+  private:
+    std::string devOneName;
+    std::string devTwoName;
+    struct bt_addr devOneAddr;
+    struct bt_addr devTwoAddr;
+};
+
+TEST_F(PersistTestSuite, LocalDeviceNameGetSet) {
+    char myDevName[] = "My Dev Name";
+    char myLongDevName[] = "\
+        My Dev Name My Dev Name My Dev Name My Dev Name My Dev Name My Dev Name My Dev Name \
+        My Dev Name My Dev Name My Dev Name My Dev Name My Dev Name My Dev Name My Dev Name \
+        My Dev Name My Dev Name My Dev Name My Dev Name My Dev Name My Dev Name My Dev Name \
+        My Dev Name My Dev Name";
+    char name[HCI_DEV_NAME_LEN];
+
+    // default name
+    ASSERT_EQ(persistGetDeviceName(name), strlen("NB.t"));
+    ASSERT_EQ(strncmp(name, "NB.t", strlen("NB.t")), 0);
+
+    // normal name
+    persistSetDeviceName(myDevName, sizeof(myDevName));
+    ASSERT_EQ(persistGetDeviceName(name), sizeof(myDevName));
+    ASSERT_EQ(strncmp(name, myDevName, sizeof(myDevName)), 0);
+
+    // long name
+    persistSetDeviceName(myLongDevName, sizeof(myLongDevName));
+    ASSERT_EQ(persistGetDeviceName(name), HCI_DEV_NAME_LEN);
+    ASSERT_EQ(strncmp(name, myLongDevName, HCI_DEV_NAME_LEN), 0);
+
+    // no name
+    persistSetDeviceName("", 0);
+    ASSERT_EQ(persistGetDeviceName(name), 0);
+}
+
+TEST_F(PersistTestSuite, LocalDiscoveryGetSet) {
+    // default discovery length
+    ASSERT_EQ(persistGetDiscoveryLength(), 60);
+
+    ASSERT_TRUE(persistSetDiscoveryLength(100));
+    ASSERT_EQ(persistGetDiscoveryLength(), 100);
+}
+
+TEST_F(PersistTestSuite, DeviceGetAddDel) {
+    char name[HCI_DEV_NAME_LEN];
+    uint32_t nameLen = 0;
+    uint32_t devCls = 0;
+
+    ASSERT_TRUE(persistGetKnownDev(getDevOneAddr(), name, &nameLen, NULL));
+    ASSERT_EQ(strncmp(name, getDevOneName().c_str(), nameLen), 0);
+
+    // delete device two
+    persistDelKnownDev(getDevTwoAddr());
+    ASSERT_FALSE(persistGetKnownDev(getDevTwoAddr(), name, &nameLen, NULL));
+
+    // add device one back
+    nameLen = getDevTwoName().size() + 1;
+    persistAddKnownDev(getDevTwoAddr(), getDevTwoName().c_str(), &nameLen, true, NULL);
+    ASSERT_TRUE(persistGetKnownDev(getDevTwoAddr(), name, &nameLen, NULL));
+    ASSERT_EQ(strncmp(name, getDevTwoName().c_str(), nameLen), 0);
+}
+
+TEST_F(PersistTestSuite, DeviceKeyGetAddDel) {
+    uint8_t keyDHK[HCI_LE_KEY_LEN] = {0x4C, 0x68, 0x38, 0x41, 0x39, 0xF5, 0x74, 0xD8,
+                                      0x36, 0xBC, 0xF3, 0x4E, 0x9D, 0xFB, 0x01, 0xBF,};
+    uint8_t keyIRK[HCI_LE_KEY_LEN] = {0x57, 0x83, 0xD5, 0x21, 0x56, 0xAD, 0x6F, 0x0E,
+                                      0x63, 0x88, 0x27, 0x4E, 0xC6, 0x70, 0x2E, 0xE0,};
+    uint8_t key[HCI_LE_KEY_LEN] = {0,};
+
+    // set IRK key for local device without providing key
+    ASSERT_TRUE(persistGetDevKey(NULL, KEY_TYPE_IRK, key));
+    ASSERT_TRUE(persistAddDevKey(NULL, KEY_TYPE_IRK, NULL));
+    ASSERT_TRUE(persistGetDevKey(NULL, KEY_TYPE_IRK, key));
+    ASSERT_TRUE(persistDelDevKeys(NULL));
+    ASSERT_FALSE(persistGetDevKey(NULL, KEY_TYPE_IRK, key));
+
+    // set IRK and DHK keys for device one
+    ASSERT_FALSE(persistGetDevKey(getDevOneAddr(), KEY_TYPE_IRK, key));
+    ASSERT_TRUE(persistAddDevKey(getDevOneAddr(), KEY_TYPE_IRK, keyIRK));
+    ASSERT_TRUE(persistGetDevKey(getDevOneAddr(), KEY_TYPE_IRK, key));
+    ASSERT_EQ(memcmp(keyIRK, key, HCI_LE_KEY_LEN), 0);
+    ASSERT_TRUE(persistAddDevKey(getDevOneAddr(), KEY_TYPE_DHK, keyDHK));
+    ASSERT_TRUE(persistGetDevKey(getDevOneAddr(), KEY_TYPE_DHK, key));
+    ASSERT_EQ(memcmp(keyDHK, key, HCI_LE_KEY_LEN), 0);
+    ASSERT_TRUE(persistDelDevKeys(getDevOneAddr()));
+    ASSERT_FALSE(persistGetDevKey(getDevOneAddr(), KEY_TYPE_IRK, key));
+    ASSERT_FALSE(persistGetDevKey(getDevOneAddr(), KEY_TYPE_DHK, key));
+
+    // delete device two and try to add/get/delete keys of device two
+    persistDelKnownDev(getDevTwoAddr());
+    ASSERT_FALSE(persistGetDevKey(getDevTwoAddr(), KEY_TYPE_IRK, key));
+    ASSERT_TRUE(persistDelDevKeys(getDevTwoAddr()));
+    ASSERT_TRUE(persistAddDevKey(getDevTwoAddr(), KEY_TYPE_IRK, keyIRK));
+}
+
+TEST_F(PersistTestSuite, DeviceNumerGetAddDel) {
+    uint64_t randNumOne = 12345678;
+    uint64_t randNumTwo = 87654321;
+    uint64_t num = 0;
+
+    // set a number for local device
+    ASSERT_TRUE(persistGetDevNumber(NULL, PERSIST_NUM_TYPE_SM_RANDOM, &num));
+    ASSERT_TRUE(persistAddDevNumber(NULL, PERSIST_NUM_TYPE_SM_RANDOM, randNumOne));
+    ASSERT_TRUE(persistGetDevNumber(NULL, PERSIST_NUM_TYPE_SM_RANDOM, &num));
+    ASSERT_EQ(num, randNumOne);
+
+    // replace the randNumOne with randNumTwo for local device
+    ASSERT_TRUE(persistAddDevNumber(NULL, PERSIST_NUM_TYPE_SM_RANDOM, randNumTwo));
+    ASSERT_TRUE(persistGetDevNumber(NULL, PERSIST_NUM_TYPE_SM_RANDOM, &num));
+    ASSERT_EQ(num, randNumTwo);
+
+    // delete numbers of local device
+    ASSERT_TRUE(persistDelDevNumbers(NULL));
+    ASSERT_FALSE(persistGetDevNumber(NULL, PERSIST_NUM_TYPE_SM_RANDOM, &num));
+
+    // set a number for device one
+    ASSERT_FALSE(persistGetDevNumber(getDevOneAddr(), PERSIST_NUM_TYPE_SM_RANDOM, &num));
+    ASSERT_TRUE(persistAddDevNumber(getDevOneAddr(), PERSIST_NUM_TYPE_SM_RANDOM, randNumOne));
+    ASSERT_TRUE(persistGetDevNumber(getDevOneAddr(), PERSIST_NUM_TYPE_SM_RANDOM, &num));
+    ASSERT_EQ(num, randNumOne);
+
+    // replace the randNumOne with randNumTwo for device one
+    ASSERT_TRUE(persistAddDevNumber(getDevOneAddr(), PERSIST_NUM_TYPE_SM_RANDOM, randNumTwo));
+    ASSERT_TRUE(persistGetDevNumber(getDevOneAddr(), PERSIST_NUM_TYPE_SM_RANDOM, &num));
+    ASSERT_EQ(num, randNumTwo);
+
+    // delete numbers of device one
+    ASSERT_TRUE(persistDelDevNumbers(getDevOneAddr()));
+    ASSERT_FALSE(persistGetDevNumber(getDevOneAddr(), PERSIST_NUM_TYPE_SM_RANDOM, &num));
+
+    // delete device two and try to add/get/delete a number
+    persistDelKnownDev(getDevTwoAddr());
+    ASSERT_FALSE(persistGetDevNumber(getDevTwoAddr(), PERSIST_NUM_TYPE_SM_RANDOM, &num));
+    ASSERT_TRUE(persistDelDevNumbers(getDevTwoAddr()));
+    ASSERT_TRUE(persistAddDevNumber(getDevTwoAddr(), PERSIST_NUM_TYPE_SM_RANDOM, randNumTwo));
+}
+
+}  // namespace
diff --git a/tests/sm_unittest.cc b/tests/sm_unittest.cc
index 339153b..a32168c 100644
--- a/tests/sm_unittest.cc
+++ b/tests/sm_unittest.cc
@@ -124,8 +124,3 @@
 }
 
 } // namespace
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-    return RUN_ALL_TESTS();
-}
diff --git a/tests/unittest.cc b/tests/unittest.cc
new file mode 100644
index 0000000..11ff69a
--- /dev/null
+++ b/tests/unittest.cc
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include <gtest/gtest.h>
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}