I need to implement a self bluetooth app to connect with all bluetooth devices to iPhone. I know it is not possible with CoreBluetooth framework.
I use private API and added header files of DeviceManager and BluetoothManager to private frameworks and downloaded BeeTee Project from here
This app runs and finds all bluetooth device near me but when I have tried to connect to a device by this code:
[self.bluetoothManager connectDevice:bluetoothDevice];
and this
[bluetoothDevice connect];
When a cell is selected, Both of above codes request to connect but BTM returns this message:
BeeTee[5473:60b] BTM: connection to service 0xffffffff on device "Nokia 500" F4:xx:xx:xx:xx:xx failed with error 109
What is error 109? Which would be set service number?
I guess I should pair devices before connecting but how can I do that?
I am just guessing, but I think the problem is that the BluetoothManager.framework is made for the External Accessory Program by Apple. And this allows (among others) SPP Bluetooth connection to certificated devices. But there is the problem: you need to have a device with a authentication chip inside.
I don't know on which level/layer Apple implemented the authentication, but I fear the did it one layer under the private framework BeeTee is using.
UPDATE: Maybe this is helpful for you:
BluetoothManager *bluetoothManager = //...
[bluetoothManager setDevicePairingEnabled:YES];
[bluetoothManager connectDevice:bluetoothDevice withServices:0x00002000];
Credits
BluetoothManager *bluetoothManager = //...
[bluetoothManager setDevicePairingEnabled:YES];
[btManager setPincode:#"111111" forDevice:bluetoothDevice.deviceRef];
//where 111111 is your device PIN
[bluetoothManager connectDevice:bluetoothDevice withServices:0x00002000];
Related
I want to remove paired bluetooth device from system settings or "unpair". I know that apple doesn't provide public api to do this so I tried private class CBPairingAgent. I found headers on github
CBCentralManager.h
CBPairingAgent.h and here is my code:
- (void)unpair:(CBPeripheral *)peer {
id agent = [self.centralManager valueForKey:#"sharedPairingAgent"];
// [agent performSelector:#selector(setDelegate:) withObject:self.centralManager];
[agent performSelector:#selector(unpairPeer:) withObject:peer];
}
This code runs without errors but also without any effect for peripheral.
Can somebody point me in right direction? Or suggest better way
The private API to remove a device pairing works if the process has enough entitlements, which is only possible on a JB device.
On JB, I've added these entitlements (found in Settings & SpringBord):
com.apple.BTServer.allowRestrictedServices
com.apple.BTServer.le
com.apple.BTServer.programmaticPairing
I am writing an iOS-based program that interacts with a Bluetooth device via the External Accessory Framework. I would like to determine ahead of time if Bluetooth is even enabled before attempting to connect. Unfortunately, I don't see anything in the External Accessory Framework documentation that allows me to do this.
After checking the docs for the [EAAccessoryManager][1], the closest I can find is to check the [connectedAccessories][1] list to see if any devices are currently connected. However, this doesn't directly indicate the Bluetooth adapter's status.
There are plenty of examples on SO pertaining to Core Bluetooth and Bluetooth LE. I'm specifically looking for a solution related to the External Accessory Framework.
This is not possible with the ExternalAccessory framework. You should use CoreBluetooth, which can give you the information you need on devices that have BLE hardware, i.e. everything released after 2011. The fact that you are using ExternalAccessory for communication with your device does not prevent you from also using CoreBluetooth just for the purpose of knowing whether Bluetooth is turned on.
For older devices there is no way to get this info with public APIs, however if you are not intending to publish your app on the App Store you can use the private BluetoothManager framework.
To get the info with CoreBluetooth you will need to instanciate a CBCentralManager instance, for example like this :
centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
and then implement the following delegate method to get the relevant info :
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
BOOL bleAvailable = central.state != CBCentralManagerStateUnsupported;
if (bleAvailable) {
BOOL bluetoothTurnedOn = central.state != CBCentralManagerStatePoweredOff;
// Do something with the info
}
else {
// Out of luck... We won't be able to determine whether BT is on or off
}
}
What I am trying to do
I am trying to connect my app to a Bluetooth LE device which needs to be paired.
Current behaviour
There is no problem without pairing the device and my iPhone application. I am able to connect, reconnect and read/write characteristics without any problem.
But, if the device need to be paired, I am only able to read/write characteristics the first time, right after the pairing popup confirmation. The next time, I discover and connect the app to my device, but I don't have the rights to read/write characteristics data because (I guess) I am not using the pairing information.
Finally...
After spending few hours searching around the web with no luck here are my questions :
How can I connect my app to a Bluetooth LE device from my iPhone app using the pairing data stored in my phone? Am I missing something?
Is it possible that it is not an IOS problem because if pairing data are present in the phone for the connecting device, it is automatically used?
Is there someone with experience with Bluetooth LE and IOS to help me?
Update 2013-10-27
I have discovered that you can't read a protected characteristic by pairing authentication right after that the characteristic has been discovered if a pairing exists (no confirmation popup). No problem with non-protected characteristic! I don't know exactly why is happening, but the behavior is that the IOS app never receive answers from the device.
So if the first reading is done after, it doesn't cause problem. Here is the code I am using to discover characteristics with the data reading in comment.
- (void) peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error;
{
NSArray *characteristics = [service characteristics];
CBCharacteristic *characteristic;
if (peripheral != servicePeripheral) {
NSLog(#"Wrong Peripheral.\n");
return ;
}
if (service != batteryService) {
NSLog(#"Wrong Service.\n");
return ;
}
if (error != nil) {
NSLog(#"Error %#\n", error);
return ;
}
for (characteristic in characteristics) {
NSLog(#"discovered characteristic %#", [characteristic UUID]);
if ([[characteristic UUID] isEqual:[CBUUID UUIDWithString:kBatteryCharacteristicUUIDString]]) { // Bat
NSLog(#"Discovered Bat Characteristic");
batteryCharacteristic = [characteristic retain];
//--> generate problem when pairing exists between IOS app and device
//[peripheral readValueForCharacteristic:batteryCharacteristic];
}
}
}
You don't have to do anything in your app for pairing management.
If your app runs in LE Central mode, and the peripheral sends an Insufficient Authentication error code in response to a read / write request, iOS will automatically pair with your device and will retry the request.
If you disconnect from the device, and later reconnect again, the peripheral needs to send the Insufficient Authentication error code again for the iPhone to restart encryption. Again, you don't have to do anything special in your app here.
If your app runs in LE Peripheral mode, things are a bit different. When you set up your GATT database, make sure to set correct flags for both the CBAttributePermissions and CBCharacteristicProperties. This will tell iOS that it should send the Insufficient Authentication error code itself, if it is not paired. It is then the responsibility of the central device to start the encryption process.
In the Bluetooth Accessory Design Guidelines for Apple Products, further restrictions are described.
Your accessory needs the capability to resolve private Bluetooth addresses. The iPhone will change its public Bluetooth address every now and then, and only paired devices will have the correct key to resolve that public address and recognize the iPhone.
"Section 3.9 Pairing" is also interesting.
Note that if you pair without man-in-the-middle (MITM) protection, your peripheral can use the resulting key to resolve the private Bluetooth address of the iPhone. However, you won't be able to encrypt the channel.
Pairing with MITM protection on iOS involves entering a PIN code that is displayed by the remote device. Out-of-band (OOB) pairing where you send pairing data over an external channel is not supported by iOS as far as I know (at least there's no public APIs to set OOB data).
Long story short: if you have only a "Pair" / "Cancel" pairing, you cannot encrypt the LE channel but only recognize the iPhone in future connections. The nice thing is that you can still recognize the iPhone even if you unpair it on the iPhone side, and even after restoring the iPhone firmware ;-).
Regarding LE encryption in general: it's not secure anyways (see http://eprint.iacr.org/2013/309).
I am currently working on an iOS application based on bluetooth low energy devices. In order to get a unique identifier to compare the peripherals got, I have to get the MAC address of the peripherals.
It is observed that the UUID property of a peripheral device varies across iOS devices and also for the peripheral device to get a UUID, it will have to get connected to a master device at least once. Since I have to deal with check-in's I don't want to establish a connection. As I went through the bluetooth services portal, I found that the device information itself is a service, which couldn't be retrieved unless a connection has been established between the master iOS device and the peripheral bluetooth low energy device.
I found that in Android we get the entire information of the device, including its MAC address (using getAddress()) when we get the response from the device on scanning itself.
I didn't find any properties in CBPeripheral class related to the device address.
Another way to get a unique parameter would be to customize the advertisement data to send additional information regarding the device, which requires more work on firmware side.
So is there any way in iOS that I could get the MAC address of the bluetooth low energy peripheral without establishing a connection?
Any help would be greatly appreciated.
CBPeripheral's identifier property will serve your purpose, available from a still-unconnected device in CBCentralManager's didDiscoverPeripheral delegate method:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
CBPeripheral *peripheral ...
NSUUID* serverId = [peripheral identifier];
I have a half dozen LE devices I'm experimenting with, including multiple sets of identical devices. I just confirmed that across two iOS devices, the identifiers for all of these LE widgets were different, but for EACH iOS device, the identifier for each widget was retained across application launches and even across app deletes and reinstalls. I would say this definitively proves that the OS is storing enough info internally that for a given iThing, you'll be able to distinguish between, and re-identify, all the peripherals your app encounters, without actually connecting to them.
Also note that the advertisementData, also available before connecting, is full of useful info like CBAdvertisementDataLocalNameKey, CBAdvertisementDataManufacturerDataKey, CBAdvertisementDataServiceUUIDsKey, CBAdvertisementDataSolicitedServiceUUIDsKey, and others, although none as certain to uniquely identify the device as [peripheral identifier] is.
I didn't try doing a device backup and restore to prove the UUIDs were retained, but I'd wager they are, and if they're not, it's something Apple would consider a bug.
Updated Answer :-
After iOS 12 we can get UDID
print(UIDevice.current.identifierForVendor)
print(UIDevice.current.identifierForVendor?.uuidString)
Before iOS 12**
There is no public API to get this information.
If this is an internal or jailbreak application you can get the value of the kLockdownBluetoothAddressKey key via liblockdown.dylib
Low energy peripherals may use privacy feature which hides the MAC address, so it is not necessarily even possible to get the address before connection or bonding. If you somehow get the MAC address which goes over the air, you need to handle privacy or you have interoperability problems.
Apple uses UUIDs to abstract these privacy features out so users do not need to worry about those.
Correct way to do that like you wrote is to either add some vendor specific data to advertisement packet or use the Device Information service.
On-behalf of the discussion of the other professionals I've found some facts which says -
“iOS hides the MAC address of the device and generates a UUID. The UUID on iOS is generated by the iOS device. Different iOS devices will get different UUIDs for the same peripheral. The MAC address is usually based on the hardware. If we both have iPhones and scan the same peripheral, we'll see different UUIDs. iOS generates the UUID on the device and hides the MAC address.
Summary - iOS doesn't let you get the MAC address of a device, it gives you a random UUID instead.“
Source - https://github.com/don/cordova-plugin-ble-central/issues/77
As per above study I've found that there’s not such a unique way to get connect to the board so far, Every board has a MAC address, which Doesn’t changes and easy to access in (only) Android, while iOS doesn’t allow to access MAC Address of the peripheral, however iOS use this MAC address to create a peripheral identifier (UUID), which is unique on (unique) device only. The peripheral identifier for a single Board is different for different iPhones devices (but unique on single device).
However we can connect to a board by searching with Peripheral's Bluetooth Service UUID, but this service UUID is same for all the boards of a kind say- “Adafruit Feather M0”. It means the App will look around the BLE boards of the same type (“Adafruit Feather M0”) and will get connect to ANY one of them. In order to connect to a particular user to a specific Board doesn’t seems to be possible so far due to the inaccessibility of MAC and giving the random UUID in iOS.
You can access to the MAC ADDRESS without problem in iOS 12.
To get the mac address you have to follow the next steps.
Parse the Data received by the BLE device to String.
extension Data{
func hexEncodedString() -> String {
let hexDigits = Array("0123456789abcdef".utf16)
var hexChars = [UTF16.CodeUnit]()
hexChars.reserveCapacity(count * 2)
for byte in self {
let (index1, index2) = Int(byte).quotientAndRemainder(dividingBy: 16)
hexChars.insert(hexDigits[index2], at: 0)
hexChars.insert(hexDigits[index1], at: 0)
}
return String(utf16CodeUnits: hexChars, count: hexChars.count)
}
}
Add a separator ":" to the address.
extension String {
func separate(every stride: Int = 4, with separator: Character = " ") -> String {
return String(enumerated().map { $0 > 0 && $0 % stride == 0 ? [separator, $1] : [$1]}.joined())
}
}
In didReadValueForCharacteristic( characteristic: CBCharacteritic) you can use the previous 2 functions to get the mac address.
func didReadValueForCharacteristic(_ characteristic: CBCharacteristic) {
if characteristic.uuid == BleDeviceProfile.MAC_ADDRESS, let mac_address = characteristic.value?.hexEncodedString().uppercased(){
let macAddress = mac_address.separate(every: 2, with: ":")
print("MAC_ADDRESS: \(macAddress)")
}
}
enjoy your mac address:
"MAC_ADDRESS: 00:0A:57:4E:86:F2"
I'm using the reachability API to detect my current connection, but I can only distinguish between WIFI and 3G.
I get the following flags:
LTE: kSCNetworkReachabilityFlagsIsLocalAddress|kSCNetworkReachabilityFlagsIsWWAN|kSCNetworkReachabilityFlagsTransientConnection|kSCNetworkReachabilityFlagsReachable
WIFI: kSCNetworkReachabilityFlagsIsDirect|kSCNetworkReachabilityFlagsReachable
The problem is that LTE returns the same flags as a 3G connection. Is there any way to determine whether the user currently has LTE or 3G?
As of iOS 7, you can find this out using the currentRadioAccessTechnology property of CTTelephonyNetworkInfo in the CoreTelephony framework.
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
CTTelephonyNetworkInfo *networkInfo = [CTTelephonyNetworkInfo new];
if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {
// ...
}
I wonder if this hidden Core Telephony API can provide you with enough info for you to determine whether you're attached to an LTE or a slower technology.
CTRegistrationGetCurrentMaxAllowedDataRate();
It might be worth experimenting with.
More about using private APIs here: iPhone mobile number using Core telephony
However, I've read that your app will be rejected by apple if you use private APIs.