persist: add the default numbers property array and random number generation

This adds the default numbers property array if it does not exist during
loading. This also adds the function reusing the function generating random key
to generate random number for the first time when the local device's properties
are created.

Unittest PersistTestSuite is added, and its tests include
- LocalDeviceNameGetterAndSetter
- LocalDiscoveryGetterAndSetter
- DeviceGetAddDel
- DeviceKeyAddGetDel
- DeviceNumerAddGetDel

BUG=chromium:805112
TEST=build and run unittest by "./new_blue_unittest
     --gtest_filter=*PersistTestSuite*"

Change-Id: Ic038c997d3e7f43df1af9a3c0284a0b46d814bf6
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();
+}