I have the following snippet of code that is intended to check if I can access Location Services. It builds ok but on the device and emulator I get a FileNotFound Exception on the switch (accessInfo.CurrentStatus) line (there is nothing on Google about this)
var accessInfo = DeviceAccessInformation.CreateFromDeviceClass(DeviceClass.Location);
accessInfo.AccessChanged += OnAccessChanged;
switch (accessInfo.CurrentStatus)
{
case DeviceAccessStatus.Unspecified:
break;
case DeviceAccessStatus.Allowed:
break;
case DeviceAccessStatus.DeniedByUser:
break;
case DeviceAccessStatus.DeniedBySystem:
break;
default:
throw new ArgumentOutOfRangeException();
}
The device stacks from Windows and Windows Phone are not exactly the same in 8.x. Geolocation is one example where they are different, and this manifests itself in this error. You can use the Geolocator.LocationStatus API instead. Note that on Windows Phone, the app is always granted the location capability (it's an install-time prompt) although the user could still turn off location globally.
Related
Background
I have a small battery powered system running on freeRTOS. I need to periodically run OTA updates, as per any proper internet connected device. The problem is that, being battery powered, the device spends 99.9% of it's life in deep sleep.
When the device is awake, it's possible to post an OTA update from AWS, by publishing to the device's OTA/update topic. From the console, you can only use QOS = 0. But from inside, say a lambda, I believe it's possible to use QOS = 1.
{
"state": {
"desired": {
"ota_url":"https://s3-ap-southeast-2.amazonaws.com/my.awesome.bucket/signed_binary_v21.bin"
}
}
}
Questions
How can I modify this approach to successfully update a device that sleeps for 15 minutes at a time, and wakes for maybe 10 s. During the waking period, it sends a message. Is there some way of leaving the desired OTA/update in the shadow that somehow is included in the response from AWS. I've not managed to figure out how shadows really work. OR can you specify a retry period and time to keep trying perhaps?
Is this approach essentially consistent with the most recent best practice from a security perspective : signed binary, encrypted flash & secure boot etc.
Many thanks.
IDF comes with an OTA example that is pretty straight forward. As suggested in the comments on your question, the example downloads the firmware update, and it will automatically revert to the previous image if the updated version has errors that cause a reset. This is the operative part of the example:
extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
esp_err_t _ota_http_event_handler(esp_http_client_event_t* evt);
#define FIRMWARE_UPGRADE_URL "https://yourserver.com/somefolder/somefirmware.bin"
void simple_ota_task(void* pvParameter)
{
esp_http_client_config_t config = {
.url = FIRMWARE_UPGRADE_URL,
.cert_pem = (char*)server_cert_pem_start,
.event_handler = _ota_http_event_handler,
};
esp_err_t ret = esp_https_ota(&config);
if (ret == ESP_OK)
ESP_LOGW("ota", "Upgrade success");
else
ESP_LOGE(TAG, "Upgrade failure");
esp_restart();
}
void foo()
{
// after initializing WiFi, create a task to execute OTA:
//
xTaskCreate(&simple_ota_task, "ota_task", 8192, NULL, 5, NULL);
}
// event handler callback referenced above, it has no functional purpose
// just dumps info log output
esp_err_t _ota_http_event_handler(esp_http_client_event_t* evt)
{
switch (evt->event_id) {
case HTTP_EVENT_ERROR:
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
break;
}
return ESP_OK;
}
To setup embedding the cert, obtain the public key by examining the SSL cert for the site on which the bin file will reside, with a web browser, save it, then convert it to Base64, aka PEM format. (I used some web page.) In my case I created a directory at the same level as the /main directory, called /server_certs, and saved the Base64 conversion in that directory as ca_cert.pem. (That was way too pedantic, wasn't it?)
Then added these lines to CMakeFiles.txt in the /main directory:
# Embed the server root certificate into the final binary
set(COMPONENT_EMBED_TXTFILES ${IDF_PROJECT_PATH}/server_certs/ca_cert.pem)
register_component()
The thing that was unclear to me was how to determine that a newer version is available, if there is a built-in way I couldn't discern it, and did not want to download the update for nothing, ever. So I rolled my own, embedded a version string in the firmware, created a WebAPI entry point on my server that returns the current version (both maintained by hand until I can think of a better way... that I feel like implementing) and call the OTA functionality when the strings don't match. It looks a lot like this:
// The WebAPI returns data in JSON format,
// so if a method returns a string it is quoted.
#define FW_VERSION "\"00.09.015\""
// the HTTP request has just completed, payload stored in recv_buf
//
// tack a null terminator using an index maintained by the HTTP transfer
recv_buf[pos + 1] = 0;
// test for success
if (strncmp(recv_buf, "HTTP/1.1 200 OK", 15) == 0)
{
// find the end of the headers
char *p = strstr(recv_buf, "\r\n\r\n");
if (p)
{
ESP_LOGI("***", "version: %s content %s", FW_VERSION, (p + 4));
if (strcmp((p + 4), FW_VERSION) > 0)
{
// execute OTA task
foo();
}
else
{
// Assumes the new version has run far enough to be
// considered working, commits the update. It doesn't
// hurt anything to call this any number of times.
esp_ota_mark_app_valid_cancel_rollback();
}
}
}
If you're not using the CMake/Ninja build tools, consider checking it out, it's way, way faster than the MingW32-based tool set.
As you have mentioned, you do not fully understand how shadows work, let me explain how using shadows may fit in scenario similar to yours.
Normally, a device that sleeps for long and wakes up for a short duration, usually makes a request to device shadow at wake up. If the shadow contains
{"state": {"desired":{"ota_url": "xxx", "do_ota": true, ...}}},
In this case, device should initiate ota update code inside device.
Further, the approach mentioned here has nothing to do with security and best practices (signed binary, encrypted flash and secure boot). All these three things should be handled by ota update program. Shadows are only used to indicate that there is a ota update available. Using shadow help you avoid making unnecessary GET Request to OTA server, in turn saving precious power and compute resources.
I tried to get sensor stream from HoloLens, so I used the HoloLensForCV.
(https://github.com/Microsoft/HoloLensForCV)
First, I checked that SensorStreamViewer project works, but few days later, I updated HoloLens and then it doesn't work. The error I receive is Access is Denied
HoloLens Camera view capture
HoloLens Privacy Camera capture
The Webcam capability in VS capture
And, I guess the error occurs in this part(SensorStreamViewer.xaml.cpp).
// Initialize MediaCapture with the specified group.
// This must occur on the UI thread because some device families
// (such as Xbox) will prompt the user to grant consent for the
// app to access cameras.
// This can raise an exception if the source no longer exists,
// or if the source could not be initialized.
return create_task(m_mediaCapture->InitializeAsync(settings))
.then([this](task<void> initializeMediaCaptureTask)
{
try
{
// Get the result of the initialization. This call will throw if initialization failed
// This pattern is docuemnted at https://msdn.microsoft.com/en-us/library/dd997692.aspx
initializeMediaCaptureTask.get();
m_logger->Log("MediaCapture is successfully initialized in shared mode.");
return true;
}
catch (Exception^ exception)
{
m_logger->Log("Failed to initialize media capture: " + exception->Message);
return false;
}
});
When I start others project like 'ComputeOnDevice', I can see an alert message window asking me allow to access camera. However, when I start 'SensorStreamViewer', I didn't see any alert message asking about camera access.
I started debugging, and confronted this error message.
Exception thrown at 0x772C3332 in SensorStreamViewer.exe: Microsoft C++ exception: Platform::AccessDeniedException ^ at memory location 0x0180E680. HRESULT:0x80070005 Access is denied.
WinRT information: The required device capability has not been declared in the manifest.
How can I solve this problem?
In your Package.appxmanifest file, you need to add the following capability.
<rescap:Capability Name="perceptionSensorsExperimental" />
So I have an iOS application that was built with Xcode 7 up until recently. It uses iOS location regions for determining if users enter/exit regions. I simply have code in the DidDetermineState method of the CLLocationManagerDelegate that sends API calls to my backend based on the state (entered/left). It was working very well with very minimal issues up until I upgraded my build machine to Xcode 8 and made a new build. Suddenly, some users are reporting "flip flopping" where they'll be sleeping and their phone will "enter home" then "left home" all while the phone is sitting on their night stand. This never happened before upgrading to Xcode 8. Were there some location changes in Xcode 8 that I missed that I'm not accounting for?
What's strange is I cannot reproduce it. And they claim that it's working normally, but misfiring a lot. I'm looking at their logs and seeing exactly what they're describing. It'll be 3am and DidDetermineState will fire with "left home" only to "enter home" seconds or minutes later. This seems to be ~10+% of users (total rough estimation).
public override void DidDetermineState(CLLocationManager manager, CLRegionState state, CLRegion region)
{
this.HandleRegionEnterExitDebugStuff("Did Determine State = " + state, manager, region);
//Console.WriteLine("region.id = " + region.Identifier);
// This is all code that hasn't changed in a long time and worked fine when built with Xcode 7. But this event is misfiring (false positives) with Xcode 8....
string message = null;
switch (state)
{
case CLRegionState.Inside:
RegionLogic (region, manager, "Region Entered", LocationManager.Location_WebHook_Enter);
break;
case CLRegionState.Outside:
RegionLogic (region, manager, "Region Exit", LocationManager.Location_WebHook_Exit);
break;
case CLRegionState.Unknown:
message = String.Format("DidDetermineState state = CLRegionState.Unknown for region.Identifier = " + region.Identifier);
LogManager.LogMessage(
LogManager.LogType.Location,
message
);
LogManager.LogMessage(
LogManager.LogType.CriticalInfo,
message
);
break;
default:
message = String.Format("DidDetermineState state = invalid for region.Identifier = " + region.Identifier);
LogManager.LogMessage(
LogManager.LogType.Location,
message
);
LogManager.LogMessage(
LogManager.LogType.CriticalInfo,
message
);
break;
}
}
Additional information:
So far i've dug into 3 users and confirmed they're seeing "false positives" via their logs and they have the following information:
User 1: iOS 10.3.1 iPhone 6s. Location USA. Language English.
User 2: iOS 10.3.1 iPhone 6s. Location USA. Language English.
User 3: iOS 10.3.2 (beta version) iPhone 7. Location USA. Language English.
Me (cannot reproduce issue): iOS 10.3.1 iPhone 6. Location USA. Language English.
Application is built with Xamarin.iOS
Target Deployment is 9.0
I have an app that keeps user data in a private database using CloudKit and iCloud. I have written code to handle when the user logs out of iCloud and back in as a different user via Settings -> iCloud on their device. I am currently in Beta Testing mode and when I try this as an internal tester, the app crashes when I change accounts. When I test this in development mode using either the Xcode simulator or a real device, my code works great. My question is: should CloudKit apps be able to handle when the iCloud account changes on the device? If so, is this case able to be tested with internal testing via TestFlight. My code to handle account changes is below...
func isCloudAccountAvailableASync() {
container.accountStatusWithCompletionHandler { (accountStatus, error) in
switch accountStatus {
case .Available:
print("INFO: iCloud Available!")
// begin sync process by finding the current iCloud user
self.fetchUserID()
return
case .NoAccount:
print("INFO: No iCloud account")
case .Restricted:
print("WARNING: iCloud restricted")
case .CouldNotDetermine:
print("WARNING: Unable to determine iCloud status")
}
// if you get here, no sync happened so make sure to exec the callback
self.syncEnded(false)
}
}
func fetchUserID(numTries: Int = 0) {
container.fetchUserRecordIDWithCompletionHandler( { recordID, error in
if let error = error {
// error handling code
// reach here then fetchUser was a failure
self.syncEnded(false)
}
else {
// fetchUserID success
if self.lastiCloudUser == nil {
print("INFO: our first iCloud user! \(recordID)")
self.saveUserID()
}
else if !recordID!.isEqual(self.lastiCloudUser) {
// User has changed!!!!!!!!!!!!!
self.saveUserID()
// delete all local data
// reset the saved server token
self.saveServerToken(nil)
// try again with reset fields
}
// else user never changed, then just create a zone
// continue the sync process
self.initZone()
}
})
}
---EDIT/UPDATE:---
Here is a screenshot of my crash log
---EDIT/UPDATE 2:---
I was able to generate another crash with a different crash log. This crash log still doesn't point to my code, but at least describes a function...
I kept making it crash and somehow got a crash log that included a line of code I could link to a line in my Xcode project. My issue was with NSOrderedSet and iOS 9. That issue can be found here. I do not know why I only got hex in my crash log before, if anyone knows how to deal with hex crash logs I would love to hear it.
Here are the answers to my original question for anyone out there:
Should CloudKit apps be able to handle when the iCloud account changes on the device?
Answer: Yes
If so, is this case able to be tested with internal testing via TestFlight?
Answer: Yes
It's hard to say were your app crashes exactly.
You have to be aware that after an account change you should reset the state of your app (clearing local user data and navigating away from the screen that has user specific data)
On startup of your app you should call a function like this:
func reactToiCloudloginChanges() {
NotificationCenter.default.addObserver(forName: NSNotification.Name.NSUbiquityIdentityDidChange, object: nil, queue: nil) { _ in
// The user’s iCloud login changed: should refresh all local user data here.
Async.main {
self.viewController?.removeFromParentViewController()
// Or some other code to go back to your start screen.
}
return
}
}
I have an iOS app which advertises itself successfully using a CBPeripheralManager. In a Chrome App, I have successfully discovered the device (hence obtaining its address).
I have added a service with a custom UUID to the peripheral manager. didAddService is called (after the peripheral manager is powered on), and so I assume that part is successful.
I call chrome.bluetoothLowEnergy.connect(device address) in the Chrome app. The callback function is called without issue, and so I believe the connection is successful. There is no function called within the app when the connection has occurred - should there be?
I then call chrome.bluetooth.getServices(device address) in the Chrome app. An array of services is returned, however the array is empty. I believe it should return an array of length 1, not 0.
Am I overlooking something? Why are no services being returned? Should any of the peripheralManager functions be called during these operations?
Connect code:
function connect(address) {
/* Connect persistently to the device. */
chrome.bluetoothLowEnergy.connect(address, {persistent: true}, function() {
if(chrome.runtime.lastError) {
/* If we click a green device, we get this. We should try and handle it
earlier. e.g. by removing the listener, or instead using the click
to disconnect the device. */
if(chrome.runtime.lastError.message == 'Already connected')
setDeviceConnected(address);
/* Print error and exit. */
console.log(chrome.runtime.lastError.message);
return;
}
console.log('Successfully connected to ' + address);
setDeviceConnected(address);
// getDeviceService();
getDeviceServices(address);
});
return true;
}
Get services code:
function getDeviceServices(address) {
chrome.bluetoothLowEnergy.getServices(address, function(services) {
console.log('services.length: ' + services.length);
});
}
Peripheral setup code:
- (void)setupPeripheral {
_serviceName = SERVICE_NAME;
_serviceUUID = [CBUUID UUIDWithString:SERVICE_UUID_STRING];
_characteristicUUID = [CBUUID UUIDWithString:CHARACTERISTIC_UUID_STRING];
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
_characteristic = [[CBMutableCharacteristic alloc] initWithType:_characteristicUUID
properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyNotify
value:nil
permissions:CBAttributePermissionsReadable];
_service = [[CBMutableService alloc] initWithType:_serviceUUID primary:YES];
_service.characteristics = #[_characteristic];
NSLog(#"Peripheral set up");
}
Start advertising (add the service just before we start advertising):
/* Start advertising */
- (void)startAdvertising {
NSLog(#"Starting advertising...");
NSDictionary *advertisment = #{
CBAdvertisementDataServiceUUIDsKey : #[self.serviceUUID],
CBAdvertisementDataLocalNameKey: self.serviceName
};
[self addServices];
[self.peripheralManager startAdvertising:advertisment];
}
Call to start advertising within didUpdateState:
/* Did update state */
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
switch (peripheral.state) {
case CBPeripheralManagerStatePoweredOn:
NSLog(#"peripheralStateChange: Powered On");
// When the bluetooth has turned on, start advertising.
[self startAdvertising];
break;
Update
On connecting to the peripheral app with OSX app LightBlue, an alert shows up on the app to request a pairing. Then LightBlue can read characteristics from the app. When connecting with the Chrome app, the pairing alert does not show up.
Having chatted to web-bluetooth developers at W3C, I have now been able to discover services from the device, by updating chrome from v51 to v52 (apparantly also v53 works).
However currently (though I am yet to try it out), reading characteristics is currently unsupported for OSX, as seen here.
(I have not yet looked into it but this may provide some options on how to overcome the issues described here.)
I've ran into this problem myself. I've developed a simple app that scans for devices and their services, but all devices found always report back an empty array of services. I have this problem on both OsX and ChromeOS.
There's an exception to this: when the device has previously been paired, I was able to get the services array on ChromeOS. This is confusing to me, since nothing I've read anywhere says a device has to be manually paired first, and there's no programatic way to pair a device one discovered.
EDIT: Upgrading to Chrome 53 fixed the problem. The site linked in louisdeb's answer is related to web-bluetooth, not chrome.bluetooth, so characteristics may actually work.