Mark L. Murphy's Blog, page 62

October 23, 2013

Linkfy and autoLink Need a Custom URLSpan

Many of you use android:autoLink in TextView widgets to convert URLs, email addresses, phone numbers, and the like into clickable items. Each launches an activity to handle the request, via an ACTION_VIEW Intent on a suitable Uri.



Under the covers, this is handled via Linkify, and many of you use Linkify directly, as it gives you a bit more control over what gets converted into links and how.



The problem is in the class that implements the links themselves: URLSpan.



URLSpan is a CharacterStyle, used to format text in a TextView. As with other styles, like ForegroundColorSpan, it applies a visual effect to a range of characters inside of a CharSequence, specifically something that implements the Spanned interface. As the name suggests, URLSpan is used for formatting links to URLs.



URLSpan is also a ClickableSpan, one that can be clicked upon by the user to perform some action. The URLSpan is called with onClick(), much like an OnClickListener does.



The problem lies in its onClick() implementation: it just blindly calls startActivity() on the Intent that it creates.



Nowadays, startActivity() is a riskier operation than it had been before. It is very possible that there will be no activity to handle a particular URL structure, particularly on Android 4.3+ devices for users running in a restricted profile. But URLSpan does not catch the ActivityNotFoundException that could be thrown by startActivity(), and there is no standard place where you could introduce your own try/catch block to handle places where onClick() fails.



The solution is to create your own span, one that handles the ActivityNotFoundException in a manner that fits your application, and apply it to the SpannedString that comes out of the autoLink/Linkify process.



private static class DefensiveURLSpan extends URLSpan {
public DefensiveURLSpan(String url) {
super(url);
}

@Override
public void onClick(View widget) {
try {
android.util.Log.d(getClass().getSimpleName(), "Got here!");
super.onClick(widget);
}
catch (ActivityNotFoundException e) {
// do something useful here
}
}
}

You can apply that by finding all the current URLSpan instances and replacing them with your own class:



private void fixTextView(TextView tv) {
SpannableString current=(SpannableString)tv.getText();
URLSpan[] spans=
current.getSpans(0, current.length(), URLSpan.class);

for (URLSpan span : spans) {
int start=current.getSpanStart(span);
int end=current.getSpanEnd(span);

current.removeSpan(span);
current.setSpan(new DefensiveURLSpan(span.getURL()), start, end,
0);
}
}

Note that you do not want to call setText() on the TextView, thinking that you would be replacing the text with the modified version. You are modifying the TextView’s text in place in this fixTextView() method, and therefore setText() is not necessary. Worse, if you are using android:autoLink, setText() would cause Android go back through and add URLSpans again.



You may also wish to keep tabs on this issue, which is tracking a requested change to URLSpan to handle this scenario better.

 •  0 comments  •  flag
Share on Twitter
Published on October 23, 2013 21:32

October 22, 2013

ACTION_GET_CONTENT? Support EXTRA_ALLOW_MULTIPLE, Please

API Level 18 sneaked in a new standard for ACTION_GET_CONTENT that addresses a long-standing request: multiple selections.



ACTION_GET_CONTENT requests can add an EXTRA_ALLOW_MULTIPLE boolean extra. Setting this to true means that the requester would like the user to be able to allow to select multiple pieces of content, not just one.



If you publish an activity with ACTION_GET_CONTENT support, please consider adding support for EXTRA_ALLOW_MULTIPLE. Requesters of ACTION_GET_CONTENT will need to handle the case where the user chooses some activity that lacks EXTRA_ALLOW_MULTIPLE support. However, some developers will not bother trying to ask for EXTRA_ALLOW_MULTIPLE, until there is a decent chance that some activity will actually honor it.

 •  0 comments  •  flag
Share on Twitter
Published on October 22, 2013 21:11

October 15, 2013

SMS Apps: I Warned You...

SMS clients have long lived on the fringes of what is acceptable in terms of SDK compliance. As I mentioned in my recent blog post suggesting a particular security technique for SMS clients, a lot of the SMS-receiving support in Android is undocumented and outside the SDK.



That will change… with some substantial impacts on existing apps.



One of the specific things that I have repeatedly warned against — despite lots of StackOverflow downvotes — is that relying on undocumented providers is risky. The SMS ContentProvider has long been undocumented.



Well, the good news is that Google is documenting that provider and various SMS-related Intent structures.



However, as of Android 4.4, apps can no longer write to the SMS ContentProvider … unless they are the one magic SMS client that the user has established as a default.



The Android Developers Blog post on this subject outlines the basic changes, though things will become a bit clearer once we get the Android 4.4 documentation. And, I suspect that there will be a fair bit of confusion, for developers in the short term, and for users who get upgrades to Android 4.4 over time.



I do applaud Google for finally making SMS clients be a first-class part of the Android SDK. Let’s hope that the limitations that they have placed will prove successful over time.

 •  0 comments  •  flag
Share on Twitter
Published on October 15, 2013 05:41

The Busy Coder's Guide to Android Development Version 5.2 Released

Subscribers now have access to the latest release of The Busy Coder’s Guide to Android Development, known as Version 5.2, in all formats. Just log into your Warescription page and download away, or set up an account and subscribe!



This chapter adds over 100 pages, though it loses the OUYA chapter, as previously noted.



This update contains:





The first two of several chapters on Gradle, introducing Gradle itself and covering fairly basic Gradle-based builds of projects using the classic directory structure. Gradle coverage will continue to be expanded over the next few updates to the book.





A chapter on navigation drawers, focusing on DrawerLayout.





A chapter on miscellaneous security, mostly covering how to examine the public key of the keystore that was used to sign an APK.





A chapter on event buses, comprising an expanded version of the previous LocalBroadcastManager coverage, plus Square’s Otto and greenrobot’s EventBus.





Expanded coverage (again!) of Maps V2, adding in flat markers and marker rotation, updating an info window (e.g., with an asynchronously downloaded image), marker animation, and improvements to other samples.





More material on foreground services, device administration and password management, and animating arbitrary data types.





Miscellaneous updates and errata fixes.





If Android 4.4 ships in the next week or so, I will aim to have another book update out by mid-November that begins to address it. Otherwise, the next update probably will not be until the end of November, or possibly early December, again based in part on the release timing of Android 4.4.

 •  0 comments  •  flag
Share on Twitter
Published on October 15, 2013 05:41

October 12, 2013

Dropping the OUYA Chapter

I will be dropping the OUYA chapter in the next update to The Busy Coder’s Guide to Android Development, which is slated to be released next week.



My OUYA console is presently demanding that I agree to a new “Marketplace Terms of Service” in order to do anything with that console. Failure to accept these terms logs me out of the console. Hence, the “Marketplace” is apparently the console itself, despite the fact that there are plenty of ways to use the OUYA that do not involve downloading and using apps from the OUYA app distribution portal.



These revised terms of service contain clauses that would prevent me from maintaining the OUYA chapter:





Those who agree to the terms are no longer allowed to use the OUYA name without written permission. This will make it rather difficult to refer to the console, as “The Console That Must Not Be Named” is a bit wordy.





Those who agree to the terms are not allowed to use the console “for any commercial purpose or for the benefit of any third party or in any manner not permitted by these terms”. This book would reasonably be considered a commercial endeavor.





Those who agree to the terms are not allowed to “violate any applicable law or regulation”, with no scope control on “applicable”. I jaywalk with some frequency, and it would not surprise me if my very existence violates some law somewhere. Besides, I write a lot, and therefore Zombie Cardinal Richelieu will probably be able to hang me.





Those who agree to the terms cannot “encourage or enable any other gamer or individual to do anything listed above”, and I fear that writing about the OUYA might “encourage or enable” people to use OUYA’s name without written permission, even if I were to somehow craft the chapter to avoid that name. And so even if I were to obtain written permission, I am not in position to require that my subscribers each attest that they have received such permission or otherwise agree never to use the OUYA name.





I have not accepted these terms of service, and therefore I can no longer use my console for anything much more than a doorstop, a role for which it is not well-designed.



Subscribers doing OUYA development should make a point of holding onto a copy of Version 5.1 of the book, to have a copy of the OUYA chapter in its last-published state.



If, at some time, I determine that there is a revision of these terms of service, where these clauses are appropriately amended (or perhaps removed), I will look to restore the chapter to the book.



I apologize for any hassle that this may cause, and affected subscribers are welcome to rip me a new one if desired.

 •  0 comments  •  flag
Share on Twitter
Published on October 12, 2013 11:24

October 11, 2013

Make Passwords Expensive

A simple workflow for encrypting data on a device is to collect a passphrase from a user, then pass that along to the encryption engine to use to encrypt (and decrypt) whatever data you give to that engine. In fact, you may be forgiven if you think that’s the entire workflow, as it fits the classic depiction of implementing cryptography.



However, depending upon your engine, that workflow may be good or bad.



The problem is that most users of our apps are human, and humans are notoriously bad at remembering passphrases. As a result, the passphrases tend to be short. Short passphrases are intrinsically weak and are prone to recovery via brute force attacks.



You can help this somewhat by using a key derivation function. One thing that separates some approaches to cryptography (e.g., SQLCipher for Android)(http://sqlcipher.net/sqlcipher-for-an...) from others (e.g., direct use of javax.crypto) is whether the engine automatically applies a key derivation function for you or not.



The objective of a key derivation function is to make passphrases expensive:





The actual passphrase used by the underlying encryption algorithm is something consistently long, rather than literally what the user types in for the passphrase





The act of generating the actual passphrase itself is expensive enough to slow down brute force attacks that rely upon dictionaries and the like





A key derivation function is usually based on a one-way cryptographic hash. A classic solution was to use something based upon MD5, but nowadays that is nowhere near sufficient.



SQLCipher for Android automatically applies PBKDF2 (originally 4,000 rounds, being bumped to 64,000 in SQLCipher 3.0.0). Popular solutions traditionally used on the server include bcrypt and scrypt.



If you are storing data locally on the device, and you want to encrypt it, make sure that your encryption approach is using some key derivation function, whether supplied by the crypto library or one that you add on separately.



Similarly, make sure that your Web service uses a key derivation function for storing the hashed passphrases for comparison purposes. Not only will this slow down attacks (coupled with other steps you might take to detect and block brute force approachs), but it will make your accounts database more secure in case your server is hacked and miscreants make off with the database.

 •  0 comments  •  flag
Share on Twitter
Published on October 11, 2013 10:22

October 7, 2013

Secured Broadcasts and SMS Clients

Some broadcast Intents can only be broadcast by the system. Sometimes, these will be documented as such. For example, only the system can broadcast ACTION_DEVICE_STORAGE_LOW, and the JavaDocs for that Intent action state:




This is a protected intent that can only be sent by the system.




Hence, assuming that there are no bugs in the system, your apps can rest assured that if you receive such a broadcast, it was really sent by the system, and was not spoofed by some rogue app on the device.



However, there are some system Intent actions that are secured somewhat differently, where the broadcaster holds some permission. Your app can require that permission (e.g., android:permission on your <receiver> in the manifest) to ensure that you will only receive broadcasts by somebody holding that permission. Doing this, once again, can help prevent your app from being spoofed by malware or other miscreant apps on a user’s device.



For example, take SMS.



It is 2013, and Google steadfastly refuses to document the means by which an app can receive SMS messages. One downside to their stubbornness in this area is that app developers may not know how to protect their apps from being spoofed.



The undocumented SMS_RECEIVED Intent action is used for an ordered broadcast that is sent when an SMS arrives. By default, only one app will receive it: whatever the device’s default SMS client is. But, users can install third-party apps that also receive this broadcast, and those apps can use android:priority to arrange to get the message before the default client.



However, not only is that Intent action undocumented, but so is the means to prevent your app from being spoofed. After all, if SMS_RECEIVED is just a broadcast Intent, what’s to stop any other app from sending that broadcast itself, fooling other apps on the device into thinking that an SMS arrived?



The answer lies in a permission: android.permission.BROADCAST_SMS. This particular permission actually is documented, albeit not especially well.



If you look at the AOSP messaging app’s manifest, you will see that the <receiver> for SMS_RECEIVED requires this permission. And, that permission is a signature-level permission, meaning that apps can only hold that permission if they are signed by the same signing key as the app that defined the permission. In this case, the platform itself defines the permission, and so to hold BROADCAST_SMS, the app has to be signed by the signing key of the device’s firmware, which usually means that the app is part of that firmware, whether it is device firmware or a third-party ROM mod.



SMS client apps that do not mirror what the AOSP client does – requiring senders of SMS_RECEIVED to hold BROADCAST_SMS – are at risk of having other apps send spoof SMSes to them. For example, malware could use this to deliver fake SMS messages to the user, containing links to yet more malware.



If you are implementing an SMS client, it behooves you to add android:permission="android.permission.BROADCAST_SMS" to your <receiver> that handles your SMS broadcasts.

 •  0 comments  •  flag
Share on Twitter
Published on October 07, 2013 10:31

October 4, 2013

Site Downtime Tomorrow

The CommonsWare family of Web sites, including the Warescription site, will be down for a bit tomorrow morning Eastern Time for migration to a new server.

 •  0 comments  •  flag
Share on Twitter
Published on October 04, 2013 15:53

October 2, 2013

Converting Animated GIFs to AnimationDrawables

A few days ago, Marc Poppleton tweeted:




Instead of using an AnimationDrawable or the Animation framework, embed a WebView displaying an animated GIF.




Wanting an animated GIF is understandable, in that animated GIFs are easy for designers to create. However, using a WebView for the sole purpose of displaying an animated GIF is, to use an American expression, “like swatting a fly with a Buick”.



So, I tossed together a quick Ruby script to convert animated GIFs to AnimationDrawables at the command line.



If you are familiar with Ruby environments, using this script should be a matter of downloading it and installing the three gems that it depends upon (RMagick, Slop, and Builder). RMagick is a bit of a pain to install, due to its dependence upon ImageMagick, but there are recipes for getting that going on most major OS versions. It is set up to be marked as executable via chmod and run directly on Linux; on other operating systems, you would run the script via the ruby interpreter.



There are four command-line switches:





-i provides a path to the animated GIF to be converted. The name of this GIF will be used as the basis for your resource names, so name the GIF how you want the output to be named.





-o provides a path to a directory (that will be created if it does not already exist) where output files will be written, representing the generated AnimationDrawable.





-d should be a standard Android density indicator (e.g., mdpi, hdpi), indicating what density directory should be used for dumping the GIF frames into as PNG files.





Optionally, --oneshot adds the android:oneshot="true" attribute to the AnimationDrawable, indicating that instead of infinitely looping, it should only run through the animation once.





So, if you were to run:




ruby gif2animdraw -i something.gif -o res -d mdpi



you would get res/drawable/something.xml with the AnimationDrawable itself, plus res/drawable-mdpi/something_*.png files, one per frame in the animation. The AnimationDrawable XML will contain android:duration values for the frames based upon the input GIF.



I have only tested this on a couple of GIFs, and so this script may have some bugs. Comment on the gist if you run into problems with the script.



Eventually — if nobody beats me to it, which would be really cool — I’ll slap a Web front end on this functionality, so you upload the GIF and download a ZIP with the animation.

 •  0 comments  •  flag
Share on Twitter
Published on October 02, 2013 03:59

September 30, 2013

My Brain Has Moved, And Other Whirled Tour Updates

My Skills Matter “In the Brain” session — conveniently using my brain — has been moved to the evening of October 22nd instead of the 23rd.



Also, I now have a link for the Android Alliance Philadelphia presentation on the evening of October 16th.



So, the full run-down of presentations that form the 2013 CommonsGuy Whirled Tour is:





Lehigh Valley Tech Meetup on 15 October in Bethlehem, PA





Android Alliance Philadelphia on 16 October





“Pushing the UI Envelope” day-long seminar on 22 October in London





Skills Matter “In the Brain of…” on 22 October in London





Apps World Europe on 23 October in London





Droidcon London on 25 October 2013 in London





Samsung Developers Conference on 27-29 October 2013 in San Francisco





AnDevCon San Francisco on November 13-15





I hope to see you there! And there! And there as well! :-)

 •  0 comments  •  flag
Share on Twitter
Published on September 30, 2013 22:41