I'm trying to get basic Bluetooth LE connections working in my app, but I'm running into problems. I have a couple of sensors that I can connect to just fine with the iOS version of this app, but I simply cannot connect to them with the Android version. The only thing I seem to be able to connect to is a beacon I've set to configuration mode.
After checking my ADB logs again, I noticed that whenever it fails to connect to a device, there's always four calls to bt_osi_alarm and bta_gattc_conn_cback. I'm wondering how to interpret this.
An unsuccessful connection attempt:
D/BluetoothGatt(17824): connect() - device: 00:17:E9:C0:86:14, auto: false
E/bt_osi_alarm(14357): reschedule_root_alarm alarm expiration too close for posix timers, switching to guns
...
E/bt_osi_alarm(14357): reschedule_root_alarm alarm expiration too close for posix timers, switching to guns
E/bt_osi_alarm(14357): reschedule_root_alarm alarm expiration too close for posix timers, switching to guns
E/bt_osi_alarm(14357): reschedule_root_alarm alarm expiration too close for posix timers, switching to guns
...
W/bt_btif (14357): bta_gattc_conn_cback() - cif=3 connected=0 conn_id=3 reason=0x0002
W/bt_btif (14357): bta_gattc_conn_cback() - cif=4 connected=0 conn_id=4 reason=0x0002
W/bt_btif (14357): bta_gattc_conn_cback() - cif=5 connected=0 conn_id=5 reason=0x0002
W/bt_btif (14357): bta_gattc_conn_cback() - cif=6 connected=0 conn_id=6 reason=0x0002
D/BtGatt.GattService(14357): onConnected() - clientIf=6, connId=0, address=00:17:E9:C0:86:14
D/BluetoothGatt(17824): onClientConnectionState() - status=133 clientIf=6 device=00:17:E9:C0:86:14
I/mono-stdout(17824): BLEAdapter.OnConnectionStateChange()
I/mono-stdout(17824): Gatt disconnected!
A successful connection attempt to one of those beacons does not have these sets of four bt_osi_alarm and bta_gattc_conn_cback errors. Preferably if someone knows the Android source well, hopefully they can tell me what's going on. It's a long shot I know, but I'm kinda out of options. Thanks.
Some additional information might help the community debug this issue for you. Please add the following to your original question:
Make/Model/Revision of all BLE devices that you've tested and whether or not each one works
Specific android device you have tested with
The error your app gets and at what step it fails
Are you able to get a BluetoothDevice
Does it fail when calling connectGatt()
Is your app written in java/android studio, or is this some kind of universal app that runs on both iOS and Android?
In my experience, alarm expiration too close for posix timers, switching to guns, isn't as serious as its error level suggests. On my nexus 7/marshmallow device, I see this message constantly while connected to a bluetooth device (serial profile, not BLE) so I don't think this is a bug.
I suggest checking up on the BLE device manufacturer to see if there are any firmware upgrades for them too.
More details on alarm expiration too close for posix timers, switching to guns
What's happening is some part of the android bt code is setting a very short timer. So short, that it expires before the app can measure it.
The error, alarm expiration too close for posix timers, switching to guns, was added to android source in a commit 081e4b67b44a5fd397c2d79a5566e11ae52d4aca
The commit message gives us more background on what is happening.
Ensure alarms are called back when they expire in the past
Turns out the posix timers we're dealing with aren't well behaved.
If the timer is TIMER_ABSTIME the following is supposed to be true:
"If the specified time has already passed, the function shall succeed
and the expiration notification shall be made."
But alas, this is not the case. If the expiration time happens to be
in the past (e.g. very short timer which gets hit with a context
switch before the timer can be set) the timer decides to disarm
itself. This means no more callbacks happen, and no more alarms are
processed ever.
sadness.
But thankfully, we can use timer_gettime to check the state of the
timer after timer_settime. If timer_gettime tells us the timer is
disarmed right after we armed it, we want to perform the alarm
callback ourselves.
Put all timer callbacks on the same thread (as would be the case in
the underlying timer implementation already) and used a sempahore to
signal when an alarm expires in the normal posix timer callback and
also in the timer didn't set case.
The code also includes the following comments
If next expiration was in the past (e.g. short timer that got context
switched) then the timer might have diarmed itself. Detect this case
and work around it by manually signalling the |alarm_expired|
semaphore.
It is possible that the timer was actually super short (a few
milliseconds) and the timer expired normally before we called
|timer_gettime|. Worst case, |alarm_expired| is signaled twice for
that alarm. Nothing bad should happen in that case though since the
callback dispatch function checks to make sure the timer at the head
of the list actually expired.
Related
I'm building a simple talker/listener app that receives OSC data through UDP. I'm using OSCKit pod which itself uses CocoaAsyncSocket library for the internal UDP communication.
When I'm listening to a particular port to receive data from another OSC capable software, I log the received commands to a NSTextView. The problem is that sometimes, I receive thousands of messages in a very short period of time (EDIT: I just added a counter to see how many messages I'm receiving. I got over 14000 in just a few seconds and that is only a single moving object in my software). There is no way to predict when this is gonna happen so I cannot lock the textStorage object of the NSTextView to keep it from sending all its notifications to update the UI. The data is processed through a delegate callback function.
So how would you go around that limitation?
///Handle incoming OSC messages
func handle(_ message: OSCMessage!) {
print("OSC Message: \(message)")
let targetPath = message.address
let args = message.arguments
let msgAsString = "Path: \"\(targetPath)\"\nArguments: \n\(args)\n\n"
print(msgAsString)
oscLogView.string?.append(msgAsString)
oscLogView.scrollToEndOfDocument(self)
}
As you can see here (this is the callback function) I'm updating the TextView directly from the callback (both adding data and scrolling to the end), every time a message is received. This is where Instruments tell me the slow down happens and the append is the slowest one. I didn't go further than that in the analysis, but it certainly is due to the fact that it tries to do a visual update, which takes a lot more time than parsing 32bits of data, and when it's finished it receives another update right away from the server's buffer.
Could I send that call to the background thread? I don't feel like filling up the background thread with visual updates is such a great idea. Maybe growing my own string buffer and flushing it to the TextView every now and then with a timer?
I want to give this a console feel, but a console that freezes is not a console.
Here is a link to the project on github. the pods are all there and configured with cocoapods, so just open the workspace. You guys might not have anything to generate that much OSC traffic, but if you really feel like digging in, you can get IanniX, which is an open-source sequencer/trajectory automator that can generate OSC and a lot of it. I've just downloaded it and I'll build a quick project that should send enough data to freeze the app and I'll add it to the repo if anybody want to give it a shot.
I append the incoming data to a buffer variable and I use a timer that flushes that buffer to the textview every 0.2 seconds. The update cycle of the textview is way too slow to handle the amount of incoming data so unloding the network callback to a timer let the server process the data instead of being stopped every 32bits.
If anybody come up with a more elegant method, I'm open minded.
Even with debug enabled for RemoteConfig, I still managed to get the following:
Error fetching remote config values Optional(Error Domain=com.google.remoteconfig.ErrorDomain Code=8002 "(null)"
UserInfo={error_throttled_end_time_seconds=1483110267.054194})
Here is my debug code:
let debug = FIRRemoteConfigSettings(developerModeEnabled: true)
FIRRemoteConfig.remoteConfig().configSettings = debug!
Shouldn't the above prevent throttling?
How long will the throttle error remain in effect?
I've experienced the same error due to throttling. I was calling FIRRemoteConfig.remoteConfig().fetchWithExpirationDuration with an expiry that was less than 60 seconds.
To immediately get around this issue during testing, use an alternative device. The throttling occurs against a particular device. e.g. move from your simulator to a device.
The intention is not to have a single client flooding the server with fetch requests every second. Make sensible use of the caching it offers out of the box and fetch only when necessary.
When you receive this error, plug the value of error_throttled_end_time_seconds into an epoch converter (like this one at https://www.epochconverter.com) and it will tell you the time when throttling ends. I've tested this myself, and the throttling remains in effect for 1 hour from the first moment you are throttled. So either wait an hour or try some of the other recommendations given here.
UPDATE: Also, if you continue making config requests and receive the throttle error, the expire timeout does not increase (i.e. "you are not further penalized").
The quick and easy hack to get your app running is to delete the application and reinstall it. Firebase identifies your device as new device on reinstalling.
Hope it helps and save your time.
It has been stated that an app running a HKWorkoutSession will have special privileges over other watchOS 2 apps, so when a user looks at their Apple Watch, it will go to the view showing running a workout rather than the watch face.
Currently, on both my device and simulator, this is not the case. If I start a HKWorkoutSession and then leave for 5 minutes and then interact with either the Apple Watch, or the Watch Simulator, it presents the watch face.
If I then open my app, it appears to have been frozen, rather than terminated (which is what I imagine happens to other apps). As the UI will update when I need receive a response in my query.updateHandler. Also if I set it to provide haptic feedback every time my query.updateHandler receives a new HKQuantitySample it will do so, so the app must be running in the background in some form.
Has anyone else noticed this behaviour, and am I doing anything wrong, or expecting something I shouldn't?
Here is how I start my HKWorkoutSession:
self.workoutSession = HKWorkoutSession(activityType: HKWorkoutActivityType.Other, locationType: HKWorkoutSessionLocationType.Indoor)
self.healthStore.startWorkoutSession(self.workoutSession) {
success, error in
if error != nil {
print("startWorkoutSession \(error)\n")
self.printLabel.setText("startWorkoutSession \(error)")
self.printLabel.setTextColor(UIColor.redColor())
}
We're seeing that too, for the moment we've made sure 'opens last activity' is configured.
When the UI is active we start a dispatch_timer to request and process data in 1 second intervals.
Make sure you do any significant processing using the NSUserProcessInfo method though and pause the dispatch_timers whenever you are no longer active. You'll get crashes otherwise.
I'm creating a game like application which supports Game Center. And I have a problem with reporting score to leaderboard when the player is authenticated to GC correctly but the network (wifi and cellular) is not available in the time when I want to report my score.
My app is for iOS 5.0 and greater and according to the documentation, it should resubmit the scores when network becomes available. Let me explain what i tried :
I opened my app and authenticate my GC account, turned the wifi off, reported score then opened wifi and waited 30 minutes. After that I checked leaderboard but there isnt any updated score on my leaderboard. (Maybe I am impatient and that is because of the undefined time / interval which apple decides to resubmit scores ?)
I opened my app and authentacate my GC account, terminated the app, turned the wifi off, opened my app again, it automatically authenticate's my GC account, I reported score then opened wifi and still no updated score on my leaderboards. (Maybe I am impatient and that is because of the undefined time / interval which apple decides to resubmit scores ?)
If this resubmit takes more then 30 minutes, I think it is so useless? Is there a way to overcome this? I mean if I save and send the scores later this would be bad too because GC will resubmit them later too? (It wont be so bad but still it would be unnecessary)
Is there any documentation about this resubmitting time ? I couldn't find any... I mean when will it resubmit? Do i need to keep my app and my wifi open until it resubmits?
Thank you for your answers ...
It doesn't matter whether wifi is on or off if you also have a cellular connection. The GC code will use whichever network access is available. If neither is available when you call 'reportScoreWithCompletionHandler:^(NSError *error)' it will report the score the next time the network becomes available.
You didn't say whether your code has ever worked. A common error is that the leaderboard identifier in your code doesn't exactly match the leaderboard ID in iTunesConnect. If they don't match the score will never be successfully reported but it won't tell you what the problem is.
Also note that the score should be a 64-bit value. Maybe you are reporting a 32-bit value.
Also be sure you aren't submitting the score before the local player is authenticated.
Are you checking the error code? If the 'error' you get back from 'reportScoreWithCompletionHandler:^(NSError *error)' is not NULL then something is wrong with your code. Its value may not be helpful (when it isn't NULL) but at least you know that something didn't work.
In my experience, in sandbox mode, the leaderboards usually update fairly quickly (less than a minute) but not instantly. But some days something is wrong with the server and the update takes hours or doesn't work at all. I've read that the production GC server is more reliable and updates faster than the sandbox server.
For what it may be worth here's the code I've been using to report scores. It seems to work:
-(void) submitScore:(int64_t)score category:(NSString *)leaderboardIdentifier {
if (!!! [GKLocalPlayer localPlayer].authenticated ) {
CCLOG(#"GKLocalPlayer is not authenticated");
return;
}
GKScore *gkScore = [[[GKScore alloc] initWithCategory:leaderboardIdentifier] autorelease];
gkScore.value = score;
[gkScore reportScoreWithCompletionHandler:^(NSError *error) {
[self setLastError:error];
}];
}
Does anybody know what happened with event-listener for location changed for WI-FI provider. I saw a lot of questions about this but no proper answer.
I'm doing everything fine and it is working for older versions of android, but now I updated my SAMSUNG GALAXY TAB 10.1 to ICS and it is not working anymore. Maybe this is a SAMSUNG (or my mobile provider) bug when they implemented their UI into ICS...
I'm registering eventlistener like this (have in mind that it works good in prevous versions of android and I also enabled all location services in settings):
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, loclistener);
//refresh time and distance are to 0 so it should trigger location change event
After this request I checked if network provider is enabled and it shows like it is.
Code:
if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
Toast.makeText(this, "NETWORK PROVIDER enabled!", Toast.LENGTH_LONG).show();
}
If someone has an idea please help me...
This is known issue:
After device reboot the network location provider works fine but after some time it stops updating.
https://code.google.com/p/android/issues/detail?id=57707
Unfortunately without solution yet.
To track how often the network provider is updating, I modified the onLocationChanged() method to the following:
public void onLocationChanged(Location location) {
dummyText.setText(location.toString() + "\n" + "Elapsed Time (ms): " + (lastTime - location.getTime()));
lastTime = location.getTime();
}
And added:
long lastTime = 0;
...as a global variable.
This will print out the time between updates in milliseconds (after the first fix - first fix will just print the negative time value from the first Location).
I ran the above code on my Samsung Galaxy S3, and the network provider updates around every 20 seconds, so the code seems fine.
I also ran this on a Samsung Dart (from T-mobile, but not activated) on WiFi, and the network provider updates around every 45 seconds.
I also ran this on a Samsung Nexus S 4G (from Sprint, but not activated) on WiFi, and the network provider at first didn't update at all. Restarting the device seemed to fix the problem, and now its updating roughly every 20 seconds.
Based on your comments and my experience, it looks like this issue varies between OEMs and even between device models of the same OEM. It might be another quirk of the lack of the strict enforcement of LocationListener behavior prior to Android Jelly Bean 4.1. Strict enforcement of LocationListener behavior only recently started in Android Jelly Bean 4.1, which is mentioned in the Android developer docs here in under the first requestLocationUpdates method signature:
http://developer.android.com/reference/android/location/LocationManager.html
Prior to Jellybean, the minTime parameter was only a hint, and some location provider implementations ignored it. From Jellybean and onwards it is mandatory for Android compatible devices to observe both the minTime and minDistance parameters.
Also, from my experience, the refresh interval of the NETWORK_PROVIDER location updates on devices that do update is pretty fixed at around 20-30 seconds across many different devices. So, the minTime parameter you pass into the locationManager.requestLocationUpdates() method is likely to be ignored.
Having to reboot the device to get a network provider location is likely due to not getting a response from the Google server that provides this location info. Not sure why rebooting fixes it.
I had the same problem with the NETWORK_PROVIDER. The reason is that when you request for updates, the NETWORK_PROVIDER does not create a callback for the method onLocationChanged(). You also need to call getLastKnownLocation(NETWORK_PROVIDER) in order for it to start updating the location.
The weird thing is that for the GPS_PROVIDER this is not neccessary.
I hope this helps.
Ok. So now I got it working. It is really strange. I restarted all my devices and now it updates. I don't know what happened and I would really like to know what is the reason for this strange behavior. Thank you #barbeau for all your time and help.