Posts in Category "API tips"

AIR Native Extensions, ByteArray, and BitmapData

[Note: Updated on October 11, 2011 to note that multiple ByteArrays or BitmapDatas can be acquired at the same time in the AIR 3 implementation. —Oliver]

The new-to-AIR 3 native extensions capability includes not only a general-purpose API for manipulating ActionScript objects from native code, but also fast-path APIs that allow direct access to ByteArray and BitmapData objects.

Using these APIs, native code can get direct access to the memory that sits behind a ByteArray or BitmapData—no copies, no translations. In order to achieve that, native applications have to declare when they want access via an acquire call, and when they’re done via a release. In between the two, we restrict access to the rest of the API in order to guarantee the pointer that’s returned remains valid.

Furthermore, it’s possible to acquire two or more byte arrays or bitmaps at the same time so long as there no intervening calls to the rest of the API. This can be used, for example, to take image as an input for a filter and write directly to a second as the output.

As always, if you have ideas for enhancements to this and other features, you can vote for them at ideas.adobe.com/air.

See the Native Extensions documentation for more on working with ByteArray and working with BitmapData.

Unique Device Identifiers on AIR Mobile

Application developers frequently want a mechanism by which they can track a device’s identity. Such a value is frequently used in conjunction with copy protection schemes to, for example, limit the total number of devices on which a user can view purchased content.

AIR ostensibly does not include an API for obtaining such an identifier. Such an API would be straightforward to implement on iOS, since iOS itself provides an identifier that can be used for this purpose. However, on Android, no such facility is provided. Seems surprising to some, but even the Android team agrees.

How to work around it? I recommend using Math.random() to generate an ID on first launch and save it locally. It’s portable, and does a better job of protecting user privacy than the iOS device identifier does. Of course it can be reset, but then users also sometimes lose or dispose of devices, so any scheme needs a mechanism to flush old device IDs from the system as “no longer in use.”

EncryptedLocalStore and Storage Reliability

Recently I’ve fielded yet another question about the reliability of the EncryptedLocalStore API, which is usually a good indication that a topic deserves a post.

They key thing to understand about all storage options, encrypted or otherwise, is that there are no guarantees. Hardware failures, software failures, user error, or even deliberate user action can all lead to data disappearance and data corruption.

Applications should always be prepared to deal with these scenarios. This doesn’t necessarily mean you need fancy data-recovery options, although it might. It does mean your application shouldn’t be rendered useless upon encountering, for example, a malformed configuration file.

Does EncryptedLocalStore provide reliable storage? Probably not any more so than storing data anywhere else on a given device, as all of the same failure modes apply. Probably a bit less so, as the complex nature of the encryption mechanisms provide that many more things that can go wrong.

Note that this doesn’t excuse the presence of defects in those mechanisms. Rather, the point here is that more complex mechanisms have more points of failure, and sooner or later something will go wrong. If you’re concerned with reliability, your only realistic option is to plan for that scenario.

If you use EncryptedLocalStore to cache confidential information, such as passwords, then this issue is probably not serious, since users can generally re-enter that information. In effect, the user servers as the backup for this data.

If, however, you use EncryptedLocalStore to maintain encryption keys, or other items that the user is not aware of or cannot reproduce, then you should explore providing other backup mechanisms. If the encrypted data is valuable, you should probably back up the data and the key. If the data is a cache and is encrypted only locally, then logic to abandon and re-fill the cache may be sufficient. Whatever your scenario, an appropriate plan can no doubt be devised.

Replacing NativeWindow on mobile devices and TVs

The AIR NativeWindow API is a staple of desktop AIR applications, used to open multiple windows on the screen, each with its own stage. On mobile devices and TVs, the class is unsupported. So how does an application go about opening a window?

It doesn’t. On mobile devices and TVs, AIR applications can render only to the single available stage. The stage is automatically create and made visible when the application launches, and remains available until the application exits. In other words, it behaves essentially the same as the Stage does for Flash Player content.

Most code need concern itself only with the stage, and in this way can be made more portable between desktop, mobile, and TV deployment. For example, the stage directly provides information about its size. It also retains the ability to enter full-screen mode, and on mobile devices provides information about screen orientation.

If you have code that needs to check at runtime whether or not it can create new windows, simply check NativeWindow.isSupported.

Replacing Updater on mobile devices and TVs

Desktop AIR developers are, hopefully, familiar with our advice to implement an auto-update mechanism for your application first, even before you add your first feature. But what should you do on mobile devices or TVs, where the Updater API isn’t available?

On mobile devices and TVs, you should be happy that the platform has already implemented an update mechanism for you. As part of the better-developed application delivery ecosystem on these environments, applications are downloaded from application marketplaces of one type or another. These same marketplaces provide an update mechanism for these applications. Details vary as to whether the updates are installed automatically or require user intervention but, in any case, these will be the update mechanism users on these devices will be expecting.

If you need to write code that works in both places, you can check support at runtime via Updater.isSupported. This is a decision you might want to take into account earlier on, however, since there’s no value in including additional code, like the AIR Updater Framework, in applications targeting mobile or TV.

Replacing EncryptedLocalStore on mobile devices and TVs

If you’re familiar with the AIR desktop APIs and now looking at AIR on mobile or TVs, you may have noticed that the EncryptedLocalStore API is not currently available in either of these new targets. Here some tips on handling the situation.

First, consider whether or not the information your storing needs the additional security provided by the EncryptedLocalStore API. This is typically appropriate for items like passwords that need to be protected even from other software on the same device. However, items like configuration settings are best stored in a SharedObject or directly in a file.

Second, consider whether or not it’s appropriate to save this information on the target device without these protections. Unlike the desktop, mobile devices and TVs typically isolate all of the data stored by each application. A password stored on such a device therefore isn’t exposed to other applications, but can still be exposed if the device itself is compromised. Whether or not this is sufficiently secure depends on your application.

If the data is sensitive, and too sensitive to store without encrypted, you’ll have to fallback to querying this data from the user each time it’s required.

Finally, if you need to write shared code that can handle either situation, you can do so by writing a wrapper class that handles the switch at runtime. To perform a runtime check for EncryptedLocalStore functionality, simply query EncryptedLocalStore.isSupported.

API Tip: File.nativePath ≠ File.url

Recently we’ve seen a number of developers get tripped up on code similar to this sample:

// Don’t copy; this code is wrong
myURLLoader.load( new URLRequest( myFile.nativePath ));

This code is wrong: myFile.nativePath returns a file path, but the constructor to URLRequest expects a URL. It’s easy to make this mistake for two reasons:

  1. In ActionScript, both file paths and URLs are of type String. Thus, the compiler cannot determine that one is being used in place of the other.
  2. Sometimes, especially on Windows, this code works anyway. This is a side-effect of some too-lenient URL parsing code in the runtime.

Taken together, these two issues make it easy to write incorrect code that works on Windows and yet fails rather inexplicably when run on Mac OS or Linux. Fortunately, the fix is easy: substitute myFile.uri for myFile.nativePath, and all will work as expected.

// Correct version of the code
myURLLoader.load( new URLRequest( myFile.url ));

An Update on HTTP Content Encoding in AIR Applications

In keeping with my recent HTTP theme, I want to provide an update on a change to HTTP content encoding supporting in AIR 2.0.3, which has just been released.

The HTTP protocol permits server and clients to agree on encoding a document for transfer. For text and XML documents, this can significantly reduce transfer time, as they typically compress well.

Prior to the AIR 2.0.3 update, AIR supported gzip and flate encodings only on Mac OS and Linux. On Mac OS, this was a result of using the default OS HTTP stack, which supports those encodings by default. On Linux, which has no default HTTP stack, we implemented direct support for these options.

On Windows, the capability was not available because AIR uses the OS HTTP stack, but Windows did not support these encodings prior to Vista. Therefore, AIR could not depend on this capability being present and did not enable it. Developers could work around this by managing the HTTP content encoding header themselves and performing the decompression in ActionScript, although that’s not a particularly fun option to implement.

As of the AIR 2.0.3 release, we’ve added support for gzip and flate encoding for all versions of Windows, thus bringing it to parity with Mac OS and Linux. This change applies to all applications using the 2.0 (and later) namespaces. Applications using the 2.0 namespace will automatically benefit from this change when run on the 2.0.3 and later runtime; there is no need to re-publish.

Single Sign-On and HTTP Cookies in AIR Applications

[3-Jan-11: Please see More on sharing HTTP cookies with AIR applications for an important follow-on to this post.]

Consistent with our philosophy that an AIR applications should behave like any other application on your device, AIR leverages the underlying operating system HTTP stack when making HTTP requests. A while back, I write about how this enables the use of OS facilities TLS client authentication.

Sharing the system HTTP stack also enables the use of HTTP cookie-based single sign-on mechanisms across both multiple applications and between AIR applications and the users browser. Assuming all parties use the shared HTTP stack, this will work by default. AIR applications can individually disable managing cookies in this way via URLRequestDefaults.manageCookies.

It should be noted that not all applications use the shared HTTP stack. Firefox is a notable exception, which unfortunately means that cookie-based single sign-on between Firefox and AIR applications (indeed, most desktop applications) does not work. Also, on Linux, AIR uses its own HTTP stack because there is no default OS stack available.

AIR 2, NativeProcess, and 64-bit Windows

AIR 2 includes, among other new APIs, the ability to launch and communicate with sub-processes via the NativeProcess API. The API can launch executables bundled with your application or installed separately. On Windows, it turns out that the latter can be trickier than expected when running on a 64-bit system.

64-bit versions of Windows can run both 32-bit and 64-bit processes. This makes it possible for users to transition to 64-bit over time, keeping their 32-bit applications with them until 64-bit versions are available. This is why AIR applications, which for now remain 32-bit applications, can run on 64-bit versions of Windows. Most of the time, this works seamlessly.

In order to ease porting 32-bit applications to 64-bit Windows, 64-bit applications continue to use the %windir%\system32 directory for 64-bit libraries. This is great for 64-bit applications, which continue to work, even with explicit references to this directory. However, it would break all 32-bit applications, as they can’t load the 64-bit libraries.

To work around this, Windows automatically redirects most accesses to this directory from 32-bit applications running on 64-bit Windows to a different location: %windir%\SysWOW64. (WOW64, despite its name, is the subsystem that runs 32-bit applications on 64-bit Windows.) Again, most of the time this works seamlessly.

This approach will if you’re trying to launch an executable from %windir%\system32 if there is no 32-bit version of the same executable in %windir%\SysWOW64. In this situation, you can see in Explorer that the executable exists in %windir%\system32, but when you check the same location via your 32-bit application, the file, due to redirection, will not be present. If this happens to you when using the AIR NativeProcess API, you’ll get an error (3214) that the executable you’re trying to use is invalid.

The workaround to the workaround? Starting with Windows Vista, you can force direct access to the 64-bit directory by using %windir%\sysnative. It’s not a real directory, but it is recognized by the redirection logic and pointed to the real %windir%\system32 directory. For Windows XP, you’ll have to resort to system calls to turn off redirection. For a complete description, see the MSDN File System Redirector documentation.