During the COVID-19 pandemic, I was one of the lucky ones who was able to keep my job and work from home. At least, so far... It's one of the benefits of working in computer programming: you can often work from anywhere, so long as you have a functioning VPN. One of my tasks over the quarantine period was to try to configure an application to support automatic updates. These applications are UWP apps built for a Windows IoT installation on a Raspberry Pi (RPi). It's an internal application that is still in a Beta stage, so it's not suitable to release over the Microsoft Store (at least, not yet). We had been manually updating the handful of devices in the field for the past two years, and as the number of deployed devices has grown, having to send someone out to manually update them was becoming a pain. Even moreso in this new era of social distancing.
Many Windows apps can be built in Visual Studio to support automatic updates.
Windows IoT has not been the best of environments to work in. It lacks a lot of functionality built-in to other Windows environments, and documentation for it is spotty, at best. Back in 2018 (shortly after we had started deploying the apps in the field), Microsoft released an update for Windows and Visual Studio that allows UWP apps to be built such that they automatically periodically check a specified server location for new updates. Awesome! It sounded like there would be a simple solution!
Be sure to update the version number.
I built the app with auto-updates enabled for every hour, deployed it to my test RPi over the Windows Device Manager, and deployed the update package and .appinstaller file onto our test server.
Then I waited.
Select an appropriate frequency to check for updates. I set it to check every hour while testing.
And nothing happened.
Nothing is ever easy in Windows IoT...
The app wasn't checking the server for the updates. I checked our server logs and found that it wasn't receiving any requests for the .appinstaller file or build package. I even let it run over night, in case it needs a whole day (or something) to start working. No luck. This wasn't going to be as simple a solution as I thought. Nothing can ever be simply when you're working with Windows IoT...
I then spent a couple weeks going down the rabbit hole of trying to figure out why it wasn't working. I found lots of forum threads, blogs, and MSDN posts about using and troubleshooting the auto update for UWP apps built for desktops, but I couldn't find anything specific to IoT. And none of the desktop solutions were working for me. I eventually came across another MSDN post about using the App Installer with MSIX. In the comments, a couple people said that they couldn't get this to work in IoT, and asked if it's supported. The response from the author basically came down to "I don't know. I'll have to ask around", but he never responded with a concrete answer.
Even the person writing the MS tech blog about auto-updates didn't know if IoT supported it.
So I was left wondering if this would even work for IoT, and started to become resigned to the likelihood that I would have to write my own code to provide auto-updates. But I was determined to find out for sure, so I kept digging.
I started looking into whether the update API was exposed in C#, and I found the MSDN documentation for the function that checks the web server for the updates. Here, I finally found the piece of information that set me on the right track.
"This method only works for applications installed via .appinstaller files."
- MSDN
The usual process of installing an application onto the RPi via the Windows Device Portal apparently wouldn't work, since the auto-update process is only supported if the app was installed using the .appinstaller file. It also means that I can't launch a test build of the app directly from Visual Studio, which is going to make debugging a lot more annoying. Instead, I had to run some PowerShell commands in order to install the application from the web URI. After logging into the RPi using Enter-PSSession, I would run the command:
Add-AppxPackage -Appinstaller "https://[remote server URL]/[App Name].appinstaller"
Reaching the solution
This installed the app, but I still couldn't get it to run because it was installed onto the RPi's Administrator account, instead of the DefaultAccount. I had to follow the procedure outlined in this MSDN article to log in to the RPi's DefaultAccount via Secure Shell. Unfortunately, doing so converted the PowerShell window into a cmd window, which prevented me from running the Add-AppxPackage command. I had to write a PowerShell script file with those commands, FTP it into the RPi's DefaultAccount's Downloads folder, and execute it from the cmd window after logging in via SSH.
Then I ran into yet one more problem. The PowerShell script file would terminate with an "Access Denied" error while trying to run the Add-AppxPackage instruction. After another day of trial-and-error, I finally stumbled across a work-around: if I installed the application to the RPi's Administrator account first, and then used SSH to run the PowerShell script, the application would install! I was finally able to launch the application on my RPi via the .appinstaller file. I quickly alt-tabbed into Visual Studio to change the version number and rebuild the app, then pushed the app package to the web server.
Then I waited for an hour.
Nothing happened.
I waited for another hour, just to be sure.
Nothing happened.
So I tried rebooting the RPi. You know, if it doesn't work, just try turning if off and on again. Eureka! The app relaunched with the new version number. Success! The installer apparently doesn't automatically restart the app after downloading the update. I had to do that manually. But once the app relaunched, it had the new version number. So, after several weeks of digging through MSDN, Stack Overflow, and numerous tech blogs, and lots of consulting with Chichian, I finally discovered once and for all:
Auto-updates using a .appinstaller file does, indeed work for Windows 10 IoT Core applications running on a Raspberry Pi!
But boy, did I have to jump through a lot of hoops in order to get it to work.
Of course, there's still more work to be done. This particular app is intended to run as the "default startup app" for the Pi, which means it is always running. If it crashes, or the Pi power cycles (for any reason) our app will automatically relaunch, but it won't automatically close and relaunch after it downloads an update. The user would need to manually restart the app, or the app would have to crash and restart, or the RPi would have to power cycle. Not exactly working as intended. So now I need to figure out a way to detect when a new app package has been downloaded, and then force the app to automatically restart in order to apply the update.
Nothing can ever be simple when you're working with Windows IoT...