Cloud Four Blog

Technical notes, War stories and anecdotes

Mobile Web? iPhone, Android, and BlackBerry, mostly.

Here are a few interesting tidbits from some mobile websites we’re monitoring. The following stats are from the two month period inclusive of December, 2010 and January, 2011. The sites are U.S. centric and tend to draw from a younger (18-35 year old) demographic.  You can make what you will of this data, I just thought it was interesting to look some actual numbers from working sites.

The data set contains 5,408 unique user agent strings submitted to the mobile sites during the sample period.  These UAs are mostly minor variations on a few major device categories, so we’re thankful that the WURFL API collapses similar UAs into the same base device for us.  There are also a fair number of weird UAs, as you’ll find whenever you traipse through server logs.

For the approximately 6 million site accesses over this period, 96% of all traffic arrives via iPhone, Android, or BlackBerry.  And of those, iPhone dominates site usage with a whopping 63% of all traffic.  While 4% of the traffic falls into the “Other” category, that does translate to about 240,000 site accesses from those other guys, so it seems reasonable to care about their experience too.  Okay, some of those are bots, so maybe we don’t worry too much about those.

Mobile Stats

The iPhone traffic is pretty much split between OS 4.x (78%) and OS 3.x (21%), with a tiny smattering of other stuff mixed in.  Virtually all Android traffic is coming from Android 2.x devices, though for this purpose I didn’t bother to break it down further than that.   BlackBerry traffic is interesting because, while BlackBerry 5.x represents 68% of the traffic, the “older” BlackBerry 4.x devices still represent 16% of all BlackBerry traffic we’re seeing.  I put that “older” in quotes because, c’mon, they’re not that old — and we know from experience that some people really love those devices (or are too afraid to let them go).  The BlackBerry 6.x devices are also about 16% of all BlackBerry traffic for these sites, which means that 84% of BlackBerry traffic… isn’t.

So, that’s all for now.  I don’t have any stunning revelations, but thought someone, somewhere may be wondering about mobile traffic breakdown for some project they’re working on, and perhaps this will help.

Presentation: Mobile Web on Drupal!

Hi, everybody! Last night I gave a presentation to the Portland, Ore., Drupal Users Group (PDXDUG) about the vast topic of the mobile Web on Drupal. There’s a whole lot crammed into this 94-slide deck (and I delivered it in 45 minutes—whew!), and some of it will be old hat to mobile Web folks.

General contents:

  • Overview of current leading Mobile Web development philosophies
  • The chaotic reality
  • A couple of core tenets I try to stick to
  • Drupal for mobile: the good and the bad
  • Some early 3rd-party Drupal mobile modules
  • Code: Framing out our own hypothetical mobile Web module to detect devices, theme-switch, etc.
  • Where to from here/further reading

When is Mobile Safari not Mobile Safari?

When it is AppleCoreMedia. Let me explain.

I wrote recently about a quiz that we built that included HTML5 audio files. In addition to the volume problems already discussed, we encountered intermittent issues where the audio either would not play or would play much later than it should.

For the quiz, we preloaded the HTML5 audio file via javascript. To make sure that the file was set up for caching, we set far future expires headers as well as making sure the file was sufficiently small.

Whenever we displayed the screen that told the user whether or not they got a question correct, we play the preloaded audio file. Most of the time this worked flawlessly, but occasionally the iPhone would play the sound long after the results screen was displayed or not play the file at all.

That’s when things started getting weird. We watched the log files and started seeing some odd behavior. Here’s what we saw:

"GET /trivia HTTP/1.1" 200 2346 "-" "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5"
"GET /media/quiz_wrong.m4a HTTP/1.1" 206 337 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /media/quiz_right.m4a HTTP/1.1" 206 337 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /trivia.css HTTP/1.1" 200 988 "-" "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5"

See what happened there? The user agent string changed from the normal Mobile Safari one (AppleWebKit) to one I hadn’t seen before: AppleCoreMedia.

It makes sense when you think about it. All video and audio playback gets handled by a system component. That component is responsible for downloading the asset. Ergo, the user agent string should change accordingly.

Surprisingly, this is true on desktop Safari as well. The equivalent user agent string is “Apple Mac OS X v10.6.6 CoreMedia v1.0.0.10J567.”

AppleCoreMedia Caching

On its own, the change from Mobile Safari’s user agent string to AppleCoreMedia’s user agent string would hardly be worth noting. But we were encountering problems with the sound not playing all of the time.

Because we noticed the sound problems happened more frequently on 3G than on WiFi, we started to suspect network issues and wondered if the audio file that we preloaded was getting cached properly. Here’s what we found in the logs:

"GET /trivia HTTP/1.1" 200 2346 "-" "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5"
"GET /media/quiz_wrong.m4a HTTP/1.1" 206 337 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /media/quiz_right.m4a HTTP/1.1" 206 337 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /trivia.css HTTP/1.1" 200 988 "-" "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5"
"GET /media/quiz_wrong.m4a HTTP/1.1" 206 5494 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /media/quiz_right.m4a HTTP/1.1" 206 2597 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /js/trivia.js HTTP/1.1" 200 2618 -" "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5"
"GET /media/quiz_right.m4a HTTP/1.1" 206 337 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /media/quiz_wrong.m4a HTTP/1.1" 206 337 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /media/quiz_right.m4a HTTP/1.1" 206 2597 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /media/quiz_wrong.m4a HTTP/1.1" 206 5494 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"

All of that occurs before the first quiz answer is submitted so it is only in relation to preloading the audio. The files get downloaded multiple times. Lest you think this is simply http chunking per the 206 responses, here are the file sizes:

-rw-r--r-- 1 cloudfour psacln 2256 Dec 10 17:33 quiz_right.m4a
-rw-r--r-- 1 cloudfour psacln 5153 Dec 10 17:33 quiz_wrong.m4a

Before the audio file is ever played, AppleCoreMedia has downloaded 11,662 bytes for quiz_wrong.m4a. At 5153 bytes, the source file is less than half of the total bytes download.

When we could replicate the audio problems, we would find that the server would return a 304 response to AppleCoreMedia letting it know that the m4a had not been modified, but then AppleCoreMedia would go ahead and download it anyways. Of course, this behavior was inconsistent making it difficult to troubleshoot.

To try to narrow down the behavior, I created a simple page with links to m4a and mp3 files. I didn’t do any HTML5 audio embedding and didn’t include javascript. The page was as vanilla as can be. The test file size was 30,196 bytes. Clicking on the m4a link with a clear cache had the following result:

"GET /examples/applecoremedia/trailer_iphone_sound/ HTTP/1.1" 200 653 "-" "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5"
"GET /examples/applecoremedia/trailer_iphone_sound/trailer_iphone_trimmed.m4a HTTP/1.1" 200 30551 "-" "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5"
"GET /examples/applecoremedia/trailer_iphone_sound/trailer_iphone_trimmed.m4a HTTP/1.1" 304 233 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /examples/applecoremedia/trailer_iphone_sound/trailer_iphone_trimmed.m4a HTTP/1.1" 206 22410 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /examples/applecoremedia/trailer_iphone_sound/trailer_iphone_trimmed.m4a HTTP/1.1" 304 233 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /examples/applecoremedia/trailer_iphone_sound/trailer_iphone_trimmed.m4a HTTP/1.1" 206 22410 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"

This is even more confusing than the last example. The full m4a file is first downloaded by Mobile Safari. Then AppleCoreMedia asks the server if the file has been modified and is told by the server that it hasn’t. AppleCoreMedia then ignores this information and proceeds to download the file. Then it repeats the process for good measure.

And despite setting far future expires headers for everything, the next time you play the m4a file, the same process is repeated.

Because of the issues we had previously had with short audio files, I decided to try a longer video file to see what it would do. Again, I set up a simple page and linked to the video file. The video file was 3,995,176 bytes (3.8MB).

"GET /examples/applecoremedia/trailer_iphone/ HTTP/1.1" 200 729 "http://www.cloudfour.com/examples/applecoremedia/" "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5"
"GET /examples/applecoremedia/trailer_iphone/trailer_iphone%20-%20iPhone.m4v HTTP/1.1" 206 386 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /examples/applecoremedia/trailer_iphone/trailer_iphone%20-%20iPhone.m4v HTTP/1.1" 200 70500 "-" "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5"
"GET /examples/applecoremedia/trailer_iphone/trailer_iphone%20-%20iPhone.m4v HTTP/1.1" 206 71910 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /examples/applecoremedia/trailer_iphone/trailer_iphone%20-%20iPhone.m4v HTTP/1.1" 206 386 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"
"GET /examples/applecoremedia/trailer_iphone/trailer_iphone%20-%20iPhone.m4v HTTP/1.1" 206 3995571 "-" "AppleCoreMedia/1.0.0.8C148 (iPhone; U; CPU OS 4_2_1 like Mac OS X; en_us)"

Mobile Safari still downloads some part of the m4v file, but it isn’t the full file size (70500 bytes). It is unclear what it does with this data chunk nor why it appears to only be part of the data, but the server reports a 200 response instead of 206.

The next time the video is played, the same pattern repeats. Nothing appears to be cached. Based on previous research into iPhone 4 cache sizes, I would have expect even the 3.8MB video to get cached.

What I Expected to See

To illustrate what I expected to see, I ran with my simple audio file test in Firefox. The only change I made was to use an mp3 file instead of an m4a.

"GET /examples/applecoremedia/trailer_iphone_sound/ HTTP/1.1" 200 653 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3"
"GET /examples/applecoremedia/trailer_iphone_sound/trailer_iphone_trimmed.mp3 HTTP/1.1" 200 28185 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3"

No matter how many times I load the mp3 file and then hit the back button to the web page containing the link, neither the web page nor the mp3 file were downloaded again. They had been successfully cached.

What does all this mean?

I wish I knew. For our project, it meant that we could not consistently ensure that the audio file would play at the right moment when the message was displayed on the screen letting the user know if they had got the question right or not. This issue combined with the volume issues caused us to remove the feature.

For consumers using iPhones, it appears to mean that every time you watch a video or play an audio file, that your phone is going to download it again. This makes for a slower experience and puts a seemingly unnecessary burden on the carrier network.

For developers, it is important to realize that video and audio playback in Mobile Safari is not handled by Mobile Safari even if you’re controlling it via javascript and the system media player is never visible. Everything you’ve learned about how Safari and Mobile Safari handle the downloading of assets doesn’t apply when AppleCoreMedia is doing the work instead of Safari.

Finally, this is just one more clue about how mobile is still frontier land. Even something that seems simple like a quiz that plays sounds can bump up against the boundaries of what we know about how mobile browsers work and what they can reliably do.

iPhone’s Magical Volume Buttons

A recent project pushed the boundaries of what is possible in mobile browsers. We learned a lot including some surprisingly simple things that everyone takes for granted like the volume buttons on the iPhone and how they work.

One of the features was a quiz that people could take on their phone. The quiz design was handled by another agency and they provided us with some cool sound files to use when the person got an answer right or wrong.

We built the site using HTML5 and quickly found that Android didn’t support HTML5 audio. Android 2.3 now supports HTML5 audio, but it was too late for this project.

On the iPhone side, we discovered what appeared to be a strange bug related to audio. Adjusting the volume didn’t seem to have any affect on the volume of the HTML5 audio clips. We began to fear that people playing the quiz would be upset when an ear drum piercing buzz came from the quiz and they couldn’t turn it down.

After nearly two weeks of believing HTML5 audio was broken, we encountered a phone that used to be loud that was suddenly quiet. What’s going here?

It was then that we learned about iPhone volume contexts. If you own an iPhone, you’re already aware of these volume contexts, but never consciously think about them because 99.9% of the time, it just works.

What do I mean by volume contexts? There is only one set of volume buttons and depending on what your iPhone is doing, they adjust different volume settings.

For example, if no audio or video is playing, the volume buttons adjust the ringer volume. However, when audio or video is playing, the volume buttons control the media audio level.

iPhone volume indicators

The iPhone provides feedback on what volume you’re adjusting by adding “ringer” above the volume level when you’re adjusting the ring level and leaves that out when you’re adjusting the volume of other audio.

But it’s not simply ringer or media contexts. The iPhone also keeps track of the volume level separately in each of these contexts:

* Headphone
* Headphone w/ microphone
* Speakers
* Bluetooth headsets

There may be more that I’m not aware of. I also don’t own a bluetooth headset so I’m relying on what I’ve been told.

The point is that it was nearly four years after owning my first iPhone before I gave any thought to how the iPhone was doing this. This is truly a remarkable design. It handles at minimum six different volume settings without the user ever giving thought to what volume they want to control.

They simply use the two volume buttons and 99.9% of the time the phone magically knows what they want it to do. You never have to think about it.

Until you build a quiz that falls into the .1% of the time when it doesn’t work the way you expect.

Our problem? the audio files were very short. Around a second each.

And unless you’re a speedster, you can’t hit the volume buttons quickly enough during that one second of audio.

Once that second passes, you’re back to adjusting the ringer volume and mistakenly wondering why the volume buttons don’t work on HTML5 audio.