Roy Want / Mbed OS beaconCompileReadyFork
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers EddystoneService.cpp Source File

EddystoneService.cpp

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2006-2015 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include "EddystoneService.h"
00018 #include "EntropySource/EntropySource.h"
00019 
00020 /* Use define zero for production, 1 for testing to allow connection at any time */
00021 #define DEFAULT_REMAIN_CONNECTABLE 0x01
00022 
00023 const char * const EddystoneService::slotDefaultUrls[] = EDDYSTONE_DEFAULT_SLOT_URLS;
00024 
00025 // Static timer used as time since boot
00026 Timer        EddystoneService::timeSinceBootTimer;
00027 
00028 /*
00029  * CONSTRUCTOR #1 Used on 1st boot (after reflash)
00030  */
00031 EddystoneService::EddystoneService(BLE                 &bleIn,
00032                                    const PowerLevels_t &advTxPowerLevelsIn,
00033                                    const PowerLevels_t &radioTxPowerLevelsIn,
00034                                    event_queue_t       &evQ,
00035                                    uint32_t            advConfigIntervalIn) :
00036     ble(bleIn),
00037     operationMode(EDDYSTONE_MODE_NONE),
00038     uidFrame(),
00039     urlFrame(),
00040     tlmFrame(),
00041     eidFrame(),
00042     tlmBatteryVoltageCallback(NULL),
00043     tlmBeaconTemperatureCallback(NULL),
00044     radioManagerCallbackHandle(NULL),
00045     deviceName(DEFAULT_DEVICE_NAME),
00046     eventQueue(evQ),
00047     nextEidSlot(0)
00048 {
00049     LOG(("1st Boot: ")); 
00050     LOG((BUILD_VERSION_STR));
00051     if (advConfigIntervalIn != 0) {
00052         if (advConfigIntervalIn < ble.gap().getMinAdvertisingInterval()) {
00053             advConfigInterval = ble.gap().getMinAdvertisingInterval();
00054         } else if (advConfigIntervalIn > ble.gap().getMaxAdvertisingInterval()) {
00055             advConfigInterval = ble.gap().getMaxAdvertisingInterval();
00056         } else {
00057             advConfigInterval = advConfigIntervalIn;
00058         }
00059     }
00060     memcpy(radioTxPowerLevels, radioTxPowerLevelsIn, sizeof(PowerLevels_t));
00061     memcpy(advTxPowerLevels,   advTxPowerLevelsIn,   sizeof(PowerLevels_t));
00062 
00063     // 1st Boot so reset everything to factory values
00064     doFactoryReset();  // includes genBeaconKeys
00065     
00066     LOG(("After FactoryReset in 1st Boot Init: genBeaconKeyRC=%d\r\n", genBeaconKeyRC));
00067     /* TODO: Note that this timer is started from the time EddystoneService
00068      * is initialised and NOT from when the device is booted.
00069      */
00070     timeSinceBootTimer.start();
00071 
00072     /* Set the device name at startup */
00073     ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceName));
00074 
00075 }
00076 
00077 /*
00078  * Constuctor #2:  Used on 2nd+ boot: EddystoneService parameters derived from persistent storage
00079  */
00080 EddystoneService::EddystoneService(BLE                 &bleIn,
00081                                    EddystoneParams_t   &paramsIn,
00082                                    const PowerLevels_t &radioTxPowerLevelsIn,
00083                                    event_queue_t       &evQ,
00084                                    uint32_t            advConfigIntervalIn) :
00085     ble(bleIn),
00086     operationMode(EDDYSTONE_MODE_NONE),
00087     uidFrame(),
00088     urlFrame(),
00089     tlmFrame(),
00090     eidFrame(),
00091     tlmBatteryVoltageCallback(NULL),
00092     tlmBeaconTemperatureCallback(NULL),
00093     radioManagerCallbackHandle(NULL),
00094     deviceName(DEFAULT_DEVICE_NAME),
00095     eventQueue(evQ),
00096     nextEidSlot(0)
00097 {
00098     LOG(("2nd (>=) Boot: "));
00099     LOG((BUILD_VERSION_STR));
00100     memcpy(capabilities, paramsIn.capabilities, sizeof(Capability_t));
00101     activeSlot          = paramsIn.activeSlot;
00102     memcpy(radioTxPowerLevels, radioTxPowerLevelsIn, sizeof(PowerLevels_t));
00103     memcpy(slotRadioTxPowerLevels, paramsIn.slotRadioTxPowerLevels, sizeof(SlotTxPowerLevels_t));
00104     memcpy(advTxPowerLevels,   paramsIn.advTxPowerLevels,   sizeof(PowerLevels_t));
00105     memcpy(slotAdvTxPowerLevels, paramsIn.slotAdvTxPowerLevels, sizeof(SlotTxPowerLevels_t));
00106     memcpy(slotAdvIntervals,   paramsIn.slotAdvIntervals,   sizeof(SlotAdvIntervals_t));
00107     lockState           = paramsIn.lockState;
00108     memcpy(unlockKey,   paramsIn.unlockKey,   sizeof(Lock_t));
00109     memcpy(unlockToken, paramsIn.unlockToken, sizeof(Lock_t));
00110     memcpy(challenge, paramsIn.challenge, sizeof(Lock_t));
00111     memset(slotCallbackHandles, 0, sizeof(SlotCallbackHandles_t));
00112     memcpy(slotStorage, paramsIn.slotStorage, sizeof(SlotStorage_t));
00113     memcpy(slotFrameTypes, paramsIn.slotFrameTypes, sizeof(SlotFrameTypes_t));
00114     memcpy(slotEidRotationPeriodExps, paramsIn.slotEidRotationPeriodExps, sizeof(SlotEidRotationPeriodExps_t));
00115     memcpy(slotEidIdentityKeys, paramsIn.slotEidIdentityKeys, sizeof(SlotEidIdentityKeys_t));
00116     remainConnectable   = paramsIn.remainConnectable;
00117 
00118     if (advConfigIntervalIn != 0) {
00119         if (advConfigIntervalIn < ble.gap().getMinAdvertisingInterval()) {
00120             advConfigInterval = ble.gap().getMinAdvertisingInterval();
00121         } else if (advConfigIntervalIn > ble.gap().getMaxAdvertisingInterval()) {
00122             advConfigInterval = ble.gap().getMaxAdvertisingInterval();
00123         } else {
00124             advConfigInterval = advConfigIntervalIn;
00125         }
00126     }
00127     
00128     // Generate fresh private and public ECDH keys for EID
00129     genEIDBeaconKeys();
00130 
00131     // Recompute EID Slot Data
00132     for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) {
00133         uint8_t* frame = slotToFrame(slot);
00134         switch (slotFrameTypes[slot]) {
00135             case EDDYSTONE_FRAME_EID:
00136                eidFrame.update(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSinceBootTimer.read_ms() / 1000);
00137                eidFrame.setAdvTxPower(frame, slotAdvTxPowerLevels[slot]);
00138                break;
00139             default: ;
00140         }
00141     }
00142 
00143 
00144     /* TODO: Note that this timer is started from the time EddystoneService
00145      * is initialised and NOT from when the device is booted.
00146      */
00147     timeSinceBootTimer.start();
00148 
00149     /* Set the device name at startup */
00150     ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceName));
00151 }
00152 
00153 // Regenerate the beacon keys
00154 void EddystoneService::genEIDBeaconKeys(void) {
00155     genBeaconKeyRC = -1;
00156 #ifdef GEN_BEACON_KEYS_AT_INIT
00157     memset(privateEcdhKey, 0, 32);
00158     memset(publicEcdhKey, 0, 32);
00159     genBeaconKeyRC = eidFrame.genBeaconKeys(privateEcdhKey, publicEcdhKey);
00160     swapEndianArray(publicEcdhKey, publicEcdhKeyLE, 32);
00161 #endif
00162 }
00163 
00164 /**
00165  * Factory reset all parmeters: used at initial boot, and activated from Char 11
00166  */
00167 void EddystoneService::doFactoryReset(void)
00168 {    
00169     memset(slotCallbackHandles, 0, sizeof(SlotCallbackHandles_t));
00170     radioManagerCallbackHandle = NULL;
00171     memcpy(capabilities, CAPABILITIES_DEFAULT, CAP_HDR_LEN);
00172     // Line above leaves powerlevels blank; Line below fills them in
00173     memcpy(capabilities + CAP_HDR_LEN, radioTxPowerLevels, sizeof(PowerLevels_t));
00174     activeSlot = DEFAULT_SLOT;
00175     // Intervals
00176     uint16_t buf1[] = EDDYSTONE_DEFAULT_SLOT_INTERVALS;
00177     for (int i = 0; i < MAX_ADV_SLOTS; i++) {
00178             // Ensure all slot periods are in range
00179             buf1[i] = correctAdvertisementPeriod(buf1[i]);
00180     }
00181     memcpy(slotAdvIntervals, buf1, sizeof(SlotAdvIntervals_t));
00182     // Radio and Adv TX Power
00183     int8_t buf2[] = EDDYSTONE_DEFAULT_SLOT_TX_POWERS;
00184     for (int i = 0; i< MAX_ADV_SLOTS; i++) {
00185       slotRadioTxPowerLevels[i] = buf2[i];
00186       slotAdvTxPowerLevels[i] = advTxPowerLevels[radioTxPowerToIndex(buf2[i])];
00187     }
00188     // Lock
00189     lockState      = UNLOCKED;
00190     uint8_t defKeyBuf[] = EDDYSTONE_DEFAULT_UNLOCK_KEY;
00191     memcpy(unlockKey,        defKeyBuf,     sizeof(Lock_t));
00192     memset(unlockToken,      0,     sizeof(Lock_t));
00193     memset(challenge,        0,     sizeof(Lock_t)); // NOTE: challenge is randomized on first unlockChar read;
00194 
00195     // Generate ECDH Beacon Key Pair (Private/Public)
00196     genEIDBeaconKeys();
00197     
00198     memcpy(slotEidIdentityKeys, slotDefaultEidIdentityKeys, sizeof(SlotEidIdentityKeys_t));
00199     uint8_t buf4[] = EDDYSTONE_DEFAULT_SLOT_EID_ROTATION_PERIOD_EXPS;
00200     memcpy(slotEidRotationPeriodExps, buf4, sizeof(SlotEidRotationPeriodExps_t));
00201     memset(slotEidNextRotationTimes, 0, sizeof(SlotEidNextRotationTimes_t));
00202     //  Slot Data Type Defaults
00203     uint8_t buf3[] = EDDYSTONE_DEFAULT_SLOT_TYPES;
00204     memcpy(slotFrameTypes, buf3, sizeof(SlotFrameTypes_t));
00205     // Initialize Slot Data Defaults
00206     int eidSlot;
00207     for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) {
00208         uint8_t* frame = slotToFrame(slot);
00209         switch (slotFrameTypes[slot]) {
00210             case EDDYSTONE_FRAME_UID:
00211                uidFrame.setData(frame, slotAdvTxPowerLevels[slot], reinterpret_cast<const uint8_t*>(slotDefaultUids[slot]));
00212                break;
00213             case EDDYSTONE_FRAME_URL:
00214                urlFrame.setUnencodedUrlData(frame, slotAdvTxPowerLevels[slot], slotDefaultUrls[slot]);
00215                break;
00216             case EDDYSTONE_FRAME_TLM:
00217                tlmFrame.setTLMData(TLMFrame::DEFAULT_TLM_VERSION);
00218                tlmFrame.setData(frame);
00219                eidSlot = getEidSlot();
00220                if (eidSlot != NO_EID_SLOT_SET) {
00221                    LOG(("EID slot Set in FactoryReset\r\n"));
00222                    tlmFrame.encryptData(frame, slotEidIdentityKeys[eidSlot], slotEidRotationPeriodExps[eidSlot], timeSinceBootTimer.read_ms() / 1000);
00223                }
00224                break;
00225             case EDDYSTONE_FRAME_EID:
00226                nextEidSlot = slot;
00227                eidFrame.setData(frame, slotAdvTxPowerLevels[slot], reinterpret_cast<const uint8_t*>(allSlotsDefaultEid));
00228                eidFrame.update(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSinceBootTimer.read_ms() / 1000);
00229                break;
00230         }
00231     }
00232 #ifdef DONT_REMAIN_CONNECTABLE
00233     remainConnectable = REMAIN_CONNECTABLE_UNSET;  
00234 #else
00235     remainConnectable = REMAIN_CONNECTABLE_SET; 
00236 #endif
00237     factoryReset = false;
00238 }
00239 
00240 /* Setup callback to update BatteryVoltage in TLM frame */
00241 void EddystoneService::onTLMBatteryVoltageUpdate(TlmUpdateCallback_t tlmBatteryVoltageCallbackIn)
00242 {
00243     tlmBatteryVoltageCallback = tlmBatteryVoltageCallbackIn;
00244 }
00245 
00246 /* Setup callback to update BeaconTemperature in TLM frame */
00247 void EddystoneService::onTLMBeaconTemperatureUpdate(TlmUpdateCallback_t tlmBeaconTemperatureCallbackIn)
00248 {
00249     tlmBeaconTemperatureCallback = tlmBeaconTemperatureCallbackIn;
00250 }
00251 
00252 EddystoneService::EddystoneError_t EddystoneService::startEddystoneBeaconAdvertisements(void)
00253 {
00254     stopEddystoneBeaconAdvertisements();
00255 
00256     bool intervalValidFlag = false;
00257     for (int i = 0; i < MAX_ADV_SLOTS; i++) {
00258         if (slotAdvIntervals[i] != 0) {
00259             intervalValidFlag = true;
00260         }
00261     }
00262 
00263     if (!intervalValidFlag) {
00264         /* Nothing to do, the period is 0 for all frames */
00265         return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL;
00266     }
00267 
00268     // In case left over from Config Adv Mode
00269     ble.gap().clearScanResponse();
00270 
00271     operationMode = EDDYSTONE_MODE_BEACON;
00272 
00273     /* Configure advertisements initially at power of active slot*/
00274     ble.gap().setTxPower(slotRadioTxPowerLevels[activeSlot]);
00275 
00276     if (remainConnectable) {
00277         ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
00278     } else {
00279          ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
00280     }
00281     ble.gap().setAdvertisingInterval(ble.gap().getMaxAdvertisingInterval());
00282 
00283     /* Make sure the queue is currently empty */
00284     advFrameQueue.reset();
00285     /* Setup callbacks to periodically add frames to be advertised to the queue and
00286      * add initial frame so that we have something to advertise on startup */
00287     for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) {
00288         uint8_t* frame = slotToFrame(slot);
00289         if (slotAdvIntervals[slot] && testValidFrame(frame)) {
00290             advFrameQueue.push(slot);
00291             slotCallbackHandles[slot] = eventQueue.post_every(
00292                 &EddystoneService::enqueueFrame, this, slot,
00293                 slotAdvIntervals[slot] /* ms */
00294             );
00295         }
00296     }
00297     /* Start advertising */
00298     manageRadio();
00299 
00300     return EDDYSTONE_ERROR_NONE;
00301 }
00302 
00303 ble_error_t EddystoneService::setCompleteDeviceName(const char *deviceNameIn)
00304 {
00305     /* Make sure the device name is safe */
00306     ble_error_t error = ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceNameIn));
00307     if (error == BLE_ERROR_NONE) {
00308         deviceName = deviceNameIn;
00309         if (operationMode == EDDYSTONE_MODE_CONFIG) {
00310             /* Need to update the advertising packets to the new name */
00311             setupEddystoneConfigScanResponse();
00312         }
00313     }
00314 
00315     return error;
00316 }
00317 
00318 /* It is not the responsibility of the Eddystone implementation to store
00319  * the configured parameters in persistent storage since this is
00320  * platform-specific. So we provide this function that returns the
00321  * configured values that need to be stored and the main application
00322  * takes care of storing them.
00323  */
00324 void EddystoneService::getEddystoneParams(EddystoneParams_t &params)
00325 {
00326     // Capabilities
00327     memcpy(params.capabilities,     capabilities,           sizeof(Capability_t));
00328     // Active Slot
00329     params.activeSlot                = activeSlot;
00330     // Intervals
00331     memcpy(params.slotAdvIntervals, slotAdvIntervals,       sizeof(SlotAdvIntervals_t));
00332     // Power Levels
00333     memcpy(params.radioTxPowerLevels, radioTxPowerLevels,   sizeof(PowerLevels_t));
00334     memcpy(params.advTxPowerLevels,   advTxPowerLevels,     sizeof(PowerLevels_t));
00335     // Slot Power Levels
00336     memcpy(params.slotRadioTxPowerLevels, slotRadioTxPowerLevels,   sizeof(MAX_ADV_SLOTS));
00337     memcpy(params.slotAdvTxPowerLevels,   slotAdvTxPowerLevels,     sizeof(MAX_ADV_SLOTS));
00338     // Lock
00339     params.lockState                = lockState;
00340     memcpy(params.unlockKey,        unlockKey,              sizeof(Lock_t));
00341     memcpy(params.unlockToken,      unlockToken,            sizeof(Lock_t));
00342     memcpy(params.challenge,        challenge,              sizeof(Lock_t));
00343     // Slots
00344     memcpy(params.slotFrameTypes,   slotFrameTypes,         sizeof(SlotFrameTypes_t));
00345     memcpy(params.slotStorage,      slotStorage,            sizeof(SlotStorage_t));
00346     memcpy(params.slotEidRotationPeriodExps, slotEidRotationPeriodExps, sizeof(SlotEidRotationPeriodExps_t));
00347     memcpy(params.slotEidIdentityKeys, slotEidIdentityKeys, sizeof(SlotEidIdentityKeys_t));
00348     // Testing and Management
00349     params.remainConnectable        = remainConnectable;
00350 }
00351 
00352 void EddystoneService::swapAdvertisedFrame(int slot)
00353 {
00354     uint8_t* frame = slotToFrame(slot);
00355     uint8_t frameType = slotFrameTypes[slot];
00356     uint32_t timeSecs = timeSinceBootTimer.read_ms() / 1000;
00357     switch (frameType) {
00358         case EDDYSTONE_FRAME_UID:
00359             updateAdvertisementPacket(uidFrame.getAdvFrame(frame), uidFrame.getAdvFrameLength(frame));
00360             break;
00361         case EDDYSTONE_FRAME_URL:
00362             updateAdvertisementPacket(urlFrame.getAdvFrame(frame), urlFrame.getAdvFrameLength(frame));
00363             break;
00364         case EDDYSTONE_FRAME_TLM:
00365             updateRawTLMFrame(frame);
00366             updateAdvertisementPacket(tlmFrame.getAdvFrame(frame), tlmFrame.getAdvFrameLength(frame));
00367             break;
00368         case EDDYSTONE_FRAME_EID:
00369             // only update the frame if the rotation period is due
00370             if (timeSecs >= slotEidNextRotationTimes[slot]) {
00371                 eidFrame.update(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSecs);
00372                 slotEidNextRotationTimes[slot] = timeSecs + (1 << slotEidRotationPeriodExps[slot]);
00373                 setRandomMacAddress(); // selects a new MAC address so the beacon is not trackable 
00374                 LOG(("EID ROTATED: Time=%lu\r\n", timeSecs));
00375             }
00376             updateAdvertisementPacket(eidFrame.getAdvFrame(frame), eidFrame.getAdvFrameLength(frame));
00377             break;
00378         default:
00379             //Some error occurred
00380             error("Frame to swap in does not specify a valid type");
00381             break;
00382     }
00383     ble.gap().setTxPower(slotRadioTxPowerLevels[slot]);
00384 }
00385 
00386 
00387 /* Helper function that calls user-defined functions to update Battery Voltage and Temperature (if available),
00388  * then updates the raw frame data and finally updates the actual advertised packet. This operation must be
00389  * done fairly often because the TLM frame TimeSinceBoot must have a 0.1 secs resolution according to the
00390  * Eddystone specification.
00391  */
00392 void EddystoneService::updateRawTLMFrame(uint8_t* frame)
00393 {
00394     if (tlmBeaconTemperatureCallback != NULL) {
00395         tlmFrame.updateBeaconTemperature((*tlmBeaconTemperatureCallback)(tlmFrame.getBeaconTemperature()));
00396     }
00397     if (tlmBatteryVoltageCallback != NULL) {
00398         tlmFrame.updateBatteryVoltage((*tlmBatteryVoltageCallback)(tlmFrame.getBatteryVoltage()));
00399     }
00400     tlmFrame.updateTimeSinceBoot(timeSinceBootTimer.read_ms());
00401     tlmFrame.setData(frame);
00402     int slot = getEidSlot();
00403     LOG(("TLMHelper Method slot=%d\r\n", slot));
00404     if (slot != NO_EID_SLOT_SET) {
00405         LOG(("TLMHelper: Before Encrypting TLM\r\n"));
00406         tlmFrame.encryptData(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSinceBootTimer.read_ms() / 1000);
00407         LOG(("TLMHelper: Before Encrypting TLM\r\n"));
00408     }
00409 }
00410 
00411 void EddystoneService::updateAdvertisementPacket(const uint8_t* rawFrame, size_t rawFrameLength)
00412 {
00413     ble.gap().clearAdvertisingPayload();
00414     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
00415     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID));
00416     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, rawFrame, rawFrameLength);
00417 }
00418 
00419 uint8_t* EddystoneService::slotToFrame(int slot)
00420 {
00421    return reinterpret_cast<uint8_t *>(&slotStorage[slot * sizeof(Slot_t)]);
00422 }
00423 
00424 void EddystoneService::enqueueFrame(int slot)
00425 {
00426     advFrameQueue.push(slot);
00427     if (!radioManagerCallbackHandle) {
00428         /* Advertising stopped and there is not callback posted in the event queue. Just
00429          * execute the manager to resume advertising */
00430         manageRadio();
00431     }
00432 }
00433 
00434 void EddystoneService::manageRadio(void)
00435 {
00436     uint8_t slot;
00437     uint32_t  startTimeManageRadio = timeSinceBootTimer.read_ms();
00438 
00439     /* Signal that there is currently no callback posted */
00440     radioManagerCallbackHandle = NULL;
00441 
00442     if (advFrameQueue.pop(slot)) {
00443         /* We have something to advertise */
00444         if (ble.gap().getState().advertising) {
00445             ble.gap().stopAdvertising();
00446         }
00447         swapAdvertisedFrame(slot);
00448         ble.gap().startAdvertising();
00449 
00450         /* Increase the advertised packet count in TLM frame */
00451         tlmFrame.updatePduCount();
00452 
00453         /* Post a callback to itself to stop the advertisement or pop the next
00454          * frame from the queue. However, take into account the time taken to
00455          * swap in this frame. */
00456         radioManagerCallbackHandle = eventQueue.post_in(
00457             &EddystoneService::manageRadio, this,
00458             ble.gap().getMinNonConnectableAdvertisingInterval() - (timeSinceBootTimer.read_ms() - startTimeManageRadio) /* ms */
00459         );
00460     } else if (ble.gap().getState().advertising) {
00461         /* Nothing else to advertise, stop advertising and do not schedule any callbacks */
00462         ble.gap().stopAdvertising();
00463     }
00464 }
00465 
00466 void EddystoneService::startEddystoneConfigService(void)
00467 {
00468     uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]);
00469     int8_t radioTxPower = slotRadioTxPowerLevels[activeSlot];
00470     int8_t advTxPower = slotAdvTxPowerLevels[activeSlot];
00471     uint8_t* slotData = slotToFrame(activeSlot) + 1;
00472     aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey);
00473 
00474     capabilitiesChar      = new ReadOnlyArrayGattCharacteristic<uint8_t, sizeof(Capability_t)>(UUID_CAPABILITIES_CHAR, capabilities);
00475     activeSlotChar        = new ReadWriteGattCharacteristic<uint8_t>(UUID_ACTIVE_SLOT_CHAR, &activeSlot);
00476     advIntervalChar       = new ReadWriteGattCharacteristic<uint16_t>(UUID_ADV_INTERVAL_CHAR, &beAdvInterval);
00477     radioTxPowerChar      = new ReadWriteGattCharacteristic<int8_t>(UUID_RADIO_TX_POWER_CHAR, &radioTxPower);
00478     advTxPowerChar        = new ReadWriteGattCharacteristic<int8_t>(UUID_ADV_TX_POWER_CHAR, &advTxPower);
00479     lockStateChar         = new GattCharacteristic(UUID_LOCK_STATE_CHAR, &lockState, sizeof(uint8_t), sizeof(LockState_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
00480     unlockChar            = new ReadWriteArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_UNLOCK_CHAR, unlockToken);
00481     publicEcdhKeyChar     = new GattCharacteristic(UUID_PUBLIC_ECDH_KEY_CHAR, publicEcdhKey, 0, sizeof(PublicEcdhKey_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
00482     eidIdentityKeyChar    = new GattCharacteristic(UUID_EID_IDENTITY_KEY_CHAR, encryptedEidIdentityKey, 0, sizeof(EidIdentityKey_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
00483     advSlotDataChar       = new GattCharacteristic(UUID_ADV_SLOT_DATA_CHAR, slotData, 0, 34, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
00484     factoryResetChar      = new WriteOnlyGattCharacteristic<uint8_t>(UUID_FACTORY_RESET_CHAR, &factoryReset);
00485     remainConnectableChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_REMAIN_CONNECTABLE_CHAR, &remainConnectable);
00486 
00487     // CHAR-1 capabilities (READ ONLY)
00488     capabilitiesChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback);
00489     // CHAR-2 Active Slot
00490     activeSlotChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback);
00491     activeSlotChar->setWriteAuthorizationCallback(this, &EddystoneService::writeActiveSlotAuthorizationCallback<uint8_t>);
00492     // CHAR-3 Adv Interval
00493     advIntervalChar->setReadAuthorizationCallback(this, &EddystoneService::readAdvIntervalAuthorizationCallback);
00494     advIntervalChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<uint16_t>);
00495     // CHAR-4  Radio TX Power
00496     radioTxPowerChar->setReadAuthorizationCallback(this, &EddystoneService::readRadioTxPowerAuthorizationCallback);
00497     radioTxPowerChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<uint8_t>);
00498     // CHAR-5
00499     advTxPowerChar->setReadAuthorizationCallback(this, &EddystoneService::readAdvTxPowerAuthorizationCallback);
00500     advTxPowerChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<uint8_t>);
00501     // CHAR-6 Lock State
00502     lockStateChar->setWriteAuthorizationCallback(this, &EddystoneService::writeLockStateAuthorizationCallback);
00503     // CHAR-7 Unlock
00504     unlockChar->setReadAuthorizationCallback(this, &EddystoneService::readUnlockAuthorizationCallback);
00505     unlockChar->setWriteAuthorizationCallback(this, &EddystoneService::writeUnlockAuthorizationCallback);
00506     // CHAR-8 Public Ecdh Key (READ ONLY)
00507     publicEcdhKeyChar->setReadAuthorizationCallback(this, &EddystoneService::readPublicEcdhKeyAuthorizationCallback);
00508     // CHAR-9 EID Identity Key (READ ONLY)
00509     eidIdentityKeyChar->setReadAuthorizationCallback(this, &EddystoneService::readEidIdentityAuthorizationCallback);
00510     // CHAR-10 Adv Slot Data
00511     advSlotDataChar->setReadAuthorizationCallback(this, &EddystoneService::readDataAuthorizationCallback);  
00512     advSlotDataChar->setWriteAuthorizationCallback(this, &EddystoneService::writeVarLengthDataAuthorizationCallback);
00513     // CHAR-11 Factory Reset
00514     factoryResetChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback);
00515     factoryResetChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<bool>);
00516     // CHAR-12 Remain Connectable
00517     remainConnectableChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback);
00518     remainConnectableChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<bool>);
00519 
00520     // Create pointers to all characteristics in the GATT service
00521     charTable[0] = capabilitiesChar;
00522     charTable[1] = activeSlotChar;
00523     charTable[2] = advIntervalChar;
00524     charTable[3] = radioTxPowerChar;
00525     charTable[4] = advTxPowerChar;
00526     charTable[5] = lockStateChar;
00527     charTable[6] = unlockChar;
00528     charTable[7] = publicEcdhKeyChar;
00529     charTable[8] = eidIdentityKeyChar;
00530     charTable[9] = advSlotDataChar;
00531     charTable[10] = factoryResetChar;
00532     charTable[11] = remainConnectableChar;
00533 
00534     GattService configService(UUID_ES_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
00535 
00536     ble.gattServer().addService(configService);
00537     ble.gattServer().onDataWritten(this, &EddystoneService::onDataWrittenCallback);
00538     updateCharacteristicValues();
00539 }
00540 
00541 
00542 void EddystoneService::freeConfigCharacteristics(void)
00543 {
00544     delete capabilitiesChar;
00545     delete activeSlotChar;
00546     delete advIntervalChar;
00547     delete radioTxPowerChar;
00548     delete advTxPowerChar;
00549     delete lockStateChar;
00550     delete unlockChar;
00551     delete publicEcdhKeyChar;
00552     delete eidIdentityKeyChar;
00553     delete advSlotDataChar;
00554     delete factoryResetChar;
00555     delete remainConnectableChar;
00556 }
00557 
00558 void EddystoneService::stopEddystoneBeaconAdvertisements(void)
00559 {
00560     /* Unschedule callbacks */
00561 
00562     for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) {
00563         if (slotCallbackHandles[slot]) {
00564             eventQueue.cancel(slotCallbackHandles[slot]);
00565             slotCallbackHandles[slot] = NULL;
00566         }
00567     }
00568 
00569     if (radioManagerCallbackHandle) {
00570         eventQueue.cancel(radioManagerCallbackHandle);
00571         radioManagerCallbackHandle = NULL;
00572     }
00573 
00574     /* Stop any current Advs (ES Config or Beacon) */
00575     BLE::Instance().gap().stopAdvertising();
00576 }
00577 
00578 /*
00579  * Internal helper function used to update the GATT database following any
00580  * change to the internal state of the service object.
00581  */
00582 void EddystoneService::updateCharacteristicValues(void)
00583 {
00584     // Init variables for update
00585     uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]);
00586     int8_t radioTxPower = slotRadioTxPowerLevels[activeSlot];
00587     int8_t advTxPower = slotAdvTxPowerLevels[activeSlot];
00588     uint8_t* frame = slotToFrame(activeSlot);
00589     uint8_t slotLength = 0;
00590     uint8_t* slotData = NULL;
00591     memset(encryptedEidIdentityKey, 0, sizeof(encryptedEidIdentityKey));
00592 
00593     switch(slotFrameTypes[activeSlot]) {
00594         case EDDYSTONE_FRAME_UID:
00595           slotLength = uidFrame.getDataLength(frame);
00596           slotData = uidFrame.getData(frame);
00597           break;
00598         case EDDYSTONE_FRAME_URL:
00599           slotLength = urlFrame.getDataLength(frame);
00600           slotData = urlFrame.getData(frame);
00601           break;
00602         case EDDYSTONE_FRAME_TLM:
00603           updateRawTLMFrame(frame);
00604           slotLength = tlmFrame.getDataLength(frame);
00605           slotData = tlmFrame.getData(frame);
00606           break;
00607         case EDDYSTONE_FRAME_EID:
00608           slotLength = eidFrame.getDataLength(frame);
00609           slotData = eidFrame.getData(frame);
00610           aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey);
00611           break;
00612     }
00613 
00614     ble.gattServer().write(capabilitiesChar->getValueHandle(), reinterpret_cast<uint8_t *>(capabilities), sizeof(Capability_t));
00615     ble.gattServer().write(activeSlotChar->getValueHandle(), &activeSlot, sizeof(uint8_t));
00616     ble.gattServer().write(advIntervalChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beAdvInterval), sizeof(uint16_t));
00617     ble.gattServer().write(radioTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&radioTxPower), sizeof(int8_t));
00618     ble.gattServer().write(advTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&advTxPower), sizeof(int8_t));
00619     ble.gattServer().write(lockStateChar->getValueHandle(), &lockState, sizeof(uint8_t));
00620     ble.gattServer().write(unlockChar->getValueHandle(), unlockToken, sizeof(Lock_t));
00621     ble.gattServer().write(publicEcdhKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(publicEcdhKey), sizeof(PublicEcdhKey_t));
00622     ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(encryptedEidIdentityKey), sizeof(EidIdentityKey_t));
00623     ble.gattServer().write(advSlotDataChar->getValueHandle(), slotData, slotLength);
00624     ble.gattServer().write(factoryResetChar->getValueHandle(), &factoryReset, sizeof(uint8_t));
00625     ble.gattServer().write(remainConnectableChar->getValueHandle(), &remainConnectable, sizeof(uint8_t));
00626 }
00627 
00628 EddystoneService::EddystoneError_t EddystoneService::startEddystoneConfigAdvertisements(void)
00629 {
00630     stopEddystoneBeaconAdvertisements();
00631 
00632     if (advConfigInterval == 0) {
00633         // Nothing to do, the advertisement interval is 0
00634         return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL;
00635     }
00636 
00637     operationMode = EDDYSTONE_MODE_CONFIG;
00638 
00639     ble.gap().clearAdvertisingPayload();
00640 
00641     /* Accumulate the new payload */
00642     ble.gap().accumulateAdvertisingPayload(
00643         GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE
00644     );
00645     /* UUID is in different order in the ADV frame (!) */
00646     uint8_t reversedServiceUUID[sizeof(UUID_ES_BEACON_SERVICE)];
00647     for (size_t i = 0; i < sizeof(UUID_ES_BEACON_SERVICE); i++) {
00648         reversedServiceUUID[i] = UUID_ES_BEACON_SERVICE[sizeof(UUID_ES_BEACON_SERVICE) - i - 1];
00649     }
00650     ble.gap().accumulateAdvertisingPayload(
00651         GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
00652         reversedServiceUUID,
00653         sizeof(reversedServiceUUID)
00654     );
00655     ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG);
00656     setupEddystoneConfigScanResponse();
00657 
00658     ble.gap().setTxPower(radioTxPowerLevels[sizeof(PowerLevels_t)-1]); // Max Power for Config
00659     ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
00660     ble.gap().setAdvertisingInterval(advConfigInterval);
00661     ble.gap().startAdvertising();
00662 
00663     return EDDYSTONE_ERROR_NONE;
00664 }
00665 
00666 void EddystoneService::setupEddystoneConfigScanResponse(void)
00667 {
00668     ble.gap().clearScanResponse();
00669     ble.gap().accumulateScanResponse(
00670         GapAdvertisingData::COMPLETE_LOCAL_NAME,
00671         reinterpret_cast<const uint8_t *>(deviceName),
00672         strlen(deviceName)
00673     );
00674     ble.gap().accumulateScanResponse(
00675         GapAdvertisingData::TX_POWER_LEVEL,
00676         reinterpret_cast<uint8_t *>(&advTxPowerLevels[sizeof(PowerLevels_t)-1]),
00677         sizeof(uint8_t)
00678     );
00679 }
00680 
00681 /* WRITE AUTHORIZATION */
00682 
00683 void EddystoneService::writeUnlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
00684 {
00685     if (lockState == UNLOCKED) {
00686         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
00687     } else if (authParams->len != sizeof(Lock_t)) {
00688         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
00689     } else if (authParams->offset != 0) {
00690         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
00691     } else if (memcmp(authParams->data, unlockToken, sizeof(Lock_t)) != 0) {
00692         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
00693     } else {
00694         authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
00695     }
00696 }
00697 
00698 void EddystoneService::writeVarLengthDataAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
00699 {
00700    if (lockState == LOCKED) {
00701         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
00702     } else if ((authParams->len > 34) || (authParams->len == 0)) {  
00703         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
00704     } else {
00705         authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
00706     }
00707 }
00708 
00709 
00710 void EddystoneService::writeLockStateAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
00711 {
00712    if (lockState == LOCKED) {
00713         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
00714     } else if ((authParams->len != sizeof(uint8_t)) && (authParams->len != (sizeof(uint8_t) + sizeof(Lock_t)))) {
00715         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
00716     } else if (authParams->offset != 0) {
00717         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
00718     } else {
00719         authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
00720     }
00721 }
00722 
00723 template <typename T>
00724 void EddystoneService::writeBasicAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
00725 {
00726     if (lockState == LOCKED) {
00727         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
00728     } else if (authParams->len != sizeof(T)) {
00729         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
00730     } else if (authParams->offset != 0) {
00731         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
00732     } else {
00733         authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
00734     }
00735 }
00736 
00737 template <typename T>
00738 void EddystoneService::writeActiveSlotAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
00739 {
00740     if (lockState == LOCKED) {
00741         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
00742     } else if (authParams->len != sizeof(T)) {
00743         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
00744     } else if (*(authParams->data) > MAX_ADV_SLOTS -1) {
00745         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
00746     } else if (authParams->offset != 0) {
00747         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
00748     } else {
00749         authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
00750     }
00751 }
00752 
00753 /* READ AUTHORIZTION */
00754 
00755 void EddystoneService::readBasicTestLockAuthorizationCallback(GattReadAuthCallbackParams *authParams)
00756 {
00757     LOG(("\r\nDO READ BASIC TEST LOCK slot=%d\r\n", activeSlot));
00758     if (lockState == LOCKED) {
00759         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
00760     } else {
00761         authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
00762     }
00763 }
00764 
00765 void EddystoneService::readEidIdentityAuthorizationCallback(GattReadAuthCallbackParams *authParams)
00766 {
00767     LOG(("\r\nDO READ EID IDENTITY slot=%d\r\n", activeSlot));
00768     aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey);
00769     int sum = 0;
00770     // Test if the IdentityKey is all zeros for this slot
00771     for (uint8_t i = 0; i < sizeof(EidIdentityKey_t); i++) {
00772         sum = sum + slotEidIdentityKeys[activeSlot][i];
00773     }
00774     ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), encryptedEidIdentityKey, sizeof(EidIdentityKey_t));
00775 
00776     // When the array is all zeros, the key has not been set, so return fault
00777     if ((lockState == LOCKED) || (sum == 0)) { 
00778         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
00779     } else {
00780         authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
00781     }
00782 }
00783 
00784 void EddystoneService::readPublicEcdhKeyAuthorizationCallback(GattReadAuthCallbackParams *authParams)
00785 {
00786     LOG(("\r\nDO READ BEACON PUBLIC ECDH KEY (LE) slot=%d\r\n", activeSlot));
00787 
00788     ble.gattServer().write(publicEcdhKeyChar->getValueHandle(), publicEcdhKeyLE, sizeof(PublicEcdhKey_t));
00789     
00790     // When the array is all zeros, the key has not been set, so return fault
00791     if (lockState == LOCKED) { 
00792         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
00793     } else {
00794         authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
00795     }
00796 }
00797 
00798 void EddystoneService::readDataAuthorizationCallback(GattReadAuthCallbackParams *authParams)
00799 {
00800     LOG(("\r\nDO READ ADV-DATA : slot=%d\r\n", activeSlot));
00801     uint8_t frameType = slotFrameTypes[activeSlot];
00802     uint8_t* frame = slotToFrame(activeSlot);
00803     uint8_t slotLength = 1;
00804     uint8_t buf[14] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0};
00805     uint8_t* slotData = buf;
00806  
00807     if (lockState == LOCKED) {
00808         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
00809         return;
00810     }
00811     LOG(("IN READ ADV-DATA AFTER LOCK TEST frameType=%d\r\n", frameType));
00812     if (testValidFrame(frame) ) { // Check the frame has valid data before proceeding
00813         switch(frameType) {
00814             case EDDYSTONE_FRAME_UID:
00815                 LOG(("READ ADV-DATA UID SLOT DATA slot=%d\r\n", activeSlot));
00816                 slotLength = uidFrame.getDataLength(frame);
00817                 slotData = uidFrame.getData(frame);
00818                 break;
00819             case EDDYSTONE_FRAME_URL:
00820                 LOG(("READ ADV-DATA URL SLOT DATA slot=%d\r\n", activeSlot));
00821                 slotLength = urlFrame.getDataLength(frame);
00822                 slotData = urlFrame.getData(frame);
00823                 break;
00824             case EDDYSTONE_FRAME_TLM:
00825                 LOG(("READ ADV-DATA TLM SLOT DATA slot=%d\r\n", activeSlot));
00826                 updateRawTLMFrame(frame);
00827                 slotLength = tlmFrame.getDataLength(frame);
00828                 slotData = tlmFrame.getData(frame);
00829                 LOG(("READ ADV-DATA AFTER T/E TLM length=%d\r\n", slotLength)); 
00830                 LOG(("Data=")); logPrintHex(slotData, 18);
00831                 break;
00832             case EDDYSTONE_FRAME_EID:
00833                 LOG(("READ ADV-DATA EID SLOT DATA slot=%d\r\n", activeSlot));
00834                 slotLength = 14;
00835                 buf[0] = EIDFrame::FRAME_TYPE_EID;
00836                 buf[1] = slotEidRotationPeriodExps[activeSlot];
00837                 // Add time as a big endian 32 bit number
00838                 uint32_t timeSecs = timeSinceBootTimer.read_ms() / 1000;
00839                 buf[2] = (timeSecs  >> 24) & 0xff;
00840                 buf[3] = (timeSecs >> 16) & 0xff;
00841                 buf[4] = (timeSecs >> 8) & 0xff;
00842                 buf[5] = timeSecs & 0xff;
00843                 memcpy(buf + 6, eidFrame.getEid(frame), 8);
00844                 slotData = buf;
00845                 break;
00846         }
00847     }
00848     LOG(("IN READ ADV-DATA AFTER FRAME PROCESSING slot=%d\r\n", activeSlot));
00849     ble.gattServer().write(advSlotDataChar->getValueHandle(), slotData, slotLength);
00850     authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
00851 }
00852 
00853 bool EddystoneService::testValidFrame(uint8_t* frame) {
00854     return (frame[0] != 0 ) ? true : false; 
00855 }
00856 
00857 void EddystoneService::readUnlockAuthorizationCallback(GattReadAuthCallbackParams *authParams)
00858 {
00859     LOG(("\r\nDO READ UNLOCK slot=%d\r\n", activeSlot));
00860     if (lockState == UNLOCKED) {
00861         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
00862         return;
00863     }
00864     // Update the challenge ready for the characteristic read
00865     generateRandom(challenge, sizeof(Lock_t));
00866     aes128Encrypt(unlockKey, challenge, unlockToken);
00867     ble.gattServer().write(unlockChar->getValueHandle(), reinterpret_cast<uint8_t *>(challenge), sizeof(Lock_t));     
00868     authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
00869 }
00870 
00871 void EddystoneService::readAdvIntervalAuthorizationCallback(GattReadAuthCallbackParams *authParams)
00872 {
00873     LOG(("\r\nDO READ ADV INTERVAL slot=%d\r\n", activeSlot));
00874     if (lockState == LOCKED) {
00875         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
00876         return;
00877     }
00878     uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]);
00879     ble.gattServer().write(advIntervalChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beAdvInterval), sizeof(uint16_t));
00880     authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
00881 }
00882 
00883 void EddystoneService::readRadioTxPowerAuthorizationCallback(GattReadAuthCallbackParams *authParams)
00884 {
00885     LOG(("\r\nDO READ RADIO TXPOWER slot=%d\r\n", activeSlot));
00886     if (lockState == LOCKED) {
00887         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
00888         return;
00889     }
00890     int8_t radioTxPower = slotRadioTxPowerLevels[activeSlot];
00891     ble.gattServer().write(radioTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&radioTxPower), sizeof(int8_t));
00892     authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
00893 }
00894 
00895 void EddystoneService::readAdvTxPowerAuthorizationCallback(GattReadAuthCallbackParams *authParams)
00896 {
00897     LOG(("\r\nDO READ ADV TXPOWER slot=%d\r\n", activeSlot));
00898     if (lockState == LOCKED) {
00899         authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
00900         return;
00901     }
00902     int8_t advTxPower = slotAdvTxPowerLevels[activeSlot];
00903     ble.gattServer().write(advTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&advTxPower), sizeof(int8_t));
00904     authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
00905 }
00906 
00907 /*
00908  * This callback is invoked when a GATT client attempts to modify any of the
00909  * characteristics of this service. Attempts to do so are also applied to
00910  * the internal state of this service object.
00911  */
00912 void EddystoneService::onDataWrittenCallback(const GattWriteCallbackParams *writeParams)
00913 {
00914     uint16_t handle = writeParams->handle;
00915     LOG(("\r\nDO WRITE: Handle=%d Len=%d\r\n", handle, writeParams->len));
00916     // CHAR-1 CAPABILITIES
00917             /* capabilitySlotChar is READ ONLY */
00918     // CHAR-2 ACTIVE SLOT
00919     if (handle == activeSlotChar->getValueHandle()) {
00920         LOG(("Write: Active Slot Handle=%d\r\n", handle));
00921         uint8_t slot = *(writeParams->data);
00922         LOG(("Active Slot=%d\r\n", slot));
00923         // Ensure slot does not exceed limit, or set highest slot
00924         if (slot < MAX_ADV_SLOTS) {
00925             activeSlot = slot;
00926         }
00927         ble.gattServer().write(activeSlotChar->getValueHandle(), &activeSlot, sizeof(uint8_t));
00928     // CHAR-3 ADV INTERVAL
00929     } else if (handle == advIntervalChar->getValueHandle()) {
00930         LOG(("Write: Interval Handle=%d\r\n", handle));
00931         uint16_t interval = correctAdvertisementPeriod(swapEndian(*((uint16_t *)(writeParams->data))));
00932         slotAdvIntervals[activeSlot] = interval; // Store this value for reading
00933         uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]);
00934         ble.gattServer().write(advIntervalChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beAdvInterval), sizeof(uint16_t));
00935     // CHAR-4 RADIO TX POWER
00936     } else if (handle == radioTxPowerChar->getValueHandle()) {
00937         LOG(("Write: RADIO Power Handle=%d\r\n", handle));
00938         int8_t radioTxPower = *(writeParams->data);
00939         uint8_t index = radioTxPowerToIndex(radioTxPower);
00940         radioTxPower = radioTxPowerLevels[index]; // Power now corrected to nearest allowed power
00941         slotRadioTxPowerLevels[activeSlot] = radioTxPower; // Store by slot number
00942         int8_t advTxPower = advTxPowerLevels[index]; // Determine adv power equivalent
00943         slotAdvTxPowerLevels[activeSlot] = advTxPower;
00944         setFrameTxPower(activeSlot, advTxPower); // Set the actual frame radio TxPower for this slot
00945         ble.gattServer().write(radioTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&radioTxPower), sizeof(int8_t));
00946     // CHAR-5 ADV TX POWER
00947     } else if (handle == advTxPowerChar->getValueHandle()) {
00948         LOG(("Write: ADV Power Handle=%d\r\n", handle));
00949         int8_t advTxPower = *(writeParams->data);
00950         slotAdvTxPowerLevels[activeSlot] = advTxPower;
00951         setFrameTxPower(activeSlot, advTxPower); // Update the actual frame Adv TxPower for this slot
00952         ble.gattServer().write(advTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&advTxPower), sizeof(int8_t));
00953     // CHAR-6 LOCK STATE
00954     } else if (handle == lockStateChar->getValueHandle()) {
00955         LOG(("Write: Lock State Handle=%d\r\n", handle));
00956         uint8_t newLockState = *(writeParams->data);
00957         if ((writeParams->len == sizeof(uint8_t)) || (writeParams->len == sizeof(uint8_t) + sizeof(Lock_t))) {
00958             if ((newLockState == LOCKED) || (newLockState == UNLOCKED) || (newLockState == UNLOCKED_AUTO_RELOCK_DISABLED)) {
00959                 lockState = newLockState;
00960             }
00961         }
00962         if ((newLockState == LOCKED) && (writeParams->len == (sizeof(uint8_t) + sizeof(Lock_t))) ) {
00963             // And sets the new secret lock code if present
00964             uint8_t encryptedNewKey[sizeof(Lock_t)];
00965             uint8_t newKey[sizeof(Lock_t)];
00966             memcpy(encryptedNewKey, (writeParams->data)+1, sizeof(Lock_t));
00967             // Decrypt the new key
00968             aes128Decrypt(unlockKey, encryptedNewKey, newKey);
00969             memcpy(unlockKey, newKey, sizeof(Lock_t));
00970         }
00971         ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(uint8_t));
00972     // CHAR-7 UNLOCK
00973     } else if (handle == unlockChar->getValueHandle()) {
00974        LOG(("Write: Unlock Handle=%d\r\n", handle));
00975        // NOTE: Actual comparison with unlock code is done in:
00976        // writeUnlockAuthorizationCallback(...)  which is executed before this method call.
00977        lockState = UNLOCKED;
00978        // Regenerate challenge and expected unlockToken for Next unlock operation
00979        generateRandom(challenge, sizeof(Lock_t));
00980        aes128Encrypt(unlockKey, challenge, unlockToken);
00981        // Update Chars
00982        ble.gattServer().write(unlockChar->getValueHandle(), reinterpret_cast<uint8_t *>(challenge), sizeof(Lock_t));      // Update the challenge
00983        ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(uint8_t)); // Update the lock
00984     // CHAR-8 PUBLIC ECDH KEY
00985         /* PublicEchdChar is READ ONLY */
00986     // CHAR-9 EID INDENTITY KEY
00987         /* EidIdentityChar is READ ONLY */
00988     // CHAR-10 ADV DATA
00989     } else if (handle == advSlotDataChar->getValueHandle()) {
00990         LOG(("Write: Adv Slot DATA Handle=%d\r\n", handle));
00991         uint8_t* frame = slotToFrame(activeSlot);
00992         int8_t advTxPower = slotAdvTxPowerLevels[activeSlot];
00993         uint8_t writeFrameFormat = *(writeParams->data);
00994         uint8_t writeFrameLen = (writeParams->len);
00995         uint8_t writeData[34];
00996         uint8_t serverPublicEcdhKey[32];
00997         
00998         if (writeFrameLen != 0) {
00999             writeFrameLen--; // Remove the Format byte from the count
01000         } else {
01001             writeFrameFormat = UNDEFINED_FRAME_FORMAT; // Undefined format
01002         }
01003         
01004         memcpy(writeData, (writeParams->data) + 1, writeFrameLen);
01005         LOG(("ADV Data Write=%d,%d\r\n", writeFrameFormat, writeFrameLen));
01006         switch(writeFrameFormat) {
01007             case UIDFrame::FRAME_TYPE_UID:
01008                 if (writeFrameLen == 16) {
01009                     uidFrame.setData(frame, advTxPower,reinterpret_cast<const uint8_t *>((writeParams->data) + 1));
01010                     slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_UID;
01011                 } else if (writeFrameLen == 0) {
01012                     uidFrame.clearFrame(frame);
01013                 }
01014                 break;
01015             case URLFrame::FRAME_TYPE_URL:
01016                if (writeFrameLen <= 18) {
01017                     urlFrame.setData(frame, advTxPower, reinterpret_cast<const uint8_t*>((writeParams->data) + 1), writeFrameLen );
01018                     slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_URL;
01019                 } else if (writeFrameLen == 0) {
01020                     urlFrame.clearFrame(frame);
01021                 }
01022                 break;
01023             case TLMFrame::FRAME_TYPE_TLM:
01024                 if (writeFrameLen == 0) {
01025                     updateRawTLMFrame(frame);
01026                     tlmFrame.setData(frame);
01027                     int slot = getEidSlot();
01028                     LOG(("WRITE: Testing if TLM or ETLM=%d\r\n", slot));
01029                     if (slot != NO_EID_SLOT_SET) {
01030                         LOG(("WRITE: Configuring ETLM Slot time=%d\r\n", timeSinceBootTimer.read_ms() / 1000));
01031                         tlmFrame.encryptData(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSinceBootTimer.read_ms() / 1000);
01032                     }
01033                     slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_TLM;
01034                 }
01035                 break;
01036             case EIDFrame::FRAME_TYPE_EID:
01037                 LOG(("EID Len=%d\r\n", writeFrameLen));
01038                 if (writeFrameLen == 17) {
01039                     // Least secure
01040                     LOG(("EID Insecure branch\r\n"));
01041                     aes128Decrypt(unlockKey, writeData, slotEidIdentityKeys[activeSlot]);
01042                     slotEidRotationPeriodExps[activeSlot] = writeData[16]; // index 16 is the exponent
01043                     ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(&writeData), sizeof(EidIdentityKey_t));
01044                 } else if (writeFrameLen == 33 ) {  
01045                     // Most secure
01046                     memcpy(serverPublicEcdhKey, writeData, 32);
01047                     ble.gattServer().write(publicEcdhKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(&serverPublicEcdhKey), sizeof(PublicEcdhKey_t));
01048                     LOG(("ServerPublicEcdhKey=")); logPrintHex(serverPublicEcdhKey, 32);
01049                     slotEidRotationPeriodExps[activeSlot] = writeData[32]; // index 32 is the exponent
01050                     LOG(("Exponent=%i\r\n", writeData[32]));
01051                     LOG(("genBeaconKeyRC=%x\r\n", genBeaconKeyRC));
01052                     LOG(("BeaconPrivateEcdhKey=")); logPrintHex(privateEcdhKey, 32);
01053                     LOG(("BeaconPublicEcdhKey=")); logPrintHex(publicEcdhKey, 32);
01054                     LOG(("genECDHShareKey\r\n"));
01055                     int rc = eidFrame.genEcdhSharedKey(privateEcdhKey, publicEcdhKey, serverPublicEcdhKey, slotEidIdentityKeys[activeSlot]);
01056                     LOG(("Gen Keys RC = %x\r\n", rc));
01057                     LOG(("Generated eidIdentityKey=")); logPrintHex(slotEidIdentityKeys[activeSlot], 16);
01058                     aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey);
01059                     LOG(("encryptedEidIdentityKey=")); logPrintHex(encryptedEidIdentityKey, 16);      
01060                     ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(&encryptedEidIdentityKey), sizeof(EidIdentityKey_t));
01061                 } else if (writeFrameLen == 0) {
01062                     // Reset eidFrame
01063                     eidFrame.clearFrame(frame);
01064                     break;
01065                 } else {
01066                     break; // Do nothing, this is not a recognized Frame length
01067                 }
01068                 // Establish the new frame type
01069                 slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_EID;
01070                 nextEidSlot = activeSlot; // This was the last one updated
01071                 LOG(("update Eid Frame\r\n"));
01072                 // Generate ADV frame packet from EidIdentity Key
01073                 eidFrame.update(frame, slotEidIdentityKeys[activeSlot], slotEidRotationPeriodExps[activeSlot], timeSinceBootTimer.read_ms() / 1000);
01074                 LOG(("END update Eid Frame\r\n"));
01075                 break;
01076             default:
01077                 frame[0] = 0; // Frame format unknown so clear the entire frame by writing 0 to its length
01078                 break;
01079         }
01080         // Read takes care of setting the Characteristic  Value
01081     // CHAR-11 FACTORY RESET
01082     } else if (handle == factoryResetChar->getValueHandle() && (*((uint8_t *)writeParams->data) != 0)) {
01083         LOG(("Write: Factory Reset: Handle=%d\r\n", handle));
01084         // Reset params to default values
01085         doFactoryReset();
01086         // Update all characteristics based on params
01087         updateCharacteristicValues();
01088     // CHAR-12 REMAIN CONNECTABLE
01089     } else if (handle == remainConnectableChar->getValueHandle()) {
01090         LOG(("Write: Remain Connectable Handle=%d\r\n", handle));
01091         remainConnectable = *(writeParams->data);
01092         ble.gattServer().write(remainConnectableChar->getValueHandle(), &remainConnectable, sizeof(uint8_t));
01093     }
01094 
01095 }
01096 
01097 void EddystoneService::setFrameTxPower(uint8_t slot, int8_t advTxPower) {
01098     uint8_t* frame = slotToFrame(slot);
01099     uint8_t frameType = slotFrameTypes[slot];
01100     switch (frameType) {
01101         case UIDFrame::FRAME_TYPE_UID:
01102            uidFrame.setAdvTxPower(frame, advTxPower);
01103            break;
01104         case URLFrame::FRAME_TYPE_URL:
01105            urlFrame.setAdvTxPower(frame, advTxPower);
01106            break;
01107         case EIDFrame::FRAME_TYPE_EID:
01108            eidFrame.setAdvTxPower(frame, advTxPower);
01109            break;
01110     }
01111 }
01112 
01113 uint8_t EddystoneService::radioTxPowerToIndex(int8_t txPower) {
01114     // NOTE: txPower is an 8-bit signed number
01115     uint8_t size = sizeof(PowerLevels_t);
01116     // Look for the value in range (or next biggest value)
01117     for (uint8_t i = 0; i < size; i++) {
01118       if (txPower <= radioTxPowerLevels[i]) {
01119           return i;
01120       }
01121     }
01122     return size - 1;
01123 }
01124 
01125 /** AES128 encrypts a 16-byte input array with a key, resulting in a 16-byte output array */
01126 void EddystoneService::aes128Encrypt(uint8_t key[], uint8_t input[], uint8_t output[]) {
01127     mbedtls_aes_context ctx;
01128     mbedtls_aes_init(&ctx);
01129     mbedtls_aes_setkey_enc(&ctx, key, 8 * sizeof(Lock_t));
01130     mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, input, output);
01131     mbedtls_aes_free(&ctx);
01132 }
01133 
01134 /** AES128 decrypts a 16-byte input array with a key, resulting in a 16-byte output array */
01135 void EddystoneService::aes128Decrypt(uint8_t key[], uint8_t input[], uint8_t output[]) {
01136     mbedtls_aes_context ctx;
01137     mbedtls_aes_init(&ctx);
01138     mbedtls_aes_setkey_dec(&ctx, key, 8 * sizeof(Lock_t));
01139     mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_DECRYPT, input, output);
01140     mbedtls_aes_free(&ctx);
01141 }
01142 
01143 
01144 
01145 #ifdef HARDWARE_RANDOM_NUM_GENERATOR
01146 // Generates a set of random values in byte array[size] based on hardware source
01147 void EddystoneService::generateRandom(uint8_t ain[], int size) {
01148     mbedtls_entropy_context entropy;
01149     mbedtls_entropy_init(&entropy);
01150     // init entropy source
01151     eddystoneRegisterEntropySource(&entropy);
01152     mbedtls_ctr_drbg_context ctr_drbg;
01153     mbedtls_ctr_drbg_init(&ctr_drbg);
01154     mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
01155     mbedtls_ctr_drbg_random(&ctr_drbg, ain, size);
01156     mbedtls_ctr_drbg_free(&ctr_drbg);
01157     mbedtls_entropy_free(&entropy);
01158     return;
01159 }
01160 #else
01161 // Generates a set of random values in byte array[size] seeded by the clock(ms)
01162 void EddystoneService::generateRandom(uint8_t ain[], int size) {
01163     int i;
01164     // Random seed based on boot time in milliseconds
01165     srand(timeSinceBootTimer.read_ms());
01166     for (i = 0; i < size; i++) {
01167         ain[i] = rand() % 256;
01168     }
01169     return;
01170 }
01171 #endif
01172 
01173 /*
01174 // ALTERNATE Better Random number generator (but has Memory usage issues)
01175 // Generates a set of random values in byte array[size]
01176 
01177 */
01178 
01179 /** Reverse Even sized Array endianess: Big to Little or Little to Big */
01180 void EddystoneService::swapEndianArray(uint8_t ptrIn[], uint8_t ptrOut[], int size) {
01181     int i;
01182     for (i = 0; i < size; i++) {
01183         ptrOut[i] = ptrIn[size - i - 1];
01184     }
01185     return;
01186 }
01187 
01188 /** Reverse endianess: Big to Little or Little to Big */
01189 uint16_t EddystoneService::swapEndian(uint16_t arg) {
01190     return (arg / 256) + (arg % 256) * 256;
01191 }
01192 
01193 uint16_t EddystoneService::correctAdvertisementPeriod(uint16_t beaconPeriodIn) const
01194 {
01195     /* Re-map beaconPeriod to within permissible bounds if necessary. */
01196     if (beaconPeriodIn != 0) {
01197         if (beaconPeriodIn < ble.gap().getMinNonConnectableAdvertisingInterval()) {
01198             return ble.gap().getMinNonConnectableAdvertisingInterval();
01199         } else if (beaconPeriodIn > ble.gap().getMaxAdvertisingInterval()) {
01200             return ble.gap().getMaxAdvertisingInterval();
01201         }
01202     }
01203     return beaconPeriodIn;
01204 }
01205 
01206 void EddystoneService::logPrintHex(uint8_t* a, int len) {
01207     for (int i = 0; i < len; i++) {
01208         LOG(("%x%x", a[i] >> 4, a[i] & 0x0f ));
01209     }
01210     LOG(("\r\n"));
01211 }
01212 
01213 void EddystoneService::setRandomMacAddress(void) {
01214 #ifdef EID_RANDOM_MAC
01215     uint8_t macAddress[6]; // 48 bit Mac Address
01216     generateRandom(macAddress, 6);
01217     macAddress[5] |= 0xc0; // Ensure upper two bits are 11's for Random Add
01218     ble.setAddress(BLEProtocol::AddressType::RANDOM_STATIC, macAddress);
01219 #endif
01220 }
01221 
01222 int EddystoneService::getEidSlot(void) {
01223     int eidSlot = NO_EID_SLOT_SET; // by default;
01224     for (int i = 0; i < MAX_ADV_SLOTS; i++) {
01225         if (slotFrameTypes[nextEidSlot] == EDDYSTONE_FRAME_EID) {
01226              eidSlot = nextEidSlot;
01227              nextEidSlot = (nextEidSlot-1) % MAX_ADV_SLOTS;
01228              break;
01229         }
01230         nextEidSlot = (nextEidSlot-1) % MAX_ADV_SLOTS; // ensure the slot numbers wrap
01231     }
01232     return eidSlot;
01233 }
01234     
01235 
01236 const uint8_t EddystoneService::slotDefaultUids[MAX_ADV_SLOTS][16] = EDDYSTONE_DEFAULT_SLOT_UIDS;
01237 
01238 const uint8_t EddystoneService::slotDefaultEidIdentityKeys[MAX_ADV_SLOTS][16] = EDDYSTONE_DEFAULT_SLOT_EID_IDENTITY_KEYS;
01239 
01240 const uint8_t EddystoneService::allSlotsDefaultEid[8] = {0,0,0,0,0,0,0,0};