/* * Copyright (C) 2018 "IoT.bzh" * Author Jonathan Aillet * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define _GNU_SOURCE #include #include #include #include #include "hal-bt.h" #include "hal-bt-data.h" /******************************************************************************* * Bt device data handling functions * ******************************************************************************/ int HalBtDataRemoveSelectedBtDeviceFromList(struct HalBtDeviceData **firstBtDeviceData, struct HalBtDeviceData *btDeviceDataToRemove) { struct HalBtDeviceData *currentBtDevice, *matchingBtDevice; if(! firstBtDeviceData || ! btDeviceDataToRemove) return -1; currentBtDevice = *firstBtDeviceData; if(currentBtDevice == btDeviceDataToRemove) { *firstBtDeviceData = currentBtDevice->next; matchingBtDevice = currentBtDevice; } else { while(currentBtDevice && currentBtDevice->next != btDeviceDataToRemove) currentBtDevice = currentBtDevice->next; if(currentBtDevice) { matchingBtDevice = currentBtDevice->next; currentBtDevice->next = currentBtDevice->next->next; } else { return -2; } } free(matchingBtDevice->hci); free(matchingBtDevice->uid); free(matchingBtDevice->name); free(matchingBtDevice->address); free(matchingBtDevice); return 0; } struct HalBtDeviceData *HalBtDataAddBtDeviceToBtDeviceList(struct HalBtDeviceData **firstBtDeviceData, json_object *currentSingleBtDeviceDataJ) { char *currentBtDeviceName = NULL, *currentBtDeviceAddress, *currentBtDevicePath, *currentBtDeviceHci; struct HalBtDeviceData *currentBtDeviceData; if(! firstBtDeviceData || ! currentSingleBtDeviceDataJ) return NULL; currentBtDeviceData = *firstBtDeviceData; if(! currentBtDeviceData) { currentBtDeviceData = (struct HalBtDeviceData *) calloc(1, sizeof(struct HalBtDeviceData)); if(! currentBtDeviceData) return NULL; *firstBtDeviceData = currentBtDeviceData; } else { while(currentBtDeviceData->next) currentBtDeviceData = currentBtDeviceData->next; currentBtDeviceData->next = calloc(1, sizeof(struct HalBtDeviceData)); if(! currentBtDeviceData) return NULL; currentBtDeviceData = currentBtDeviceData->next; } if(wrap_json_unpack(currentSingleBtDeviceDataJ, "{s:s s:s}", "address", ¤tBtDeviceAddress, "adapter", ¤tBtDevicePath)) { HalBtDataRemoveSelectedBtDeviceFromList(firstBtDeviceData, currentBtDeviceData); return NULL; } if(asprintf(¤tBtDeviceData->uid, "BT#%s", currentBtDeviceAddress) == -1) { HalBtDataRemoveSelectedBtDeviceFromList(firstBtDeviceData, currentBtDeviceData); return NULL; } currentBtDeviceHci = strtok(currentBtDevicePath, "/"); while(currentBtDeviceHci && strncmp(currentBtDeviceHci, "hci", 3)) currentBtDeviceHci = strtok(NULL, "/"); if((! currentBtDeviceHci) || (! (currentBtDeviceData->hci = strdup(currentBtDeviceHci)))) { HalBtDataRemoveSelectedBtDeviceFromList(firstBtDeviceData, currentBtDeviceData); return NULL; } wrap_json_unpack(currentSingleBtDeviceDataJ, "{s:s}", "name", ¤tBtDeviceName); if (currentBtDeviceName) { if(! (currentBtDeviceData->name = strdup(currentBtDeviceName))) { HalBtDataRemoveSelectedBtDeviceFromList(firstBtDeviceData, currentBtDeviceData); return NULL; } } if(! (currentBtDeviceData->address = strdup(currentBtDeviceAddress))) { HalBtDataRemoveSelectedBtDeviceFromList(firstBtDeviceData, currentBtDeviceData); return NULL; } return currentBtDeviceData; } int HalBtDataGetNumberOfBtDeviceInList(struct HalBtDeviceData **firstBtDeviceData) { unsigned int btDeviceNb = 0; struct HalBtDeviceData *currentBtDeviceData; if(! firstBtDeviceData) return -1; currentBtDeviceData = *firstBtDeviceData; while(currentBtDeviceData) { currentBtDeviceData = currentBtDeviceData->next; btDeviceNb++; } return btDeviceNb; } struct HalBtDeviceData *HalBtDataSearchBtDeviceByAddress(struct HalBtDeviceData **firstBtDeviceData, char *btAddress) { struct HalBtDeviceData *currentBtDevice; if(! firstBtDeviceData || ! btAddress) return NULL; currentBtDevice = *firstBtDeviceData; while(currentBtDevice) { if(! strcmp(btAddress, currentBtDevice->address)) return currentBtDevice; currentBtDevice = currentBtDevice->next; } return NULL; } int HalBtDataHandleReceivedMediaBtDeviceData(struct HalBtPluginData *halBtPluginData, json_object *currentSingleBtMediaDataJ) { const char *currentBtType = NULL, *currentBtAction = NULL, *currentBtDeviceRaw = NULL; char *currentBtDeviceAddress = NULL, *tmp = NULL; AFB_ApiT api = halBtPluginData->currentHalApiHandle; struct HalBtDeviceData *currentBtDevice = NULL; int currentBtDeviceIsConnected; wrap_json_unpack(currentSingleBtMediaDataJ, "{s:s}", "type", ¤tBtType); if(currentBtType && strcmp(currentBtType, "transport")) return 0; wrap_json_unpack(currentSingleBtMediaDataJ, "{s:s}", "action", ¤tBtAction); if(!currentBtAction) return 0; if(strcmp(currentBtAction, "added") && strcmp(currentBtAction, "removed")) return 0; wrap_json_unpack(currentSingleBtMediaDataJ, "{s:s}", "device", ¤tBtDeviceRaw); if(!currentBtDeviceRaw || strlen(currentBtDeviceRaw) < 4) return 0; currentBtDeviceAddress = strdup(¤tBtDeviceRaw[4]); // TODO: don't assume all bluetooth stacks produce device names that an // address can be groked from. tmp = currentBtDeviceAddress; for (;*tmp; tmp++) if (*tmp == '_') *tmp = ':'; currentBtDeviceIsConnected = !strcmp(currentBtAction, "added"); currentBtDevice = HalBtDataSearchBtDeviceByAddress(&halBtPluginData->first, currentBtDeviceAddress); if(currentBtDevice && ! currentBtDeviceIsConnected) { if(HalBtDataRemoveSelectedBtDeviceFromList(&halBtPluginData->first, currentBtDevice)) return -1; AFB_ApiInfo(api, "Bluetooth device (address = %s) successfully removed from list", currentBtDeviceAddress); if(halBtPluginData->selectedBtDevice == currentBtDevice) { halBtPluginData->selectedBtDevice = halBtPluginData->first; AFB_ApiDebug(api, "Bluetooth selected device changed to '%s'", halBtPluginData->selectedBtDevice ? halBtPluginData->selectedBtDevice->address : "none"); } } else if(! currentBtDevice && currentBtDeviceIsConnected) { json_object_object_add(currentSingleBtMediaDataJ, "address", json_object_new_string(currentBtDeviceAddress)); if(! HalBtDataAddBtDeviceToBtDeviceList(&halBtPluginData->first, currentSingleBtMediaDataJ)) return -1; AFB_ApiInfo(api, "Bluetooth device (address = %s) successfully added to list", currentBtDeviceAddress); if(! halBtPluginData->selectedBtDevice) { halBtPluginData->selectedBtDevice = halBtPluginData->first; AFB_ApiDebug(api, "Bluetooth selected device changed to '%s'", halBtPluginData->selectedBtDevice ? halBtPluginData->selectedBtDevice->address : "none"); } } return 0; } int HalBtDataHandleReceivedSingleBtDeviceData(struct HalBtPluginData *halBtPluginData, json_object *currentSingleBtDeviceDataJ) { unsigned int currentBtDeviceIsConnected = 0; unsigned int currentBtDeviceIsA2DP = 0; unsigned int idx = 0; char *currentBtDeviceAddress; json_object *currentBtAllProfilesJ = NULL, *currentBtCurrentProfileJ; struct HalBtDeviceData *currentBtDevice; if(! halBtPluginData || ! currentSingleBtDeviceDataJ) return -1; if(wrap_json_unpack(currentSingleBtDeviceDataJ, "{s:s s:b s?:o}", "address", ¤tBtDeviceAddress, "connected", ¤tBtDeviceIsConnected, "uuids", ¤tBtAllProfilesJ)) { return -2; } if(! currentBtAllProfilesJ) { AFB_ApiInfo(halBtPluginData->currentHalApiHandle, "Bluetooth device (address = %s) has no specified profiles, ignore it", currentBtDeviceAddress); return 0; } if(json_object_is_type(currentBtAllProfilesJ, json_type_array)) { int btProfilesCount = json_object_array_length(currentBtAllProfilesJ); while(idx < btProfilesCount) { currentBtCurrentProfileJ = json_object_array_get_idx(currentBtAllProfilesJ, idx); if(json_object_is_type(currentBtCurrentProfileJ, json_type_string) && ! strncasecmp(json_object_get_string(currentBtCurrentProfileJ), A2DP_AUDIOSOURCE_UUID, sizeof(A2DP_AUDIOSOURCE_UUID))) { currentBtDeviceIsA2DP = 1; break; } idx++; } } if(! currentBtDeviceIsA2DP) return 0; currentBtDevice = HalBtDataSearchBtDeviceByAddress(&halBtPluginData->first, currentBtDeviceAddress); if(currentBtDevice && ! currentBtDeviceIsConnected) { if(HalBtDataRemoveSelectedBtDeviceFromList(&halBtPluginData->first, currentBtDevice)) return -3; AFB_ApiInfo(halBtPluginData->currentHalApiHandle, "Bluetooth device (address = %s) successfully removed from list", currentBtDeviceAddress); if(halBtPluginData->selectedBtDevice == currentBtDevice) { halBtPluginData->selectedBtDevice = halBtPluginData->first; AFB_ApiDebug(halBtPluginData->currentHalApiHandle, "Bluetooth selected device changed to '%s'", halBtPluginData->selectedBtDevice ? halBtPluginData->selectedBtDevice->address : "none"); } } else if(! currentBtDevice && currentBtDeviceIsConnected) { if(! HalBtDataAddBtDeviceToBtDeviceList(&halBtPluginData->first, currentSingleBtDeviceDataJ)) return -4; AFB_ApiInfo(halBtPluginData->currentHalApiHandle, "Bluetooth device (address = %s) successfully added to list", currentBtDeviceAddress); if(! halBtPluginData->selectedBtDevice) { halBtPluginData->selectedBtDevice = halBtPluginData->first; AFB_ApiDebug(halBtPluginData->currentHalApiHandle, "Bluetooth selected device changed to '%s'", halBtPluginData->selectedBtDevice ? halBtPluginData->selectedBtDevice->address : "none"); } } return 0; } int HalBtDataHandleReceivedMutlipleBtDeviceData(struct HalBtPluginData *halBtPluginData, json_object *currentMultipleBtDeviceDataJ) { int err = 0; size_t idx, btDeviceNumber; if(! halBtPluginData || ! currentMultipleBtDeviceDataJ) return -1; if(! json_object_is_type(currentMultipleBtDeviceDataJ, json_type_array)) return -2; btDeviceNumber = json_object_array_length(currentMultipleBtDeviceDataJ); for(idx = 0; idx < btDeviceNumber; idx++) { struct json_object *jobj = json_object_array_get_idx(currentMultipleBtDeviceDataJ, (unsigned int) idx); struct json_object *val; if(!json_object_object_get_ex(jobj, "properties", &val)) continue; err = HalBtDataHandleReceivedSingleBtDeviceData(halBtPluginData, val); if(err) return ((int) idx * err * 10); } return 0; }