Tuesday, December 14, 2010

I've been Gawked!

Lessons on security in light of Gawker Media breach, resulting in hundreds of thousands of compromised email accounts.

Never deal with Gawker again. I found I could not even delete my account on Gizmodo. That's pathetic- they let it get hacked and you can't even sever ties. Oh, and I couldn't log in to Gizmodo- I think my account was inactive, implying these clowns were keeping my data for no reason (and insecurely) or someone changed my password. Frankly, I hope someone is using my account to harass Gawker. Just remember, it's not me! Hahaha.

For lame things like commenting on websites (such as Gawker runs), create an email account you use for absolutely nothing but commenting on websites.

Don't use only one password for everything, or even variations on a theme. It's hard, but think about it. "One ring to rule them all..."

Delete things (everything, really) from your email frequently that may provide pointers to parties you do business with. These email leave a trail of what other websites you have accounts with. And if these accounts use your compromised email address and password, you are in trouble.

Don't save your credit card info on any website. Really, who can you trust? Just type it in each time. Even then it is still not completely safe, but less likely to be sent out through bittorrent.

Use passwords of at least 16 characters. Yes, you can remember it!




Friday, December 3, 2010

Netflix Movies for only $30, brought to you by Verizon

Yes, you read it right! You really can watch a Netflix movie in HD for only $30. That is, of course, if you have Verizon 4G service and a 5GB data plan. You see, after using up your 5GB watching 2 movies in HD on Netflix, you start paying $10 per GB. A 2 hour movie uses about 3.3GB, so that is a whopping $30 in overage fees.

Of course, Verizon will send you an email or text message to warn you when you hit your data cap a few days into the month. Please be sure to check frequently while watching a movie. Nice!

Thursday, October 21, 2010

Keeping up with 4G networks and LBS

Do you have LBS applications and is there a chance they will ever run on a 4G network? If the answer is yes to both (and I hope it is), then you need to start adding location capabilities to your apps so they continue to work properly when connected via 4G. Some of this is already taken care of. For example, if using a Sprint 3G/4G device and their location services, you will receive location data from Clearwire (the 4G provider) when on 4G.

Not all situations work this easily, however. Consider this case. You have customers using a location-aware app on a laptop embedded with Wimax. You app needs to know location to about a half mile accuracy. If you do not change your app to use a 4G location service, your customers can only get IP-based location- which is often only good enough to tell you what city you are in and likely NOT meeting the requirements of your application. As more and more people use 4G, this will become a real problem for you, especially as people start calling your care line. The solution? Integrate 4G location services now. It is not hard and will be worth the investment. See http://developer.clear.com/projects/activity/location-based-serv/ for how to do this.

In the future, 4G location will be available the way wi-fi and 3G location are today, but that will take awhile as 4G networks are and will continue to be built out over the next several years. In the meantime, don't be left behind.


Thursday, September 9, 2010

Apple? Android? NINTENDO!

I am glad to see Apple reacting to the Android threat by opening its door a little more to developers. This is great news for all developers since it helps spur innovation. Here's the basic story, but you can find much more on the web about it.

Now is this enough? No. It still costs more to develop for Apple devices than for Android. You need a Mac (although maybe that just changed with Flash becoming an option, but Adobe tools are the costliest of all) and you still pay yearly developer fees of $99. Not much, but compared to zero it is significant for lone developers.

But let's talk about what would really shake things up. There are over 115 million Nintendo DS (and variant) devices floating around the world. That's a big party to crash. Wouldn't it be interesting to have an open developer community around that? Could the little guys compete? Well, no one thought they could on smartphones and now look at it. Nintendo could use something big and this could be it.

Sunday, July 25, 2010

Youtube and Android

Youtube is great and has a wealth of API's. On mobile, though, I just want simple playback. Up until around July 22, 2010, you could pass a url like this:

http://www.youtube.com/get_video?video_id=6Yd5DQykZow&t=some_token&fmt=17

into an Android VideoView and it would play the video. This used progressive download meaning the video would play once it had buffered enough, then keep downloading as play progressed. It also provided very good quality, since fmt (format) controlled whether it was a low-bandwidth or high-bandwidth data connection. In case you are wondering, if on WiFi or Wimax, use high bandwidth. Then something changed and this type of url does not work anymore. I would love to know what happened. I suspect it was just too easy to download content from Youtube this way and save it to a file and go build your own site.

You still play videos a variety of ways on Android. Here are some options found by using the gdata API's for Youtube (see http://code.google.com/apis/youtube/2.0/developers_guide_protocol.html)

Using this url
you get an XML feed about a given video. Here's the part relevant to playback:

<media:content url='http://www.youtube.com/v/6Yd5DQykZow?f=videos&app=youtube_gdata' type='application/x-shockwave-flash' medium='video' isDefault='true' expression='full' duration='322' yt:format='5'/>

<media:content url='rtsp://v1.cache8.c.youtube.com/CiILENy73wIaGQmMZqQMDXmH6RMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp' type='video/3gpp' medium='video' expression='full' duration='322' yt:format='1'/>

<media:content url='rtsp://v3.cache4.c.youtube.com/CiILENy73wIaGQmMZqQMDXmH6RMYESARFEgGUgZ2aWRlb3MM/0/0/0/video.3gp' type='video/3gpp' medium='video' expression='full' duration='322' yt:format='6'/>

Format 1 = h.263, 5= flash video, and 6 = mp4. Formats 1 and 6 are low quality and meant for old cell phones on slow networks (176 x 144 resolution), but they work fine when passed to VideoView.setVideoURI(uri)

What's more interesting is the flash url (don't ask where html5 video comes in- I don't know). You can play this in a browser or in the Youtube application on HTC Hero and EVO and many more phones. Clicking the link will bring up the chooser for you to pick how to handle the video link. You don't need a VideoView; in fact, it won't work if you try one- go figure. Google is definitely having some struggle with all of this. Froyo (Android 2.2) will support flash, also, so lots of phones will work with the flash option.

There is still a question of quality of the playback- I want more control (i.e., flash video parameters for what I want) or the ability to give hints via SDP to the RTSP server, but no dice.

There is more mystery
This link
retrieves a different info feed entirely. It is all url-encoded name/value pairs, easy enough to decode and see that there are urls in it. In fact, you can download videos using them. The content type is flash, so if you download the video and save it as an *.flv file you could play the video with VLC (http://www.videolan.org)


Friday, July 16, 2010

Secure file distribution with apache, openssl, and curl

This is a long post. Be warned.
I have a problem to solve. I need to share a file on a regular basis with a small group of users. It has to be done securely and upload and download must be automated. It also has to be cheap and simple.

The plan is to layer HTTP Basic and SSL Client Auth for directory access to the BSA files. This provides encryption, strong authentication, and with HTTP Basic, the ability to disable an account easily (even though their client certificate may still be good. I didn't want to deal with certificate revocation lists). For retrieval, curl is a great option and meets all requirements.

Tools and assumptions
I used Ubuntu (8.04 or later), apache2 (2.2), and the latest curl and openssl packages. You know some *nix.

Apache2 configuration

The BSA file will be placed in a directory called 'bsa', so create this in /var/www (the default)

Add the following to the /etc/apache2/httpd.conf file to protect your new directory.

<Directory /var/www/bsa>

AuthType Basic

AuthName "Approved BSA users"

AuthUserFile /usr/local/apache/passwd/passwords

AuthGroupFile /usr/local/apache/passwd/groups

Require group BsaUsers

</Directory>

Note that this will be changed later for the final solution. I am just showing how to do this in general.

You will need to create the directories apache and passwd so that the path /usr/local/apache/passwd exists. Here is how to create the files 'passwords' and 'groups'.

group file creation

You simply make a text file (called 'groups') with an entry like this:

BsaUsers: user1 user2 user3

Everything after "BsaUsers:" are the user names for who has access. Each will need a password.

passwords file creation

For this you must use an apache tool for creating password files, htpasswd. The command below creates the passwords file (-c option) and adds one user, user1. You will be prompted to add their password.

>htpasswd -c /usr/local/apache/passwd/passwords user1

Adding more users to the password file

>htpasswd /usr/local/apache/passwd/passwords user2

>htpasswd /usr/local/apache/passwd/passwords user3

(each time you are prompted for the password for the user)

Note: htpasswd is in /usr/local/apache2/bin in case that location is not on your path.

Adding a new user

  1. add the user name to the 'group's file.
  2. add a password for the new user as above

Now test that http basic auth is working for the direcory 'bsa' in the root of the web server. After writing to the httpd.conf file you may need to restart the server.

TIP: apache2 service name is not httpd, it is apache2. So restart is this:

>service apache2 restart

Now on to how to access an http basic protected directory using curl. First, when you try curl against the protected directory it will appear to work. If you look at what you downloaded, though, it will be a 401 Authentication Required file.

Here is how to use curl on a resource (URL) requiring http basic authentication:

>curl -u name:password www.example.com

So for my example, the command is this:

>curl -u user1:password1 -O localhost/bsa/testfile.zip

This will send the credentials to the server and download and save the requested file. It's that simple.

Now for the main event: SSL CLient authentication set up

(see http://httpd.apache.org/docs/2.2/ssl/ssl_howto.html and http://httpd.apache.org/docs/2.2/ssl/ssl_faq.html for complete details)

Enabling SSL

>a2enmod ssl

>service apache2 restart

You can verify that /etc/apache2/mods-enabled now has the ssl.conf and ssl.load symbolic links in it.

Creating Certs and Keys

I tried CA.pl, a script for working with openssl for creating server certificates and it caused no end of problems arround the private key not being found. I believe it is all due to a format issue but not worth hassling with. By using openssl directly, as follows, I got it working easily.

Create a RSA private key for your server (will be Triple-DES encrypted and PEM formatted):

>openssl genrsa -des3 -out server.key 1024

Please backup this host.key file and the pass-phrase you entered in a secure location. You can create a stronger key by changing 1024 to something like 4096.

You can see the details of this RSA private key by using the command:

>openssl rsa -noout -text -in server.key

Create a self-signed Certificate (X509 structure) with the RSA key you just created (output will be PEM formatted):

>openssl req -new -x509 -nodes -sha1 -days 365 -key server.key -out server.crt

This signs the server CSR and results in a server.crt file. Set 'days' to whatever you like.

Copy the server.crt file and the server.key file to a directory under /etc/apache2. You can make your own- just don't use conf.d since that will cause other errors.

You must set up apache2 so your server uses SSL and your new certs. I did this using a virtual host for SSL. You will find these under /etc/apache2/sites-enabled/000-default (I tried using the main config file, but ran into problems. Using a virtual host works.)

Add the following to 000-default:

<VirtualHost *:443>

DocumentRoot /var/www

SSLEngine on

SSLCertificateFile /etc/apache2/conf/ssl/ca.crt

SSLCertificateKeyFile /etc/apache2/conf/ssl/server.key

<Directory /var/www/bsa>

#ssl client auth set up

SSLVerifyClient require

SSLVerifyDepth 1

SSLCACertificateFile conf/ssl/ca.crt

# http basic auth set up

AuthType Basic

AuthName "Approved BSA users"

AuthUserFile /usr/local/apache/passwd/passwords

AuthGroupFile /usr/local/apache/passwd/groups Require group BsaUsers

</Directory>

</VirtualHost>

Second, I ensure you can only access the private area using https with this:

<Directory /var/www/bsa>

Deny from all

</Directory>

This goes in the virtual host for port 80 (in 000-default) and blocks all access except through port 443. At this point, you have a web server that supports both http and https traffic with a protected directory (bsa) that can only be accessed as follows:

  • https
  • with username and password
  • and a client certificate

Now when I try to run the server, here's what I get. Note that the cert private key is found, as evidenced by being challenged for its passphrase.

>/etc/apache2# service apache2 restart

* Restarting web server apache2 Apache/2.2.12 mod_ssl/2.2.12 (Pass Phrase Dialog)

Some of your private key files are encrypted for security reasons.

In order to read them you have to provide the pass phrases.

Server localhost:443 (RSA)

Enter pass phrase:

OK: Pass Phrase Dialog successful.

When accessing using a browser I am getting the message I want- the server certificate is untrusted. This confirms apache is using my self-signed cert.

I accept the 'untrusted' cert, and get the following error in Firefox:

An error occurred during a connection to localhost.

SSL peer was unable to negotiate an acceptable set of security parameters.

(Error code: ssl_error_handshake_failure_alert)

This is also expected, since I am requiring client authentication, but my client has no certificate. Just to confirm, I removed the requirement for client auth from httpd.conf, and restarted the server. Now when I use https, I can get to the web page after accepting the cert. This is all good.

Next step- use a client cert for authentication

First, create a sample client certificate using own CA created earlier (and stored in 'selfCA').

Create a private key

>openssl genrsa -des3 -out server.key 1024

Create a CSR using your new key

>openssl req -new -key server.key -out server.csr

Sign CSR using your own CA

>openssl x509 -req -days 1100 -in server.csr -CA ../selfCA/server.crt -CAkey ../selfCA/server.key -set_serial 01 -out test.crt

You will be asked for the passphrase for server.key.

Now to use test.crt and the key. To work with curl, the cert and key need to be in the same file.

>cat test.crt server.key > client.crt

This just combines the files into one text file. Take a look.

Here's the finished curl command to download the file using client authentication and basic authentication.

>curl -u user1:password1 -k https://localhost/bsa/testfile.zip -E client.crt:passphrase1 -O https://localhost/bsa/testfile.zip

The explanation of everything

  • -u username and password set up on the server for http basic authentication
  • -k ignore certificate warnings from my server (since using self-signed CA with mismatched CN and hostname)
  • -E specifies file with the client cert and key to use plus the key passphrase
  • -O download and save the file from give URL and using the filename in the URL

Pretty cool, huh?

Thursday, July 1, 2010

Location-aware Twitter search app for Android


I just wrote and published a new free app for android. It is called Locobird and a chunk of the code is below. It uses your location to search twitter around you. What's new about this app is that it uses the Clearwire Location Platform to get your rough location when you are connected to the 4G network. Now you are wondering, how do I connect to that? Simple, you get an EVO.









This app uses the new static map API from Google and a java Twitter API from http://www.winterwell.com/software/jtwitter.php

Here's the code for using the Clearwire Location Platform (CLP)

package com.thehopemachine.locobird;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.Date;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONObject;
import org.json.JSONStringer;

import android.location.Location;
import android.util.Log;

public class WimaxLocationProvider {

private static String CLP_ADDRESS = "http://testlocation.clearwire-wmx.net:8000/json/";
private static Long STALE = 60000l; //one minutes worth of milliseconds
private static String HOST = "host";
private static String VERSION = "version";
private static String VERSION_VAL = "1.1.0";
private static String LATITUDE = "latitude";
private static String LONGITUDE = "longitude";
private static String ACCURACY = "accuracy";
private static String LOCATION = "location";
private static String ERROR = "error";
private static String QUERYARG = "/?app_id=";
private Location mLastFix = null;
public static String WIMAX = "the Clear 4G Network";
private String mHost = null;
private String mAppID = null;
private Date mLastCall = null;
public WimaxLocationProvider (String host, String appID)
{
mHost = host;
mAppID = appID;
}
public Location getLocation() throws Exception
{
//www.example.com/?app_id=freiu4898fhjrh4yshefheoiuhr
//do we have a last fix? is it still good?
String hostValue = mHost + QUERYARG + mAppID;
if (mLastFix != null && !isStale())
{
Log.d("locobird", "returning last fix");
return mLastFix;
}
//now test if called too quick
if (mLastCall != null && (new Date().getTime() - mLastCall.getTime() <= Locobird.SPURIOUS_FILTER))
{
//this implies we've been getting errors since we have no last fix but do have a last call
throw new CLPException("unable to provide location from CLP");
}
//else get a fix
Location l = null;
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost post = new HttpPost(CLP_ADDRESS);
HttpResponse response = null;
HttpEntity entity = null;
InputStream input = null;
//create a json request (basic)
String request = null;
try
{
request = new JSONStringer().object().key(HOST).value(hostValue).key(VERSION).value(VERSION_VAL).endObject().toString();
post.setEntity(new StringEntity(request));
JSONObject locationData = null;
StringBuffer data = new StringBuffer(512);
//send the request (POST it)
response = httpclient.execute(post);
entity = response.getEntity();
//get IO stream from entity and turn into JSON object
input = entity.getContent();
//look at response code
if (response.getStatusLine().getStatusCode() >= HttpURLConnection.HTTP_BAD_REQUEST)
{
//400 bad request, 404 not found, 500 server error ,etc
//do not try to parse json- will cause error
l = null;
}
else if (response.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_OK)
{
byte[] buffer = new byte[512];
int x = 0;
while ((x = input.read()) != -1)
{
data.append((char)x);
}
//create location from the response
Log.d("locobird", "CLP response= " + data.toString());
locationData = new JSONObject(data.toString());
//test for location object in the json (if you just query you will get an exception)
if (!locationData.has(LOCATION))
{
//get the error object and examine it for cause
//{"error":"server error"}
if (locationData.has(ERROR))
{
String errorCause = locationData.getString(ERROR);
//could return error message in a bundle or just throw an error
throw new CLPException(errorCause);
}
else
{
//neither location nor error so a clp problem
throw new CLPException("Missing location and error objects from CLP response");
}
}
else
{
JSONObject jsonLocation = locationData.getJSONObject(LOCATION);
//we have a good location object from the CLP
//load the location object with the locationData
l = new Location(WIMAX);
l.setLatitude(jsonLocation.getDouble(LATITUDE));
l.setLongitude(jsonLocation.getDouble(LONGITUDE));
l.setAccuracy((float) jsonLocation.getDouble(ACCURACY));
}
}
}
finally
{
//ensure call time is recorded
mLastCall = new Date();
try
{
if (entity != null)
{
entity.consumeContent();
input.close(); //may not be needed
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//can return null in case of error. if this happens, do not update the fix- keep old one.
if (l == null)
{
throw new CLPException("unable to provide location from CLP");
}
else
{
mLastFix = l;
mLastFix.setTime(new Date().getTime());
Log.d("locobird", "returning new fix");
return l;
}
}
//if time since last fix exceed STALE, the fix is stale so get a new one.
private boolean isStale()
{
return ((new Date().getTime() - mLastFix.getTime()) > STALE);
}
}


For checking if you are on 4G, here's the code.

public static boolean onWimax(ConnectivityManager cm)
{
NetworkInfo info = cm.getActiveNetworkInfo();
if (info == null)
{
//no active network=
return false;
}
int networkType = cm.getActiveNetworkInfo().getType();
//type = 6 means wimax

if((networkType == 6) && (cm.getActiveNetworkInfo().getDetailedState().compareTo(DetailedState.CONNECTED)) == 0)
{
return true;
}
else
{
return false;
}
}
You will need these imports:

import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;


Saturday, June 26, 2010

Dalvik ate my projects

I got too clever and wrote a script to build my Android apps. I added the android\tools directory to my system path to help with this.

The script worked fine but problems started when I opened Eclipse. I was patching a file and the build failed with these details:

Problems occurred building the selected resources.
Errors running builder 'Java Builder' on project 'xyz'.
java.lang.NullPointerException
These errors were in the Problems tab:
  • Conversion to Dalvik format failed with error 1
  • R cannot be resolved to a variable

All I had done was add the android tools directory to the system path Troubleshooting 101- even if you don't understand how your change might have caused a problem, undo the change anyway. I undid my path change.

When I tried to build my projects (yes all had the same errors), I still had the Dalvik error listed.
I deleted the error (yes you can! Errors can get stuck and sometimes have to be deleted) from the Problems tab and rebuilt. Then I got:
Could not find xyz.apk!
I manually exported an unsigned apk from the project. This finally fixed things. I had to do similar steps on all my projects.

Thursday, June 24, 2010

Preparing an Android app for publishing ($$)

Writing apps isn't so hard. Writing apps with high usability is another matter. Making the step from one to the other is critical, since you will have better ratings and hence make more money.

First, Wikipedia has a nice definition of usability and it forms a pretty good basis for my purpose. This is the quality of a product as seen by the end-user. I will address each point of the definition in the context of Android but you should be able to see how this applies to devices like the iPhone as well.
  1. Is the user interface intuitive (self-explanatory/self-documenting)? Keep it simple. Mobile devices are powerful, but are best at a limited range of functions per app. Please don't create 20 menu items and a UI that is some complex and detailed that you can't tell what can be clicked.
  2. Is it easy to perform simple operations? Again, keep it simple. The most used functions should not require going into a menu to make them work. If you followed rule one, you might not even need a menu, except for a help file.
  3. Is it feasible to perform complex operations? No. Never.
  4. Does the software give sensible error messages? Use Dialogs or Toast to give good user feedback (and not just for errors). Keep the users in a box so that many errors are precluded by the design of your app. When they do happen, tell the user what they can do about it (if anything). If they can't do anything about the error, keep the message simple: "Please try again later," is just fine. Don't use error codes- this is not Windows development.
  5. Do widgets behave as expected? Use standard widgets, unless you are really good. There is a reason for them. Don't make things hard to use on a touchscreen. Don't make the user guess how something works.
  6. Is the software well documented? Always create a Help or About file. A demo video on YouTube that your app can play is great. I just use a regular html page and load it into a WebView in a Dialog. This is simple and easy to make look good since you can use css. Just put the html and css in the assets directory of your project.
  7. Is the user interface responsive or too slow? Slow apps are bad. Whenever you are doing IO or complex functions, wrap the code in a separate thread so that you can avoid the Application Not Responding message. Web sites do not always respond quickly, different phones runs at different speeds, so help your user out. Eliminate the chance of an ANR and your users will think your app runs like a dream.

Enough motherhood and apple pie. On to some real lessons learned.

Have a kid test your app. they will try all kinds of things you won't and find bugs.

You are going to need to use try/catch/finally a lot, so just get used to it. And you are going to be catching errors you have never even heard of. You will also need to catch some runtime errors (those not declared as thrown, like NullPointerException) since you can't account for everything.

Pay attention to a new feature in the Android Market- errors. Your app listings now show you weekly error reports with stack traces from some phones. This is great! Thousands of people testing your code for you and google reporting the problems. They are not too hard to fix (you usually end up catching some weird error and saying try again).

You can get errors when the handset is rotated unless you prepare your app for working that way. Here's what I put in the Application Manifest to prevent these errors. Include this in your main activity element in the manifest:

android:configChanges="orientation|keyboardHidden"

Don't forget to stop listening for updates to sensors, location services, etc. when your app is paused (unless you need these things). It is simple to turn these back on in onResume.

You may need to catch this: android.view.WindowManager.BadTokenException
It can occur when launching a new activity that wants to put something on screen but you quickly hit 'back'. Basically, you can pull the rug out from under the new activity and cause this error. I have a video app that catches this.

Avoid the ANR by putting IO (and 'expensive' code) into an inner class that extends Thread and uses a Handler to provide data back to the main UI. Other than the main activity Thread, no thread can write data to the screen. You have to use a mechanism like a Handler.

Watch out for window leaks. The fix is to call dismiss() on any Dialog objects in onDestroy(). You are overriding key life cycle events in your Activity, right?

I'm even going so far as to try an work out a scheme to watch memory usage during onCreate to ensure an app can start successfully. On older phones with less memory this can be a problem when a user is running too many apps (and Android fails to shut them down).

That's all for now. Got any more tips to share?

Android logcat filtering for EVO

I'm developing apps for the EVO handset. It spews out more logs than I've ever seen, and it is driving me crazy. Below is a batch file you can use to filter out most of it so you can actually see the things that matter.

rem filter excess Android 2.1 logcat output
adb logcat HtcWimaxStatusBar:W WimaxReceiver:W WimaxStateTracker:W BluetoothHandsfree:W Wimax:F wimax:F UsbConnectedReceiver:W BatteryService:W WimaxInfo:F BatteryStatsImpl:W OpenSSLSessionImpl:W HtcLockScreen:W

Change it as you will.

Saturday, June 19, 2010

The dreaded Android ANR (force close)

Application Not Responding is a message I don't really like to see, unless it is early in development. It means you are doing something wrong and if you want to sell your app, you better fix it. The basic fix is to move your slow process/code into a Thread, but that is another topic.

I sell apps and my code has lots of extra error handling for strange cases that come up. In fact, the new Android Market feature that provides you error reports for your apps is handy. After you get a few thousand users of your apps you will see all kinds of odd things happen to your beautiful code. Mostly, you just have to catch some runtime errors you never heard of and handle them gracefully.

Today I will tell you how to get the detailed stack trace explaining exactly what caused the ANR. Every ANR puts a trace file on the phone and that is what you need.

How to get the trace
  1. connect the phone to your computer (you need the Android SDK and USB drivers for this)
  2. open a command window and cd to the android sdk tools directory
  3. run this command: adb pull /data/anr/traces.txt .

traces.txt is now in your tools directory. It is human readable so check it out.


Wednesday, June 16, 2010

EVO Video Camera Settings

The EVO has a real nice camera and a dual flash (finally). For video, it comes with Qik. I did some testing to figure out how ot make the best Qik videos. Here are the settings for the app when operating on the Sprint 3G network. For 4G results, see below.

low- front (and rear) camera
328 kbits per second
~30frames per second
320 x 240 (qvga)
MPEG-4 video with AMR NArrowband audio (mono, 8KHz)

normal- rear camera
355 kbits per second
~30frames per second
640 x 480 (vga)
MPEG-4 video with AMR NArrowband audio (mono, 8KHz)

high- rear camera
387 kbits per second
~27frames per second
800 x 480 (wvga)
MPEG-4 video with AMR NArrowband audio (mono, 8KHz)

So which looks best? Well, ironically the so-called low quality settings produce the best looking video by far. This is because these settings use nearly the same data rate but only record at qvga size. Simply put, you get more bytes of data per frame (since the frames are smaller) so the quality is higher. I confirmed that using the 'low' quality settings on the rear camera also produce the same good video. I would expect it to be slightly better looking since it uses a better lens, but the data rates are the same.

To really use the normal and high quality settings to get better quality, a much higher data rate is required for the larger video resolution. This shows a basic misunderstanding by Qik as to what affects video quality.

Luckily, the limits of Qik are not the limits of the phone hardware. I did a separate test using the Camcorder app. It recorded VGA video at 30 fps and at a data rate of 1.6 mbps. Now we are talking! The audio is still weak, though, since it uses AMR Narrowband even though the device can record raw 16-bit audio at 44.1 Khz (CD quality). Oh well. I can't upload this video to Qik since I didn't take it with Qik.

Now to test on 4G
Things are much better when on 4G. Qik has this part right- they sense the network you are on and adjust the quality accordingly.

high- rear camera
1 mega bit per second
24 frames per second
800 x 480 (wvga)
MPEG-4 video with AMR NArrowband audio (mono, 8KHz)

You can see this isn't as good as the Camcorder app can deliver, but is excellent for mobile video and truly game changing.

low- front (and rear) camera
1 mega bits per second
~30frames per second
320 x 240 (qvga)
MPEG-4 video with AMR NArrowband audio (mono, 8KHz)

Oddly enough, the Qik app also uses 1 mbps when on low quality for the front camera. This only captures at 320x240 making 1 mbps an absolute waste. All you get is a file 3 times the size with no discernible quality improvements. This really should be set to no more than 400-425 kbps and the lower rate when on 3G (of 325kbps) looks as just good.

Conclusions
With the EVO's front camera and high quality, video calls are going to be great on this and future 4G devices. This type of device, plus 4G will make video calling a meaningful and fun application. Qik has to work out a couple of things regarding how they manage data rate and indicate video quality settings, but their system and Android app is so much better thanks to devices that can really use it well.

You can see the test videos at http://www.qik.com/ski

The EVO files

I have an EVO and it is awesome. If you don't know, it is a new phone from HTC for the Sprint (and Clear 4G) networks. Since I do Android development, I've got some information to share for this device.

First, you need the HTC Sync update for EVO 4G. Without this, you cannot connect your phone to your computer for things like application installation, debugging, etc. A must for developers.
http://www.htc.com/us/support/evo-sprint/downloads/ When you connect, just choose "HTC Sync". That will let you run the adb tools you need and work with Eclipse (and the Android SDK) and of course, charge your phone.

Second, save your battery. There is already an EVO site and it has good tips: http://www.goodandevo.net/2010/05/20-tips-to-improve-htc-evo-4g-battery-life.html My battery life is now as good as other Android handsets I have. But a great phone needs power so be prepared to charge. You can also view what is using your battery from a screen in the phone: Settings > About Phone > Battery > Battery Use

Third, if you don't like having to unlock your home screen or enter passwords to unlock it, you need this widget: Screen Mode Widget Lite. This saves me from going nuts having to enter a password in every time I pick up my phone. You see, once you configure Exchange email (built in to EVO), the password lock is turned on and I found no way to turn off, except this app.

Fourth, there is a developer guide- sort of. It's incomplete but is being updated every 2-3 weeks so keep an eye on it.

Other tidbits
  • The browser appears to support HTML5 Geolocation as opposed to Google Gears.
  • The front camera only works in low resolution with Qik. Rear camera supports low, medium, and high, but exactly what they are is not known (yet!)
  • The phone is wicked fast, so when developing don't get carried away otherwise your apps won't work on older Android phones
  • You need to test if on a 4G network specially, since the Android code does not know what 4G is. See the Sprint guide.