Hardware • 40:41
In this session, hear what Apple is doing with Bluetooth, the cutting-edge technology that enables short-range wireless connections. Bluetooth opens up numerous opportunities for Mac OS X developers. Get the details on Apple's Bluetooth support, how to extend our stack to support your hardware, how to add support for additional profiles required by your devices, and how to develop applications that take advantage of the unique attributes of Bluetooth.
Speakers: Craig Keithley, Eric Brown, Michael Larson
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Good morning and welcome. I'm Craig Keithley. I'm Apple's IOT Technology Evangelist, and I'm a Bluetooth Bluetooth. Thank you for coming today. We're really, really pleased and excited with our success with Bluetooth in the last year or so. We've been doing this for about a year now, and we're really happy about it. This is one of those things that comes out of left field. We're watching for new hardware devices. We're evangelizing gaming device makers, computer makers, and suddenly something comes along which is unexpected, so we're pleased when things come back.
Today, what we're going to go into is an update on where we are with Bluetooth and give a little sense of what kind of products we'd like to see in the future, what kind of things we'd like to see in the future. So, having said that, I'm going to bring up Eric Brown. Thank you.
[Transcript missing]
So that's what we had in our 1.0 release back in Jaguar. Well, over the past year, we've shipped a number of software updates that added a number of new both user and developer features that you might or might not be aware of. So through these releases, we've added support in addition to the Object Push profile. We now have the File Transfer Profiler, FTP for short, that whereas Object Push allows you to send single files back and forth, you can actually do both directory listings and treat it much like a file system.
So we've added support to Bluetooth File Exchange as well as our system services so that you can go browse other systems, actually seeing some kind of cool devices you can connect up to, palms and access file systems. There's even a little portable hard drive that's kind of need to be connected up to it, read and write files from it, just keep it in your backpack, we're pretty nice.
In our latest release, we added support for the HIT program, so with that you can use Bluetooth Keyboard to mice, it just works, it makes it really easy to set up. As far as the setup process goes, we've added a new Bluetooth Setup Assistant that provides a nice easy step-by-step approach to configuring new devices and makes it so that hopefully your mother can actually go and configure a Bluetooth device.
As a part of the work that we've been doing, we looked at how serial port services have been in the system, and as we kept adding features to it, we decided that those just getting a little too complicated for the average user, so what we've done is taken those serial port configuration out of preference panel where it was for the original release, and added a separate application, Bluetooth Serial Utility, that provides all of the more power user options for configuring serial ports. And we also then, in removing that from the preferences pane, we've reworked that a little bit and added a new devices tab that makes it really easy to make changes.
So it provides a kind of a one-stop shop to configure that. As a part of the work that we've done over the past year, we've also added some new and updated APIs to the existing services that we provide already. So most of the main objects we've added some new features to that I'll talk about in a little bit.
So Bluetooth device, L2CAP channel, our COM channel, all really have been, has some new features as well. We've added some new features to that. We've added a bunch of new support to the UI elements, both with the user visible features and one developer API. And as always, all of the APIs that we provide are in both C and Objective-C versions, so we've compared between the two. So we've been getting a lot of questions about the HID support in the system.
How do I customize the HID device? I've got some special things I want to do with the HID device. So it's probably a good idea to go ahead and tell you a little bit about how that works in the system. So what we've done is we provide a full compliant HID class driver. So we've pretty much done all the work for you there.
When you configure a new HID device, there'll be a HID class driver that loads up, connects to the device, takes care of all of the profile specific issues and talking with the Bluetooth partner. And what this does is that makes the device itself accessible through the HID manager APIs.
So if you need to do some custom things with the HID device, you can just use the HID manager APIs. You shouldn't even touch the hardware at all. Which is kind of a good thing because we don't actually give you access in the kernel to the hardware. So that's really the only place you can do it. Now, if you have really have a need to do this, Craig Keithley, our developer contact, is the person to talk to. There's maybe some things that can be done if it's absolutely necessary.
But hopefully, you'll be able to do it. Hopefully, we've made it so that you don't have to do that. I want to talk a little bit about the way that the Bluetooth serial ports work on the system and the way that the serial utilities use the figure. So again, as I was saying, we decided that the settings were just a little too complex for novice users. So we created the serial utility application that now gives you full control over the creation of the ports.
It's currently the only way to create those ports. So it's kind of good to know how it works. There are really two different types of serial ports that are subtly different semantics. So it's good to treat them independently. There's incoming ports and outgoing ports. Now, with a normal serial cable, you don't have to worry about it. You connect up the cable. You talk to the device on the end. It's not incoming or outgoing.
But with Bluetooth, it's a little bit different. So with incoming serial ports, a device itself will connect up to the system, and any device can connect to a particular serial port service. And the application that is listening on that serial port will get the data from that device.
Now, as a result of that, the device is going to, or the application rather, is going to connect up and attempt to use the port before there's anything connected. So in that particular case, it treats it just like you would a serial port with no cable connected. If you do a read, you don't get any data. If you write to it, it just goes to bit bucket. Once an incoming connection is established, it gets routed automatically to the application that has the serial port open, has an outstanding read, and just automatically starts passing data.
As far as outgoing serial ports are concerned, it's up to the user to configure the single outgoing port works with a single device. So they select the device, they can select any RFCOM-based service on the device, and that serial port becomes configured. When you go to open up that port in an application, the system will go and attempt to connect to the device, attempt to open up the necessary channels to create the data pipes for that port.
Now, one possible problem here is that this open process is actually can be quite lengthy. If the device isn't present, there are timeouts associated with it. So you might spend 10, 15 seconds in the synchronous open call waiting for a connection to fail. So as a result, it's not always resulting in the best user experience.
So, now that we know kind of how the serial ports work, I want to tell you that we really recommend that you don't use them. They're really for legacy support. If you've got an application that's already written that needs to use serial ports, or maybe for debugging purposes, it's a little bit easier, that's fine, but for a number of the reasons I've mentioned, it's really an inefficient way to do it. You can get full access to those serial ports by using the RFCOM APIs that we provide.
And those APIs provide you with a much richer set of functionality. It gives you a lot better and more granular error reporting. You get a lot more direct control over the channel creation and the behavior of the channel itself, as well as provide a lot more information about it. Again, we can't recommend this technique enough. It just will result in a lot better applications.
In addition to the serial port changes, we've added a number of features in our UI elements that make the user experience a lot nicer. Now, to support that, we've added a couple of new device categories. So we've allowed now the concept of favorite device or multiple favorite devices so that if you have a device that you use over and over again, you just mark it as a favorite and then every time you go in any application to one of our UI panels, that device will be accessible.
So if you say you always say send files over to your Palm or want to send new applications to your Palm, now you can just use the Bluetooth file exchange. Every time you go to send a file, Palm's going to be listed right there as a favorite device. So you don't have to spend time going through a lengthy discovery process every time you want to send a file.
We've also added support for recent devices where we keep track of each connection that's established and we'll go ahead and list those in most recent first. So if you're sending contacts to your phone and you've got three different ones you want to send, as soon as you've sent that first one, it now shows up in your recent list. So you can keep selecting that phone over and over again. So again, it drastically speeds up the process where you're doing more than one thing at once.
In the UI itself, we've added new filters that let you filter by those categories. So say you only want to see your favorites, you can easily do that. Only want to see recents or just the discovered devices, you can do all of those things. As well, we've let you filter by the actual type of device, so computer, PDA, phones. So if you're in an environment that tend to have a lot of Bluetooth devices, which we're not there yet, but someday, we'll actually let you easily go and find the specific device you're looking for without having to wade through a long list.
In addition to those user features, there's a number of new pieces of API that make things a lot better in the UI elements that allow you to, for example, run PanelModal or as a sheet, full control over all of the text that actually appears on the panel. So the title bar, the description, the prompt that's used, and the button to select the device are fully customizable by the application, and as well, lets you fully localize the information that you put in there. So you should be able to do pretty much anything you want with these panels. We've also added support for filtering and validating user selection.
So if you know you want to connect to, say, an object push service on a device, you can tell the panel that I only want devices to let the user select a device that supports the object push service. In that case, it will actually go and verify, it'll do an SCP query, it'll verify the device has that service before finalizing the selection.
Your code returns, you'll know that the device, it'll basically give you the service that you asked for. You know that the device has it, you don't have to worry about doing it yourself. All of these new APIs are fully header-docked, and they're consistent across all the different UI elements, so once you learn it for one, it applies to all of them.
To support the new different categories and device types, we've added some new API onto the IO Bluetooth device object itself. So we've got a whole slew of API to manage the favorite devices. It lets you get to list the currently logged in users' favorites, lets you check to see if a single device is actually a favorite, and then you can even add and remove the device from the favorites as well. So hopefully you only do that at the user prompting and not just behind their back, because they probably wouldn't like it too much.
But we also add, finally, API to access the paired devices, so you can get a list of all the system-paired devices, as well as find out if an individual device is paired. And then in order to support recent, we've added a recent access date attribute to the device object.
So it lets you see when the device was most recently accessed, and there's API that lets you get the list of the end most recently accessed device for the user. So say you only have room for the 5G. So you can just call this API, and it will return you the 5 most recent devices for the user.
Additionally, we've added a lot of new functionality in the, basically in our L2CAP and RFCOM layers. The original set of APIs we had worked, but there wasn't really a distinction between asynchronous and synchronous versions of the API. So the APIs that were there were kind of a... and the rest of the team.
Thank you. There's a number of new notifications about channel state changes. Now, all of these new APIs are available only in 10.2.5 and forward. So, if you do make use of them, you will need to be dependent upon 10.2.5. However, we highly recommend that you do this because these new APIs, especially for asynchronous behavior, really lets you provide a much better user experience.
You can really improve the application responsiveness, avoid the spinning cursor in a lot of cases, since a number of the operations tend to take a long time, especially in the failure cases. So, it's really worthwhile to use the asynchronous APIs so that the user doesn't have to feel like the application isn't responding.
Within the L2CAP channel object, in order to support the new asynchronous behavior, we've added the concept of a channel delegate. And what it does, it allows the delegate to then receive the necessary callbacks, notifications, and events that go along with the different happenings in the system. So the delegate will get new incoming data when it'll get the channel open complete notification, it'll get notified when the channel gets closed, when it gets reconfigured.
This is a new one that we didn't have before, so if the L2CAP channel itself goes through the configuration process to get some new parameters, it will do that. It's also then a write complete notification, so it's kind of a one-stop shop for all the asynchronous notifications within the channel.
The APIs for those are divided into a couple different categories. We've got a couple new APIs on Bluetooth device that lets you both open the channel asynchronously or synchronously. Now, as a result of the new channel delegate, every channel has to have a delegate when opened. So, since that is now the one way to receive incoming data notifications, for example, it's a requirement that there is a delegate that at least implements the incoming data callback. Otherwise, if data comes in, it would just get dropped on the floor. So, pretty much have to have that set up.
The old method that was used to open the channel, as we are going to be deprecating that, so we definitely recommend that you move to these new APIs. It's just generally a lot better. You know whether it's a synchronous call, you know whether it's an asynchronous call. It really defines what the behavior is rather than kind of leaving it up to how we've decided to do it.
We also have new asynchronous write methods on the L2CAP channel objects. So we've got writeAsync and writeSync that, again, behave the same way. It does the right thing in the right place. The asynchronous write will go ahead and return right away. It's not going to block on you. It lets, you know, gives you a real good user experience with that. Both of these replace the old write call.
RFCOM channel has APIs really similar. There's a new RFCOM channel delegate object that gets the same messages when there's new incoming data, channel open/closed. RFCOM has some additional control signals and flow control information that are communicated to the delegate. Gets a write complete notification and then there's a new queue space available notification that I'll explain in a little more detail in a minute.
For the RFCOM APIs, we have the same kind of channel open APIs on the Bluetooth device object itself for opening a channel synchronously, asynchronously. As with L2CAP, the delegate is required to be able to open a new channel. And as with L2CAP, we're going to be deprecating the old open RFCOM channel method as well.
The RFCOM channel APIs itself have, as you'd expect, write async, write sync versions that are going to be replacing the old write and write simple methods. Now, we're also deprecating the old incoming data listener and incoming event listener methods because all of those listeners are now replaced by the delegate. Everything goes to the one place in the delegate object.
Now, one of the things that in doing these fully synchronous, fully asynchronous APIs with RFCOM channels, a little different than L2CAP, is that RFCOM provides flow control. So, we're basically told when we can and when we can't write data. So, in the case where we can't write data, we have to queue it up internally. Now, in order to avoid having just an unbounded queue space, there are some maximums set within the channel that are pretty high.
It's unlikely you're going to run into a case where you're queuing up too much data, but it's possible. So, in order to deal with that situation nicely, we've added the case where a write call may fail with a KR return no space error. So, if that happens, we will then subsequently call the delegate the queue space available notification once the queue is actually available again. So, you can easily just pen that operation until you're notified that we have queue space again. It's pretty much a common operation when dealing with a system with flow control.
Now, we've also gotten a number of questions about how SDP services work in the system. It's not straightforward to just go and add new services, so I thought it's a good idea to talk a little bit about how that works. You pretty much, if you're looking to add support for new Bluetooth profiles, or maybe you want to write your own profile, like maybe your chat service or something like that, you will likely need to add services to the system so that other devices can find that service. So, we've defined really two types of services, transient and persistent.
A transient service is, as implied, it's going to only exist for a short period of time. In our case, what we've done is we've defined that amount of time to be the running time for the application that registers to the service. So, as soon as that application dies, exits, is quit by the user, we're going to remove that service. So, you don't have to worry about cleaning up, it'll just go away for you.
Persistent Service is a little bit better, where, maybe not better, but more useful in some cases anyway, where it will stick around outside of the scope of the application that registered it. So, it'll exist through reboots. It doesn't actually require that your client application be running. It can be configured to go and automatically launch an application when an incoming connection is created to the service.
Now the service itself, we've defined a dictionary format to describe how a particular service looks. So the dictionary itself has individual entries where each entry corresponds to an SDP attribute. Now that format itself is a little bit in-depth and a little bit more that we can go into here, but it should be fully documented in the HeaderDoc. And if you do have any questions about it, we've got a developer mailing list, so we'd be more than happy to help you with anything you need there.
In addition to the format itself, some of the attributes will actually be defined by the system. So the service record handle and RFCOM channel ID are the two that are currently assigned by the systems. That way, there's a single namespace essentially for the record handles and channel IDs, so we have to manage that internally. We also have this additional local attributes attribute that lets you specify some custom controls. So we have controls for a particular service that control the behavior of the system with regards to that single service.
Currently, we only have two supported attributes. Persistent attributes, as you'd expect, it's a Boolean attribute that if it's set to true, it indicates that the service will exist until you remove it. If it's either missing or false, then that service will be removed as soon as the application exits. There's also a target application that specifies a particular application to launch when an incoming connection to that service is established.
So, that pretty much will only work when there's a logged-in user. So, when that user's logged in, connection to, say, you've created your new chat service, and you've said, oh, here's my chat application to launch when somebody connects to it, well, what happened then? It'll go and launch that application as the currently logged-in user. So, it's just like they're running an app.
Now, you don't actually have to have a target application for persistent service, but it's more useful in that case. You might want to have a running process like a menu extra or something that provides notifications on the states of things, so you can set up even a persistent service without launching your application, and a running app can get notifications for that service. Additionally, we're likely to add new local attributes to control additional things about the way that individual services behave.
Now in order to actually add a new service, we've got a function out of Bluetooth, Add Service Stick. It's pretty simple to use, and what we've done is for the service dictionary description, we've defined it so that it only uses the native plist data types. So it makes it real easy.
You can define your service actually in a plist file, load it in, and then call add service with that dictionary. So you don't have to create a lot of code to build up a complex dictionary. You can just do it in the file. It makes it real easy.
This particular function will actually return a newly created Bluetooth SDP service record that includes all of the attributes that were signed by the system. So one thing to really take note here is that if you're creating a persistent service, the client has to remember the record handle returned in this particular service record, because that record handle actually is then used in the remove service with record handle call.
So if it doesn't remember it, then you're kind of stuck. You need to know that to remove that service. So in this particular case, the transient services you don't need to worry about, they'll be removed automatically for you. But again, if, say, you only want a service to exist for not for the entire run of the application, but just part of it, you'll need to remember that as well for columnist function. Thank you.
So, I've got a number of helpful hints here. We've got some sample code that does a number of the things I've talked about. We've got the RFCOM server sample is actually a, it's a simple chat server that goes along with, there's an RFCOM client sample. Well, the server code shows how to, it has its own plist file with the dictionary format set for a sort of a fictional custom chat service. It also shows how to load that in, how to set up the service, save off the handle, and then remove it when needed.
Now, in case you do get into a situation where, say, your code crashed and didn't save off the handle, or if you forgot to implement the code to save it off, it is possible to remove it with a little bit of surgery. So, in this case, there's the blueD.plist file in the Roots Library Preferences directory. So, you can remove that file.
It will remove your service. It will also remove all of the other cached information on your system. So, I definitely recommend only doing this as a last-ditch effort, because you will lose all the device names, device services. You'll lose all your pairings. So, it's useful as a developer tool, but definitely not for the end user.
So now, how do you actually get access to these new APIs? Well, we are currently in the last stages of working on an SDK that will be based on the R1025 release. Keep checking the developer site because it will be up there real soon. It includes all the APIs that I've talked about here. It also includes support for Packet Decoder, which, as some of you have noticed, in one of our software updates, we disabled it for security reasons.
The Packet Decoder itself would allow pretty much anybody access to all of the traffic going over your Bluetooth link, and that includes link key data with the hid stuff, hid data. They could snoop your keyboards and everything. So by default, in production builds, all of the support for Packet Decoder is completely removed.
The SDK itself includes a build that has that turned back on, so all you need to do to use that application and develop your new services is just install the SDK and you'll get support for it. So now I'd like to turn the presentation over to Mike Larson. He's a Bluetooth technology manager. He's going to talk about kind of new directions and new what's going on in the Bluetooth industry.
Thanks, Eric. It's been a long year. There's been a lot going on. Bluetooth industry has really come a long way. The few industry events I've attended, it's gone from how do we create this brand-new technology and get people to adopt it to, okay, it's out there, it's starting to become successful and in lots of devices, what do we have to do now? The push is largely for simple configuration.
People are realizing that if it takes more than five minutes to configure a device, the user's going to drop it on the floor and not touch it again and say, this doesn't work. Along those lines, the special interest group has now started what they call the five-minute ready initiative. What they're looking for there is a five-minute out-of-box experience. And as the industry shows, others are pushing for even sooner. I've heard as low as 30 seconds. So that's the goal.
What else has happened in terms of Bluetooth devices over the last year? The user interfaces have gotten a lot simpler. If you've been an early adopter of this, like I've been, you've been able to notice that the user interface has moved from five or six levels deep in a device to right at the foreground with the new devices like the Sony Ericsson T610. So that's the direction this is going. People are starting to take this seriously and figure out a way to get the user up and running in very little time.
There are a few changes coming on on the technology side from the Bluetooth SIG and the industry. The first one is late this year, they're coming out with version 1.2. They have a few features included there. The primary one is improved quality of service, which means that you can guarantee that your HID devices will get time to transfer their HID data when they need to, and other quality of service-dependent devices will get the time they need when they need it. Another feature of version 1.2 is adaptive frequency hopping. What AFH is going to provide is better coexistence of Bluetooth devices with other 2.4 GHz devices. In order for AFH to work and be successful, it does require a version 1.2 radio on both sides of the link.
Now that said, this is something you're going to want to consider if you're building a hardware device. If you have the choice and are making radio decisions now, check with your vendors and make sure that the radio that you're going to use for your device will be 1.2 capable and will support AFH. It will help you out and it will help your users out down the road.
Past the end of this year and onto the next horizon, sometime next year hopefully, the Bluetooth SIG and the technology leaders are looking at medium data rate. This will require a new radio. Today's radios aren't likely to be upgradable through firmware to support this. It will require new hardware.
What does medium data rate give you? It gives you up to 2 to 3 megabit per second throughput on a single channel. So that's currently scheduled for mid-next year. We hope everything comes together and that will allow a few more Bluetooth devices to exist on a single link. Get some new things using Bluetooth.
There are a few things that we've noticed in the last year and a half, two years that we've been playing with Bluetooth. And here are a few of our notes that will help you out and make your Bluetooth devices coexist with the rest of the world. Inquiry operations and paging operations, which are device discovery and creating connections respectively, they really disrupt 802.11 traffic as well as other Bluetooth traffic on the link.
For that reason and a couple others, we do not plan to offer a direct inquiry API. Accessing the inquiry API is possible through our user interface. Another thing to make note of is 720 kilobits per second is the total link budget for bandwidth and throughput. If you assume that that is the budget available for your connection, you're not going to be very kind to other connections.
And you will not get that throughput if the user is selected to use a Bluetooth keyboard or a mouse because the quality of service constraints will require us to access those devices as well. So if you need a full 700 kilobits of bandwidth, Bluetooth is probably the wrong choice for your wireless connectivity. If you need somewhat less than that and can coexist and deal with less throughput, then it's a good choice.
This goes back to the point I mentioned on the last slide where paging operations disrupt both existing Bluetooth traffic and 802.11 traffic. In order to find out if devices are within range, a loop of paging to find if the device is there and connect to it is not a real good use of resources.
It's not kind to the other people using the link, and it's not kind to the 802.11 connections that are in place. Now I'd like to introduce Craig Keithley, who's going to talk about some opportunities that are out there in the Bluetooth world for our customers. Thank you very much.
Thanks, Mike. So there are a number of opportunities. In fact, before I came up on stage today, we were talking with Mike, and he just got back from the Bluetooth Congress, and he tells me that my price point of $75 for a low-cost serial adapter is too high and that it should be doable at $40 based on some new chipsets that are coming out. So scratch that and put $40 in there.
We do want to see more HID devices, and I'm particularly interested in seeing gaming devices come along. There are some gamepads, for example, that are RF-based, but they work with the USB adapter, custom one. I'd like to see those go to Bluetooth. The I3A Committee, which is the organization that deals with specifications for digital stealth cameras, does have a PTP over Bluetooth specification and development.
For those of you who aren't familiar with PTP, it's Picture Transport Protocol. It's the core of which our image capture architecture is based. All the command sets that are in image capture, all those that architecture is structuring. So there's USB PTP cameras available today, built in support with a profile or a USB class driver. There's a FireWire PTP class specification in works, and we want to see that extended to digital stealth cameras as well.
I've seen some GPS receivers, samples that are just in prototype form now that have a Bluetooth interface on them, and I think that would be a very cool device to have to be able to get your GPS location over Bluetooth wirelessly. Application-wise, this is a somewhat challenging area for us. Certainly, the silent clicker is a nice innovative product. The question now is, what other things can you do application-front? I'd like to hear about them. If you've got ideas, you want to have us help you with marketing efforts, please contact me.
That actually goes for all of Bluetooth products that you might be working on. I'm looking at stuff that we can do at Next Macworld San Francisco for a Bluetooth area, and I'd like to be able to highlight products, so please do contact me if you've got something in the works.
The Bluetooth hardware solutions, well, so serial and audio are the ones that are presently on our minds. Our hardware is capable of supporting SEO. We haven't released any profile support for that yet. We should, if all things go well, be able to do that in the future. What I do want to encourage people to do is to pay attention that SEO and its current implementation is not adequate for doing speech recognition. So the SEO-based cell phone-style headsets, although we may be able to support them in the operating system, would not be something that I want you to plan on using with our speech recognition engine.
If you do an advanced audio product, however, where you can do, say, stereo headphones or you can get good sample rates on the microphone, that's something I'm very interested in working with you on. I want to make sure that we have support in our speech recognition engine for those types of devices. So as a just general rule, if you're working on advanced audio, we need 22 kilohertz, 16-bit resolution to be able to do good speech recognition. So as was mentioned earlier, we haven't put anything in the kernel.
For developers, it's not released to Darwin. I'm able to consider some specialized requests that I then need to go work with marketing and engineering on to make sure that it's the right thing to do. But I do want to hear from you if you need to do something in the kernel.
So the roadmap, well, this was yesterday's session, but as you're aware, we do have DVDs that come out usually about three months after developer conference, so that's one session you should look at. The other thing is that, and I should have put it on the slide, Friday morning at 9 o'clock in this room, we have a HID and force feedback session. And if you're doing HID devices, or you want to be able to access a Bluetooth HID device, you should come to this session.
As was mentioned, we really are strongly encouraging people to do everything that's HID-related through the HID manager. So that includes Bluetooth, and in fact, it could include almost any transport protocol. We could do HID controls over FireWire, for that matter. The representation of a HID device with a HID-style report descriptor and getting HID reports will come in through the HID manager, regardless of the transport layer in the future. And that's where we want people to go to get HID data. Thank you.
So contact-wise, there's the standard list. I do encourage all of you to take a look at the Bluetooth developer mailing list. I've got a long mural there, but look for list.apple.com and then click on the lists on that website. There's a lot of lists run by us. Bluetooth is one of them, and you should go there to participate in discussions with other Bluetooth developers. Our standard reference library stuff.