am d2a0d233: Use consistent device-id even the device is wiped.

Merge commit 'd2a0d23380a2751d82f9d1f955a812f94a301e2a' into froyo-plus-aosp

* commit 'd2a0d23380a2751d82f9d1f955a812f94a301e2a':
  Use consistent device-id even the device is wiped.
This commit is contained in:
Makoto Onuki 2010-04-14 22:19:30 -07:00 committed by Android Git Automerger
commit ad383ff123
3 changed files with 97 additions and 9 deletions

View File

@ -32,8 +32,11 @@ import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.security.MessageDigest;
import android.telephony.TelephonyManager;
import android.text.Editable;
import android.util.Base64;
import android.util.Log;
import android.widget.TextView;
import java.io.IOException;
@ -43,6 +46,7 @@ import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
@ -527,4 +531,45 @@ public class Utility {
task.cancel(mayInterruptIfRunning);
}
}
/**
* @return Device's unique ID if available. null if the device has no unique ID.
*/
public static String getConsistentDeviceId(Context context) {
final String deviceId;
try {
TelephonyManager tm =
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (tm == null) {
return null;
}
deviceId = tm.getDeviceId();
if (deviceId == null) {
return null;
}
} catch (Exception e) {
Log.d(Email.LOG_TAG, "Error in TelephonyManager.getDeviceId(): " + e.getMessage());
return null;
}
final MessageDigest sha;
try {
sha = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException impossible) {
return null;
}
sha.update(Utility.toUtf8(deviceId));
final int hash = getSmallHashFromSha1(sha.digest());
return Integer.toString(hash);
}
/**
* @return a non-negative integer generated from 20 byte SHA-1 hash.
*/
/* package for testing */ static int getSmallHashFromSha1(byte[] sha1) {
final int offset = sha1[19] & 0xf; // SHA1 is 20 bytes.
return ((sha1[offset] & 0x7f) << 24)
| ((sha1[offset + 1] & 0xff) << 16)
| ((sha1[offset + 2] & 0xff) << 8)
| ((sha1[offset + 3] & 0xff));
}
}

View File

@ -20,6 +20,7 @@ package com.android.exchange;
import com.android.email.AccountBackupRestore;
import com.android.email.Email;
import com.android.email.SecurityPolicy;
import com.android.email.Utility;
import com.android.email.mail.MessagingException;
import com.android.email.mail.transport.SSLUtils;
import com.android.email.provider.EmailContent;
@ -968,16 +969,19 @@ public class SyncManager extends Service implements Runnable {
}
static public synchronized String getDeviceId(Context context) throws IOException {
SyncManager syncManager = INSTANCE;
if (sDeviceId != null) {
return sDeviceId;
} else if (syncManager == null && context == null) {
if (sDeviceId == null) {
sDeviceId = getDeviceIdInternal(context);
}
return sDeviceId;
}
static private String getDeviceIdInternal(Context context) throws IOException {
if (INSTANCE == null && context == null) {
throw new IOException("No context for getDeviceId");
} else if (context == null) {
context = syncManager;
context = INSTANCE;
}
// Otherwise, we'll read the id file or create one if it's not found
try {
File f = context.getFileStreamPath("deviceName");
BufferedReader rdr = null;
@ -986,14 +990,18 @@ public class SyncManager extends Service implements Runnable {
rdr = new BufferedReader(new FileReader(f), 128);
id = rdr.readLine();
rdr.close();
sDeviceId = id;
return id;
} else if (f.createNewFile()) {
BufferedWriter w = new BufferedWriter(new FileWriter(f), 128);
id = "android" + System.currentTimeMillis();
final String consistentDeviceId = Utility.getConsistentDeviceId(context);
if (consistentDeviceId != null) {
// Use different prefix from random IDs.
id = "androidc" + consistentDeviceId;
} else {
id = "android" + System.currentTimeMillis();
}
w.write(id);
w.close();
sDeviceId = id;
return id;
}
} catch (IOException e) {

View File

@ -20,9 +20,11 @@ import com.android.email.provider.EmailContent.Mailbox;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.telephony.TelephonyManager;
import android.test.AndroidTestCase;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import java.util.HashSet;
import java.util.Set;
@ -170,4 +172,37 @@ public class UtilityUnitTests extends AndroidTestCase {
assertEquals("\r\n\r\n\r\n", Utility.replaceBareLfWithCrlf("\n\n\n"));
assertEquals("A\r\nB\r\nC\r\nD", Utility.replaceBareLfWithCrlf("A\nB\r\nC\nD"));
}
public void testGetConsistentDeviceId() {
TelephonyManager tm =
(TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
if (tm == null) {
Log.w(Email.LOG_TAG, "TelephonyManager not supported. Skipping.");
return;
}
final String deviceId = Utility.getConsistentDeviceId(getContext());
assertNotNull(deviceId);
final String deviceId2 = Utility.getConsistentDeviceId(getContext());
// Should be consistent.
assertEquals(deviceId, deviceId2);
}
public void testGetSmallSha1() {
byte[] sha1 = new byte[20];
// White box test. Not so great, but to make sure it may detect careless mistakes...
assertEquals(0, Utility.getSmallHashFromSha1(sha1));
for (int i = 0; i < sha1.length; i++) {
sha1[i] = (byte) 0xFF;
}
assertEquals(Integer.MAX_VALUE, Utility.getSmallHashFromSha1(sha1));
// Boundary check
for (int i = 0; i < 16; i++) {
sha1[19] = (byte) i;
Utility.getSmallHashFromSha1(sha1);
}
}
}