Merge "Add inPreferQualityOverSpeed into BitmapFactory.Options."
diff --git a/Android.mk b/Android.mk
index 4e286dbb..07b76d8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -121,11 +121,11 @@
 	core/java/android/nfc/ILlcpConnectionlessSocket.aidl \
 	core/java/android/nfc/ILlcpServiceSocket.aidl \
 	core/java/android/nfc/ILlcpSocket.aidl \
-	core/java/android/nfc/INdefTag.aidl \
 	core/java/android/nfc/INfcAdapter.aidl \
 	core/java/android/nfc/INfcTag.aidl \
 	core/java/android/nfc/IP2pInitiator.aidl \
 	core/java/android/nfc/IP2pTarget.aidl \
+    core/java/android/nfc/INfcSecureElement.aidl \
 	core/java/android/os/IHardwareService.aidl \
 	core/java/android/os/IMessenger.aidl \
 	core/java/android/os/INetworkManagementService.aidl \
@@ -254,7 +254,6 @@
 	frameworks/base/core/java/android/nfc/NdefMessage.aidl \
 	frameworks/base/core/java/android/nfc/NdefRecord.aidl \
 	frameworks/base/core/java/android/nfc/Tag.aidl \
-	frameworks/base/core/java/android/nfc/NdefTag.aidl \
 	frameworks/base/core/java/android/os/Bundle.aidl \
 	frameworks/base/core/java/android/os/DropBoxManager.aidl \
 	frameworks/base/core/java/android/os/ParcelFileDescriptor.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index c7bd9fd..15e073e 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -86,6 +86,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/trustedlogic)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/com/trustedlogic)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/target/common/obj/APPS/Music2_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/nfc/INdefTag.java)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 07e506a..7ba5291c 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -31,7 +31,7 @@
 #include <media/IMediaPlayerService.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include "include/ARTSPController.h"
-#include "include/LiveSource.h"
+#include "include/LiveSession.h"
 #include "include/NuCachedSource2.h"
 #include <media/stagefright/AudioPlayer.h>
 #include <media/stagefright/DataSource.h>
@@ -560,6 +560,7 @@
 
     sp<ALooper> looper;
     sp<ARTSPController> rtspController;
+    sp<LiveSession> liveSession;
 
     int res;
     while ((res = getopt(argc, argv, "han:lm:b:ptsow:kx")) >= 0) {
@@ -852,8 +853,15 @@
                 String8 uri("http://");
                 uri.append(filename + 11);
 
-                dataSource = new LiveSource(uri.string());
-                dataSource = new NuCachedSource2(dataSource);
+                if (looper == NULL) {
+                    looper = new ALooper;
+                    looper->start();
+                }
+                liveSession = new LiveSession;
+                looper->registerHandler(liveSession);
+
+                liveSession->connect(uri.string());
+                dataSource = liveSession->getDataSource();
 
                 extractor =
                     MediaExtractor::Create(
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java
index d2b3bc7..524d3f4 100644
--- a/core/java/android/accounts/AccountAuthenticatorCache.java
+++ b/core/java/android/accounts/AccountAuthenticatorCache.java
@@ -38,7 +38,8 @@
  * @hide
  */
 /* package private */ class AccountAuthenticatorCache
-        extends RegisteredServicesCache<AuthenticatorDescription> {
+        extends RegisteredServicesCache<AuthenticatorDescription> 
+        implements IAccountAuthenticatorCache {
     private static final String TAG = "Account";
     private static final MySerializer sSerializer = new MySerializer();
 
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 8471df9..61ef8f2 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -16,6 +16,10 @@
 
 package android.accounts;
 
+import com.android.internal.R;
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.TelephonyIntents;
+
 import android.Manifest;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -57,16 +61,13 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
-import java.util.LinkedHashMap;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
-import com.android.internal.R;
-import com.android.internal.telephony.ITelephony;
-import com.android.internal.telephony.TelephonyIntents;
-
 /**
  * A system service that provides  account, password, and authtoken management for all
  * accounts on the device. Some of these calls are implemented with the help of the corresponding
@@ -91,13 +92,15 @@
 
     private final Context mContext;
 
+    private final PackageManager mPackageManager;
+
     private HandlerThread mMessageThread;
     private final MessageHandler mMessageHandler;
 
     // Messages that can be sent on mHandler
     private static final int MESSAGE_TIMED_OUT = 3;
 
-    private final AccountAuthenticatorCache mAuthenticatorCache;
+    private final IAccountAuthenticatorCache mAuthenticatorCache;
     private final DatabaseHelper mOpenHelper;
     private final SimWatcher mSimWatcher;
 
@@ -129,8 +132,6 @@
     private static final String META_KEY = "key";
     private static final String META_VALUE = "value";
 
-    private static final String[] ACCOUNT_NAME_TYPE_PROJECTION =
-            new String[]{ACCOUNTS_ID, ACCOUNTS_NAME, ACCOUNTS_TYPE};
     private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
             new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
     private static final Intent ACCOUNTS_CHANGED_INTENT;
@@ -143,6 +144,15 @@
             + " AND " + ACCOUNTS_NAME + "=?"
             + " AND " + ACCOUNTS_TYPE + "=?";
 
+    private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
+            AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
+    private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
+            AUTHTOKENS_AUTHTOKEN};
+
+    private static final String SELECTION_USERDATA_BY_ACCOUNT =
+            EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
+    private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
+
     private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
     private final AtomicInteger mNotificationIds = new AtomicInteger(1);
 
@@ -163,6 +173,16 @@
         ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
     }
 
+    private final Object mCacheLock = new Object();
+    /** protected by the {@link #mCacheLock} */
+    private final HashMap<String, Account[]> mAccountCache = new HashMap<String, Account[]>();
+    /** protected by the {@link #mCacheLock} */
+    private HashMap<Account, HashMap<String, String>> mUserDataCache =
+            new HashMap<Account, HashMap<String, String>>();
+    /** protected by the {@link #mCacheLock} */
+    private HashMap<Account, HashMap<String, String>> mAuthTokenCache =
+            new HashMap<Account, HashMap<String, String>>();
+
     /**
      * This should only be called by system code. One should only call this after the service
      * has started.
@@ -173,47 +193,14 @@
         return sThis.get();
     }
 
-    public class AuthTokenKey {
-        public final Account mAccount;
-        public final String mAuthTokenType;
-        private final int mHashCode;
-
-        public AuthTokenKey(Account account, String authTokenType) {
-            mAccount = account;
-            mAuthTokenType = authTokenType;
-            mHashCode = computeHashCode();
-        }
-
-        public boolean equals(Object o) {
-            if (o == this) {
-                return true;
-            }
-            if (!(o instanceof AuthTokenKey)) {
-                return false;
-            }
-            AuthTokenKey other = (AuthTokenKey)o;
-            if (!mAccount.equals(other.mAccount)) {
-                return false;
-            }
-            return (mAuthTokenType == null)
-                    ? other.mAuthTokenType == null
-                    : mAuthTokenType.equals(other.mAuthTokenType);
-        }
-
-        private int computeHashCode() {
-            int result = 17;
-            result = 31 * result + mAccount.hashCode();
-            result = 31 * result + ((mAuthTokenType == null) ? 0 : mAuthTokenType.hashCode());
-            return result;
-        }
-
-        public int hashCode() {
-            return mHashCode;
-        }
+    public AccountManagerService(Context context) {
+        this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
     }
 
-    public AccountManagerService(Context context) {
+    public AccountManagerService(Context context, PackageManager packageManager,
+            IAccountAuthenticatorCache authenticatorCache) {
         mContext = context;
+        mPackageManager = packageManager;
 
         mOpenHelper = new DatabaseHelper(mContext);
 
@@ -221,32 +208,58 @@
         mMessageThread.start();
         mMessageHandler = new MessageHandler(mMessageThread.getLooper());
 
-        mAuthenticatorCache = new AccountAuthenticatorCache(mContext);
+        mAuthenticatorCache = authenticatorCache;
         mAuthenticatorCache.setListener(this, null /* Handler */);
 
         mSimWatcher = new SimWatcher(mContext);
         sThis.set(this);
 
-        validateAccounts();
+        validateAccountsAndPopulateCache();
     }
 
-    private void validateAccounts() {
+    private void validateAccountsAndPopulateCache() {
         boolean accountDeleted = false;
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         Cursor cursor = db.query(TABLE_ACCOUNTS,
                 new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
                 null, null, null, null, null);
         try {
-            while (cursor.moveToNext()) {
-                final long accountId = cursor.getLong(0);
-                final String accountType = cursor.getString(1);
-                final String accountName = cursor.getString(2);
-                if (mAuthenticatorCache.getServiceInfo(AuthenticatorDescription.newKey(accountType))
-                        == null) {
-                    Log.d(TAG, "deleting account " + accountName + " because type "
-                            + accountType + " no longer has a registered authenticator");
-                    db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
-                    accountDeleted = true;
+            synchronized (mCacheLock) {
+                mAccountCache.clear();
+                final HashMap<String, ArrayList<String>> accountNamesByType =
+                        new HashMap<String, ArrayList<String>>();
+                while (cursor.moveToNext()) {
+                    final long accountId = cursor.getLong(0);
+                    final String accountType = cursor.getString(1);
+                    final String accountName = cursor.getString(2);
+                    if (mAuthenticatorCache.getServiceInfo(
+                            AuthenticatorDescription.newKey(accountType)) == null) {
+                        Log.d(TAG, "deleting account " + accountName + " because type "
+                                + accountType + " no longer has a registered authenticator");
+                        db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
+                        accountDeleted = true;
+                        final Account account = new Account(accountName, accountType);
+                        mUserDataCache.remove(account);
+                        mAuthTokenCache.remove(account);
+                    } else {
+                        ArrayList<String> accountNames = accountNamesByType.get(accountType);
+                        if (accountNames == null) {
+                            accountNames = new ArrayList<String>();
+                            accountNamesByType.put(accountType, accountNames);
+                        }
+                        accountNames.add(accountName);
+                    }
+                }
+                for (HashMap.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
+                    final String accountType = cur.getKey();
+                    final ArrayList<String> accountNames = cur.getValue();
+                    final Account[] accountsForType = new Account[accountNames.size()];
+                    int i = 0;
+                    for (String accountName : accountNames) {
+                        accountsForType[i] = new Account(accountName, accountType);
+                        ++i;
+                    }
+                    mAccountCache.put(accountType, accountsForType);
                 }
             }
         } finally {
@@ -258,30 +271,15 @@
     }
 
     public void onServiceChanged(AuthenticatorDescription desc, boolean removed) {
-        boolean accountDeleted = false;
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        Cursor cursor = db.query(TABLE_ACCOUNTS,
-                new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
-                ACCOUNTS_TYPE + "=?", new String[]{desc.type}, null, null, null);
-        try {
-            while (cursor.moveToNext()) {
-                final long accountId = cursor.getLong(0);
-                final String accountType = cursor.getString(1);
-                final String accountName = cursor.getString(2);
-                Log.d(TAG, "deleting account " + accountName + " because type "
-                        + accountType + " no longer has a registered authenticator");
-                db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
-                accountDeleted = true;
-            }
-        } finally {
-            cursor.close();
-            if (accountDeleted) {
-                sendAccountsChangedBroadcast();
-            }
-        }
+        validateAccountsAndPopulateCache();
     }
 
     public String getPassword(Account account) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "getPassword: " + account
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (account == null) throw new IllegalArgumentException("account is null");
         checkAuthenticateAccountsPermission(account);
 
@@ -313,39 +311,29 @@
     }
 
     public String getUserData(Account account, String key) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "getUserData: " + account
+                    + ", key " + key
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (account == null) throw new IllegalArgumentException("account is null");
         if (key == null) throw new IllegalArgumentException("key is null");
         checkAuthenticateAccountsPermission(account);
         long identityToken = clearCallingIdentity();
         try {
-            return readUserDataFromDatabase(account, key);
+            return readUserDataFromCache(account, key);
         } finally {
             restoreCallingIdentity(identityToken);
         }
     }
 
-    private String readUserDataFromDatabase(Account account, String key) {
-        if (account == null) {
-            return null;
-        }
-
-        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
-        Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_VALUE},
-                EXTRAS_ACCOUNTS_ID
-                        + "=(select _id FROM accounts WHERE name=? AND type=?) AND "
-                        + EXTRAS_KEY + "=?",
-                new String[]{account.name, account.type, key}, null, null, null);
-        try {
-            if (cursor.moveToNext()) {
-                return cursor.getString(0);
-            }
-            return null;
-        } finally {
-            cursor.close();
-        }
-    }
-
     public AuthenticatorDescription[] getAuthenticatorTypes() {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "getAuthenticatorTypes: "
+                    + "caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         long identityToken = clearCallingIdentity();
         try {
             Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
@@ -364,27 +352,12 @@
         }
     }
 
-    public Account[] getAccountsByType(String accountType) {
-        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
-
-        final String selection = accountType == null ? null : (ACCOUNTS_TYPE + "=?");
-        final String[] selectionArgs = accountType == null ? null : new String[]{accountType};
-        Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_NAME_TYPE_PROJECTION,
-                selection, selectionArgs, null, null, null);
-        try {
-            int i = 0;
-            Account[] accounts = new Account[cursor.getCount()];
-            while (cursor.moveToNext()) {
-                accounts[i] = new Account(cursor.getString(1), cursor.getString(2));
-                i++;
-            }
-            return accounts;
-        } finally {
-            cursor.close();
-        }
-    }
-
     public boolean addAccount(Account account, String password, Bundle extras) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "addAccount: " + account
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (account == null) throw new IllegalArgumentException("account is null");
         checkAuthenticateAccountsPermission(account);
 
@@ -417,6 +390,8 @@
                             + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
                     new String[]{account.name, account.type});
             if (numMatches > 0) {
+                Log.w(TAG, "insertAccountIntoDatabase: " + account
+                        + ", skipping since the account already exists");
                 return false;
             }
             ContentValues values = new ContentValues();
@@ -425,17 +400,22 @@
             values.put(ACCOUNTS_PASSWORD, password);
             long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
             if (accountId < 0) {
+                Log.w(TAG, "insertAccountIntoDatabase: " + account
+                        + ", skipping the DB insert failed");
                 return false;
             }
             if (extras != null) {
                 for (String key : extras.keySet()) {
                     final String value = extras.getString(key);
                     if (insertExtra(db, accountId, key, value) < 0) {
+                        Log.w(TAG, "insertAccountIntoDatabase: " + account
+                                + ", skipping since insertExtra failed for key " + key);
                         return false;
                     }
                 }
             }
             db.setTransactionSuccessful();
+            insertAccountIntoCache(account);
         } finally {
             db.endTransaction();
         }
@@ -455,6 +435,13 @@
 
     public void hasFeatures(IAccountManagerResponse response,
             Account account, String[] features) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "hasFeatures: " + account
+                    + ", response " + response
+                    + ", features " + stringArrayToString(features)
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
         if (features == null) throw new IllegalArgumentException("features is null");
@@ -495,6 +482,10 @@
                         onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
                         return;
                     }
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
+                                + response);
+                    }
                     final Bundle newResult = new Bundle();
                     newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
                             result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
@@ -516,6 +507,12 @@
     }
 
     public void removeAccount(IAccountManagerResponse response, Account account) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "removeAccount: " + account
+                    + ", response " + response
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
         checkManageAccountsPermission();
@@ -565,6 +562,10 @@
                 }
                 IAccountManagerResponse response = getResponseAndClose();
                 if (response != null) {
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
+                                + response);
+                    }
                     Bundle result2 = new Bundle();
                     result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
                     try {
@@ -578,14 +579,20 @@
         }
     }
 
-    private void removeAccount(Account account) {
+    protected void removeAccount(Account account) {
         final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
                 new String[]{account.name, account.type});
+        removeAccountFromCache(account);
         sendAccountsChangedBroadcast();
     }
 
     public void invalidateAuthToken(String accountType, String authToken) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "invalidateAuthToken: accountType " + accountType
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
         if (authToken == null) throw new IllegalArgumentException("authToken is null");
         checkManageAccountsOrUseCredentialsPermissions();
@@ -625,6 +632,7 @@
                 String accountName = cursor.getString(1);
                 String authTokenType = cursor.getString(2);
                 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
+                writeAuthTokenIntoCache(new Account(accountName, accountType), authTokenType, null);
             }
         } finally {
             cursor.close();
@@ -652,6 +660,7 @@
             values.put(AUTHTOKENS_AUTHTOKEN, authToken);
             if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
                 db.setTransactionSuccessful();
+                writeAuthTokenIntoCache(account, type, authToken);
                 return true;
             }
             return false;
@@ -660,39 +669,31 @@
         }
     }
 
-    public String readAuthTokenFromDatabase(Account account, String authTokenType) {
-        if (account == null || authTokenType == null) {
-            return null;
-        }
-        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
-        Cursor cursor = db.query(TABLE_AUTHTOKENS, new String[]{AUTHTOKENS_AUTHTOKEN},
-                AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?) AND "
-                        + AUTHTOKENS_TYPE + "=?",
-                new String[]{account.name, account.type, authTokenType},
-                null, null, null);
-        try {
-            if (cursor.moveToNext()) {
-                return cursor.getString(0);
-            }
-            return null;
-        } finally {
-            cursor.close();
-        }
-    }
-
     public String peekAuthToken(Account account, String authTokenType) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "peekAuthToken: " + account
+                    + ", authTokenType " + authTokenType
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (account == null) throw new IllegalArgumentException("account is null");
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
         checkAuthenticateAccountsPermission(account);
         long identityToken = clearCallingIdentity();
         try {
-            return readAuthTokenFromDatabase(account, authTokenType);
+            return readAuthTokenFromCache(account, authTokenType);
         } finally {
             restoreCallingIdentity(identityToken);
         }
     }
 
     public void setAuthToken(Account account, String authTokenType, String authToken) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "setAuthToken: " + account
+                    + ", authTokenType " + authTokenType
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (account == null) throw new IllegalArgumentException("account is null");
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
         checkAuthenticateAccountsPermission(account);
@@ -705,6 +706,11 @@
     }
 
     public void setPassword(Account account, String password) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "setAuthToken: " + account
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (account == null) throw new IllegalArgumentException("account is null");
         checkAuthenticateAccountsPermission(account);
         long identityToken = clearCallingIdentity();
@@ -738,10 +744,17 @@
     }
 
     private void sendAccountsChangedBroadcast() {
+        Log.i(TAG, "the accounts changed, sending broadcast of "
+                + ACCOUNTS_CHANGED_INTENT.getAction());
         mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
     }
 
     public void clearPassword(Account account) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "clearPassword: " + account
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (account == null) throw new IllegalArgumentException("account is null");
         checkManageAccountsPermission();
         long identityToken = clearCallingIdentity();
@@ -753,13 +766,16 @@
     }
 
     public void setUserData(Account account, String key, String value) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "setUserData: " + account
+                    + ", key " + key
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (key == null) throw new IllegalArgumentException("key is null");
         if (account == null) throw new IllegalArgumentException("account is null");
         checkAuthenticateAccountsPermission(account);
         long identityToken = clearCallingIdentity();
-        if (account == null) {
-            return;
-        }
         if (account.type.equals(GOOGLE_ACCOUNT_TYPE) && key.equals("broadcast")) {
             sendAccountsChangedBroadcast();
             return;
@@ -797,12 +813,20 @@
 
             }
             db.setTransactionSuccessful();
+            writeUserDataIntoCache(account, key, value);
         } finally {
             db.endTransaction();
         }
     }
 
     private void onResult(IAccountManagerResponse response, Bundle result) {
+        if (result == null) {
+            Log.e(TAG, "the result is unexpectedly null", new Exception());
+        }
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
+                    + response);
+        }
         try {
             response.onResult(result);
         } catch (RemoteException e) {
@@ -817,6 +841,15 @@
     public void getAuthToken(IAccountManagerResponse response, final Account account,
             final String authTokenType, final boolean notifyOnAuthFailure,
             final boolean expectActivityLaunch, final Bundle loginOptions) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "getAuthToken: " + account
+                    + ", response " + response
+                    + ", authTokenType " + authTokenType
+                    + ", notifyOnAuthFailure " + notifyOnAuthFailure
+                    + ", expectActivityLaunch " + expectActivityLaunch
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
@@ -829,7 +862,7 @@
             // if the caller has permission, do the peek. otherwise go the more expensive
             // route of starting a Session
             if (permissionGranted) {
-                String authToken = readAuthTokenFromDatabase(account, authTokenType);
+                String authToken = readAuthTokenFromCache(account, authTokenType);
                 if (authToken != null) {
                     Bundle result = new Bundle();
                     result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
@@ -920,8 +953,7 @@
         n.setLatestEventInfo(mContext,
                 title, subtitle,
                 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
-        ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
-                .notify(getCredentialPermissionNotificationId(account, authTokenType, uid), n);
+        installNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), n);
     }
 
     private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
@@ -952,7 +984,7 @@
         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT_TYPE_LABEL,
                         authContext.getString(serviceInfo.type.labelId));
         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_PACKAGES,
-                        mContext.getPackageManager().getPackagesForUid(uid));
+                        mPackageManager.getPackagesForUid(uid));
         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
         return intent;
     }
@@ -985,10 +1017,18 @@
         return id;
     }
 
-
     public void addAcount(final IAccountManagerResponse response, final String accountType,
             final String authTokenType, final String[] requiredFeatures,
             final boolean expectActivityLaunch, final Bundle options) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "addAccount: accountType " + accountType
+                    + ", response " + response
+                    + ", authTokenType " + authTokenType
+                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
+                    + ", expectActivityLaunch " + expectActivityLaunch
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
         checkManageAccountsPermission();
@@ -1017,6 +1057,13 @@
 
     public void confirmCredentials(IAccountManagerResponse response,
             final Account account, final Bundle options, final boolean expectActivityLaunch) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "confirmCredentials: " + account
+                    + ", response " + response
+                    + ", expectActivityLaunch " + expectActivityLaunch
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
         checkManageAccountsPermission();
@@ -1040,6 +1087,14 @@
     public void updateCredentials(IAccountManagerResponse response, final Account account,
             final String authTokenType, final boolean expectActivityLaunch,
             final Bundle loginOptions) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "updateCredentials: " + account
+                    + ", response " + response
+                    + ", authTokenType " + authTokenType
+                    + ", expectActivityLaunch " + expectActivityLaunch
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
@@ -1066,6 +1121,13 @@
 
     public void editProperties(IAccountManagerResponse response, final String accountType,
             final boolean expectActivityLaunch) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "editProperties: accountType " + accountType
+                    + ", response " + response
+                    + ", expectActivityLaunch " + expectActivityLaunch
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
         checkManageAccountsPermission();
@@ -1100,7 +1162,7 @@
         }
 
         public void run() throws RemoteException {
-            mAccountsOfType = getAccountsByType(mAccountType);
+            mAccountsOfType = getAccountsByTypeFromCache(mAccountType);
             // check whether each account matches the requested features
             mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
             mCurrentAccount = 0;
@@ -1154,6 +1216,10 @@
                     for (int i = 0; i < accounts.length; i++) {
                         accounts[i] = mAccountsWithFeatures.get(i);
                     }
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
+                                + response);
+                    }
                     Bundle result = new Bundle();
                     result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
                     response.onResult(result);
@@ -1174,10 +1240,15 @@
     }
 
     public Account[] getAccounts(String type) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "getAccounts: accountType " + type
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         checkReadAccountsPermission();
         long identityToken = clearCallingIdentity();
         try {
-            return getAccountsByType(type);
+            return getAccountsByTypeFromCache(type);
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -1185,23 +1256,20 @@
 
     public void getAccountsByFeatures(IAccountManagerResponse response,
             String type, String[] features) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "getAccounts: accountType " + type
+                    + ", response " + response
+                    + ", features " + stringArrayToString(features)
+                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", pid " + Binder.getCallingPid());
+        }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (type == null) throw new IllegalArgumentException("accountType is null");
         checkReadAccountsPermission();
-        if (features != null && type == null) {
-            if (response != null) {
-                try {
-                    response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "type is null");
-                } catch (RemoteException e) {
-                    // ignore this
-                }
-            }
-            return;
-        }
         long identityToken = clearCallingIdentity();
         try {
             if (features == null || features.length == 0) {
-                Account[] accounts = getAccountsByType(type);
+                Account[] accounts = getAccountsByTypeFromCache(type);
                 Bundle result = new Bundle();
                 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
                 onResult(response, result);
@@ -1397,12 +1465,20 @@
             if (response != null) {
                 try {
                     if (result == null) {
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, getClass().getSimpleName()
+                                    + " calling onError() on response " + response);
+                        }
                         response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
                                 "null bundle returned");
                     } else {
                         if (mStripAuthTokenFromResult) {
                             result.remove(AccountManager.KEY_AUTHTOKEN);
                         }
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, getClass().getSimpleName()
+                                    + " calling onResult() on response " + response);
+                        }
                         response.onResult(result);
                     }
                 } catch (RemoteException e) {
@@ -1420,13 +1496,11 @@
 
         public void onError(int errorCode, String errorMessage) {
             mNumErrors++;
-            if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Log.v(TAG, "Session.onError: " + errorCode + ", " + errorMessage);
-            }
             IAccountManagerResponse response = getResponseAndClose();
             if (response != null) {
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "Session.onError: responding");
+                    Log.v(TAG, getClass().getSimpleName()
+                            + " calling onError() on response " + response);
                 }
                 try {
                     response.onError(errorCode, errorMessage);
@@ -1744,7 +1818,7 @@
                 }
             }
         } else {
-            Account[] accounts = getAccountsByType(null /* type */);
+            Account[] accounts = getAccountsByTypeFromCache(null /* type */);
             fout.println("Accounts: " + accounts.length);
             for (Account account : accounts) {
                 fout.println("  " + account);
@@ -1786,15 +1860,19 @@
                         String.format(notificationTitleFormat, account.name),
                         message, PendingIntent.getActivity(
                         mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
-                ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
-                        .notify(notificationId, n);
+                installNotification(notificationId, n);
             }
         } finally {
             restoreCallingIdentity(identityToken);
         }
     }
 
-    private void cancelNotification(int id) {
+    protected void installNotification(final int notificationId, final Notification n) {
+        ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
+                .notify(notificationId, n);
+    }
+
+    protected void cancelNotification(int id) {
         long identityToken = clearCallingIdentity();
         try {
             ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
@@ -1811,24 +1889,24 @@
         for (String perm : permissions) {
             if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "caller uid " + uid + " has " + perm);
+                    Log.v(TAG, "  caller uid " + uid + " has " + perm);
                 }
                 return;
             }
         }
 
         String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions);
-        Log.w(TAG, msg);
+        Log.w(TAG, "  " + msg);
         throw new SecurityException(msg);
     }
 
     private boolean inSystemImage(int callerUid) {
-        String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
+        String[] packages = mPackageManager.getPackagesForUid(callerUid);
         for (String name : packages) {
             try {
-                PackageInfo packageInfo =
-                        mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
-                if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
+                if (packageInfo != null
+                        && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                     return true;
                 }
             } catch (PackageManager.NameNotFoundException e) {
@@ -1846,7 +1924,7 @@
                 && hasExplicitlyGrantedPermission(account, authTokenType);
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
-                    + callerUid + ", account " + account
+                    + callerUid + ", " + account
                     + ": is authenticator? " + fromAuthenticator
                     + ", has explicit permission? " + hasExplicitGrants);
         }
@@ -1858,7 +1936,7 @@
                 mAuthenticatorCache.getAllServices()) {
             if (serviceInfo.type.type.equals(accountType)) {
                 return (serviceInfo.uid == callingUid) ||
-                        (mContext.getPackageManager().checkSignatures(serviceInfo.uid, callingUid)
+                        (mPackageManager.checkSignatures(serviceInfo.uid, callingUid)
                                 == PackageManager.SIGNATURE_MATCH);
             }
         }
@@ -1975,4 +2053,170 @@
         }
         cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid));
     }
+
+    static final private String stringArrayToString(String[] value) {
+        return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
+    }
+
+    private void removeAccountFromCache(Account account) {
+        synchronized (mCacheLock) {
+            final Account[] oldAccountsForType = mAccountCache.get(account.type);
+            if (oldAccountsForType != null) {
+                ArrayList<Account> newAccountsList = new ArrayList<Account>();
+                for (Account curAccount : oldAccountsForType) {
+                    if (!curAccount.equals(account)) {
+                        newAccountsList.add(curAccount);
+                    }
+                }
+                if (newAccountsList.isEmpty()) {
+                    mAccountCache.remove(account.type);
+                } else {
+                    Account[] newAccountsForType = new Account[newAccountsList.size()];
+                    newAccountsForType = newAccountsList.toArray(newAccountsForType);
+                    mAccountCache.put(account.type, newAccountsForType);
+                }
+            }
+            mUserDataCache.remove(account);
+            mAuthTokenCache.remove(account);
+        }
+    }
+
+    /**
+     * This assumes that the caller has already checked that the account is not already present.
+     */
+    private void insertAccountIntoCache(Account account) {
+        synchronized (mCacheLock) {
+            Account[] accountsForType = mAccountCache.get(account.type);
+            int oldLength = (accountsForType != null) ? accountsForType.length : 0;
+            Account[] newAccountsForType = new Account[oldLength + 1];
+            if (accountsForType != null) {
+                System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
+            }
+            newAccountsForType[oldLength] = account;
+            mAccountCache.put(account.type, newAccountsForType);
+        }
+    }
+
+    protected Account[] getAccountsByTypeFromCache(String accountType) {
+        synchronized (mCacheLock) {
+            if (accountType != null) {
+                final Account[] accounts = mAccountCache.get(accountType);
+                if (accounts == null) {
+                    return EMPTY_ACCOUNT_ARRAY;
+                } else {
+                    return Arrays.copyOf(accounts, accounts.length);
+                }
+            } else {
+                int totalLength = 0;
+                for (Account[] accounts : mAccountCache.values()) {
+                    totalLength += accounts.length;
+                }
+                if (totalLength == 0) {
+                    return EMPTY_ACCOUNT_ARRAY;
+                }
+                Account[] accounts = new Account[totalLength];
+                totalLength = 0;
+                for (Account[] accountsOfType : mAccountCache.values()) {
+                    System.arraycopy(accountsOfType, 0, accounts, totalLength,
+                            accountsOfType.length);
+                    totalLength += accountsOfType.length;
+                }
+                return accounts;
+            }
+        }
+    }
+
+    protected void writeUserDataIntoCache(Account account, String key, String value) {
+        synchronized (mCacheLock) {
+            HashMap<String, String> userDataForAccount = mUserDataCache.get(account);
+            if (userDataForAccount == null) {
+                userDataForAccount = readUserDataForAccountFromDatabase(account);
+                mUserDataCache.put(account, userDataForAccount);
+            }
+            if (value == null) {
+                userDataForAccount.remove(key);
+            } else {
+                userDataForAccount.put(key, value);
+            }
+        }
+    }
+
+    protected void writeAuthTokenIntoCache(Account account, String key, String value) {
+        synchronized (mCacheLock) {
+            HashMap<String, String> authTokensForAccount = mAuthTokenCache.get(account);
+            if (authTokensForAccount == null) {
+                authTokensForAccount = readAuthTokensForAccountFromDatabase(account);
+                mAuthTokenCache.put(account, authTokensForAccount);
+            }
+            if (value == null) {
+                authTokensForAccount.remove(key);
+            } else {
+                authTokensForAccount.put(key, value);
+            }
+        }
+    }
+
+    protected String readAuthTokenFromCache(Account account, String authTokenType) {
+        synchronized (mCacheLock) {
+            HashMap<String, String> authTokensForAccount = mAuthTokenCache.get(account);
+            if (authTokensForAccount == null) {
+                // need to populate the cache for this account
+                authTokensForAccount = readAuthTokensForAccountFromDatabase(account);
+                mAuthTokenCache.put(account, authTokensForAccount);
+            }
+            return authTokensForAccount.get(authTokenType);
+        }
+    }
+
+    protected String readUserDataFromCache(Account account, String key) {
+        synchronized (mCacheLock) {
+            HashMap<String, String> userDataForAccount = mUserDataCache.get(account);
+            if (userDataForAccount == null) {
+                // need to populate the cache for this account
+                userDataForAccount = readUserDataForAccountFromDatabase(account);
+                mUserDataCache.put(account, userDataForAccount);
+            }
+            return userDataForAccount.get(key);
+        }
+    }
+
+    protected HashMap<String, String> readUserDataForAccountFromDatabase(Account account) {
+        HashMap<String, String> userDataForAccount = new HashMap<String, String>();
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        Cursor cursor = db.query(TABLE_EXTRAS,
+                COLUMNS_EXTRAS_KEY_AND_VALUE,
+                SELECTION_USERDATA_BY_ACCOUNT,
+                new String[]{account.name, account.type},
+                null, null, null);
+        try {
+            while (cursor.moveToNext()) {
+                final String tmpkey = cursor.getString(0);
+                final String value = cursor.getString(1);
+                userDataForAccount.put(tmpkey, value);
+            }
+        } finally {
+            cursor.close();
+        }
+        return userDataForAccount;
+    }
+
+    protected HashMap<String, String> readAuthTokensForAccountFromDatabase(Account account) {
+        HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        Cursor cursor = db.query(TABLE_AUTHTOKENS,
+                COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
+                SELECTION_AUTHTOKENS_BY_ACCOUNT,
+                new String[]{account.name, account.type},
+                null, null, null);
+        try {
+            while (cursor.moveToNext()) {
+                final String type = cursor.getString(0);
+                final String authToken = cursor.getString(1);
+                authTokensForAccount.put(type, authToken);
+            }
+        } finally {
+            cursor.close();
+        }
+        return authTokensForAccount;
+    }
 }
diff --git a/core/java/android/accounts/IAccountAuthenticatorCache.java b/core/java/android/accounts/IAccountAuthenticatorCache.java
new file mode 100644
index 0000000..618771f
--- /dev/null
+++ b/core/java/android/accounts/IAccountAuthenticatorCache.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.content.pm.RegisteredServicesCache;
+import android.content.pm.RegisteredServicesCacheListener;
+import android.os.Handler;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collection;
+
+/**
+ * An interface to the Authenticator specialization of RegisteredServicesCache. The use of
+ * this interface by the AccountManagerService makes it easier to unit test it.
+ * @hide
+ */
+public interface IAccountAuthenticatorCache {
+    /**
+     * Accessor for the {@link android.content.pm.RegisteredServicesCache.ServiceInfo} that
+     * matched the specified {@link android.accounts.AuthenticatorDescription} or null
+     * if none match.
+     * @param type the authenticator type to return
+     * @return the {@link android.content.pm.RegisteredServicesCache.ServiceInfo} that
+     * matches the account type or null if none is present
+     */
+    RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> getServiceInfo(
+            AuthenticatorDescription type);
+
+    /**
+     * @return A copy of a Collection of all the current Authenticators.
+     */
+    Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> getAllServices();
+
+    /**
+     * Dumps the state of the cache. See
+     * {@link android.os.Binder#dump(java.io.FileDescriptor, java.io.PrintWriter, String[])}
+     */
+    void dump(FileDescriptor fd, PrintWriter fout, String[] args);
+
+    /**
+     * Sets a listener that will be notified whenever the authenticator set changes
+     * @param listener the listener to notify, or null
+     * @param handler the {@link Handler} on which the notification will be posted. If null
+     * the notification will be posted on the main thread.
+     */
+    void setListener(RegisteredServicesCacheListener<AuthenticatorDescription> listener,
+            Handler handler);
+}
\ No newline at end of file
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f5a46c5..a196792 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -16,25 +16,19 @@
 
 package android.app;
 
-import java.text.NumberFormat;
-import java.util.Date;
+import com.android.internal.R;
 
-import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
-import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
-import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.util.Slog;
 import android.view.View;
 import android.widget.RemoteViews;
 
-import com.android.internal.R;
+import java.text.NumberFormat;
 
 /**
  * A class that represents how a persistent notification is to be presented to
@@ -46,8 +40,6 @@
  */
 public class Notification implements Parcelable
 {
-    private static final String TAG = "Notification";
-
     /**
      * Use all default values (where applicable).
      */
@@ -402,6 +394,7 @@
         }
     }
 
+    @Override
     public Notification clone() {
         Notification that = new Notification();
 
@@ -645,7 +638,10 @@
 
         public Builder(Context context) {
             mContext = context;
+
+            // Set defaults to match the defaults of a Notification
             mWhen = System.currentTimeMillis();
+            mAudioStreamType = STREAM_DEFAULT;
         }
 
         public Builder setWhen(long when) {
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index fa7f794..97e6931 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -61,10 +61,16 @@
 
     /**
      * Flag for {@link #disable} to hide only the navigation buttons.  Don't use this
-     * unless you're the setup wizard.
+     * unless you're a special part of the system UI (i.e., setup wizard, keyguard).
      */
     public static final int DISABLE_NAVIGATION = 0x00000020;
 
+    /**
+     * Flag for {@link #disable} to hide only the clock.  You might use this if your activity has
+     * its own clock making the status bar's clock redundant.
+     */
+    public static final int DISABLE_CLOCK = 0x00000040;
+
 
     /**
      * Re-enable all of the status bar features that you've disabled.
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 06095d1..b1fc788 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -182,7 +182,8 @@
         public final ComponentName componentName;
         public final int uid;
 
-        private ServiceInfo(V type, ComponentName componentName, int uid) {
+        /** @hide */
+        public ServiceInfo(V type, ComponentName componentName, int uid) {
             this.type = type;
             this.componentName = componentName;
             this.uid = uid;
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index cd08e33..a663fb8 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -24,6 +24,7 @@
 import android.nfc.INfcTag;
 import android.nfc.IP2pTarget;
 import android.nfc.IP2pInitiator;
+import android.nfc.INfcSecureElement;
 
 /**
  * @hide
@@ -36,9 +37,12 @@
     INfcTag getNfcTagInterface();
     IP2pTarget getP2pTargetInterface();
     IP2pInitiator getP2pInitiatorInterface();
+    INfcSecureElement getNfcSecureElementInterface();
 
     // NfcAdapter-class related methods
     boolean isEnabled();
+    NdefMessage localGet();
+    void localSet(in NdefMessage message);
     void openTagConnection(in Tag tag);
 
     // Non-public methods
diff --git a/core/java/android/nfc/INdefTag.aidl b/core/java/android/nfc/INfcSecureElement.aidl
old mode 100644
new mode 100755
similarity index 68%
rename from core/java/android/nfc/INdefTag.aidl
rename to core/java/android/nfc/INfcSecureElement.aidl
index d131ebe..aa98dd2
--- a/core/java/android/nfc/INdefTag.aidl
+++ b/core/java/android/nfc/INfcSecureElement.aidl
@@ -16,13 +16,13 @@
 
 package android.nfc;
 
-import android.nfc.NdefMessage;
-
 /**
- * @hide
+ * {@hide}
  */
-interface INdefTag
-{
-    NdefMessage read(int nativeHandle);
-    boolean write(int nativeHandle, in NdefMessage msg);
+interface INfcSecureElement {
+    int openSecureElementConnection();
+    int closeSecureElementConnection(int nativeHandle);
+    byte[] exchangeAPDU(int nativeHandle, in byte[] data);
+    int[] getSecureElementTechList(int nativeHandle);
+    byte[] getSecureElementUid(int nativeHandle);
 }
\ No newline at end of file
diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
index 13b97d6..ad3c1bb 100644
--- a/core/java/android/nfc/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -25,7 +25,7 @@
 {
     int close(int nativeHandle);
     int connect(int nativeHandle);
-    String getType(int nativeHandle);
+    int[] getTechList(int nativeHandle);
     byte[] getUid(int nativeHandle);
     boolean isNdef(int nativeHandle);
     boolean isPresent(int nativeHandle);
diff --git a/core/java/android/nfc/NdefTag.aidl b/core/java/android/nfc/NdefTag.aidl
deleted file mode 100644
index 288f667..0000000
--- a/core/java/android/nfc/NdefTag.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.nfc;
-
-parcelable NdefTag;
\ No newline at end of file
diff --git a/core/java/android/nfc/NdefTag.java b/core/java/android/nfc/NdefTag.java
deleted file mode 100644
index eb9d0dc..0000000
--- a/core/java/android/nfc/NdefTag.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.nfc;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents a discovered tag that contains {@link NdefMessage}s (or a tag that can store them).
- * In practice, a tag is a thing that an NFC-enabled device can communicate with. This
- * class is a representation of such a tag and can contain the NDEF messages shared by the tag.
- * <p>An NDEF tag can contain zero or more NDEF messages (represented by {@link NdefMessage}
- * objects) in addition to the basic tag properties of UID and Type.
- * <p>
- * {@link NdefTag}s that have been initialized will usually contain a single {@link NdefMessage}
- * (and that Message can contain multiple {@link NdefRecord}s). However it
- * is possible for {@link NdefTag}s to contain multiple {@link NdefMessage}s.
- * <p>{@link NfcAdapter#createNdefTagConnection createNdefTagConnection()} can be used to modify the
- * contents of some tags.
- * <p>This is an immutable data class. All properties are set at Tag discovery
- * time and calls on this class will retrieve those read-only properties, and
- * not cause any further RF activity or block. Note however that arrays passed to and
- * returned by this class are *not* cloned, so be careful not to modify them.
- * @hide
- */
-public class NdefTag extends Tag implements Parcelable {
-    /**
-     * Target for NFC Forum Type 1 compliant tag.
-     * <p>This is based on Jewel/Topaz technology
-     */
-    public static final String TARGET_TYPE_1 = "type_1";
-
-    /**
-     * Target for NFC Forum Type 2 compliant tag.
-     * <p>This is based on Mifare Ultralight technology.
-     */
-    public static final String TARGET_TYPE_2 = "type_2";
-
-    /**
-     * Target for NFC Forum Type 3 compliant tag.
-     * <p>This is based on Felica technology.
-     */
-    public static final String TARGET_TYPE_3 = "type_3";
-
-    /**
-     * Target for NFC Forum Type 4 compliant tag.
-     * <p>This is based on Mifare Desfire technology.
-     */
-    public static final String TARGET_TYPE_4 = "type_4";
-
-    /**
-     * Target for NFC Forum Enabled: Mifare Classic tag.
-     * <p>This is not strictly a NFC Forum tag type, but is a common
-     * NDEF message container.
-     */
-    public static final String TARGET_MIFARE_CLASSIC = "type_mifare_classic";
-
-    /**
-     * Any other target.
-     */
-    public static final String TARGET_OTHER = "other";
-
-    private final String[] mNdefTargets;
-    private final NdefMessage[][] mMessages;  // one NdefMessage[] per NDEF target
-    private NdefMessage[] mFlatMessages;  // collapsed mMessages, built lazily, protected by (this)
-
-    /**
-     * Hidden constructor to be used by NFC service only.
-     * @hide
-     */
-    public NdefTag(byte[] id, String[] rawTargets, byte[] pollBytes, byte[] activationBytes,
-            int serviceHandle, String[] ndefTargets, NdefMessage[][] messages) {
-        super(id, true, rawTargets, pollBytes, activationBytes, serviceHandle);
-        if (ndefTargets == null || messages == null) {
-            throw new IllegalArgumentException("ndefTargets or messages cannot be null");
-        }
-        if (ndefTargets.length != messages.length){
-            throw new IllegalArgumentException("ndefTargets and messages arrays must match");
-        }
-        for (NdefMessage[] ms : messages) {
-            if (ms == null) {
-                throw new IllegalArgumentException("messages elements cannot be null");
-            }
-        }
-        mNdefTargets = ndefTargets;
-        mMessages = messages;
-    }
-
-    /**
-     * Construct a mock NdefTag.
-     * <p>This is an application constructed tag, so NfcAdapter methods on this
-     * Tag such as {@link NfcAdapter#createRawTagConnection} will fail with
-     * {@link IllegalArgumentException} since it does not represent a physical Tag.
-     * <p>This constructor might be useful for mock testing.
-     * @param id The tag identifier, can be null
-     * @param rawTargets must not be null
-     * @param pollBytes can be null
-     * @param activationBytes can be null
-     * @param ndefTargets NDEF target array, such as {TARGET_TYPE_2}, cannot be null
-     * @param messages messages, one array per NDEF target, cannot be null
-     * @return freshly constructed NdefTag
-     */
-    public static NdefTag createMockNdefTag(byte[] id, String[] rawTargets, byte[] pollBytes,
-            byte[] activationBytes, String[] ndefTargets, NdefMessage[][] messages) {
-        // set serviceHandle to 0 to indicate mock tag
-        return new NdefTag(id, rawTargets, pollBytes, activationBytes, 0, ndefTargets, messages);
-    }
-
-    /**
-     * Get all NDEF Messages.
-     * <p>
-     * This retrieves the NDEF Messages that were found on the Tag at discovery
-     * time. It does not cause any further RF activity, and does not block.
-     * <p>
-     * Most tags only contain a single NDEF message.
-     *
-     * @return NDEF Messages found at Tag discovery
-     */
-    public NdefMessage[] getNdefMessages() {
-        // common-case optimization
-        if (mMessages.length == 1) {
-            return mMessages[0];
-        }
-
-        // return cached flat array
-        synchronized(this) {
-            if (mFlatMessages != null) {
-                return mFlatMessages;
-            }
-            // not cached - build a flat array
-            int sz = 0;
-            for (NdefMessage[] ms : mMessages) {
-                sz += ms.length;
-            }
-            mFlatMessages = new NdefMessage[sz];
-            int i = 0;
-            for (NdefMessage[] ms : mMessages) {
-                System.arraycopy(ms, 0, mFlatMessages, i, ms.length);
-                i += ms.length;
-            }
-            return mFlatMessages;
-        }
-    }
-
-    /**
-     * Get only the NDEF Messages from a single NDEF target on a tag.
-     * <p>
-     * This retrieves the NDEF Messages that were found on the Tag at discovery
-     * time. It does not cause any further RF activity, and does not block.
-     * <p>
-     * Most tags only contain a single NDEF message.
-     *
-     * @param target one of targets strings provided by getNdefTargets()
-     * @return NDEF Messages found at Tag discovery
-     */
-    public NdefMessage[] getNdefMessages(String target) {
-        for (int i=0; i<mNdefTargets.length; i++) {
-            if (target.equals(mNdefTargets[i])) {
-                return mMessages[i];
-            }
-        }
-        throw new IllegalArgumentException("target (" + target + ") not found");
-    }
-
-    /**
-     * Return the NDEF targets on this Tag that support NDEF messages.
-     *
-     * @return
-     */
-    public String[] getNdefTargets() {
-        return mNdefTargets;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        // Tag fields
-        dest.writeInt(mIsNdef ? 1 : 0);
-        writeBytesWithNull(dest, mId);
-        dest.writeInt(mRawTargets.length);
-        dest.writeStringArray(mRawTargets);
-        writeBytesWithNull(dest, mPollBytes);
-        writeBytesWithNull(dest, mActivationBytes);
-        dest.writeInt(mServiceHandle);
-
-        // NdefTag fields
-        dest.writeInt(mNdefTargets.length);
-        dest.writeStringArray(mNdefTargets);
-        dest.writeInt(mMessages.length);
-        for (NdefMessage[] ms : mMessages) {
-            dest.writeInt(ms.length);
-            dest.writeTypedArray(ms, flags);
-        }
-    }
-
-    public static final Parcelable.Creator<NdefTag> CREATOR =
-            new Parcelable.Creator<NdefTag>() {
-        public NdefTag createFromParcel(Parcel in) {
-            boolean isNdef = (in.readInt() == 1);
-            if (!isNdef) {
-                throw new IllegalArgumentException("Creating NdefTag from Tag parcel");
-            }
-
-            // Tag fields
-            byte[] id = readBytesWithNull(in);
-            String[] rawTargets = new String[in.readInt()];
-            in.readStringArray(rawTargets);
-            byte[] pollBytes = readBytesWithNull(in);
-            byte[] activationBytes = readBytesWithNull(in);
-            int serviceHandle = in.readInt();
-
-            // NdefTag fields
-            String[] ndefTargets = new String[in.readInt()];
-            in.readStringArray(ndefTargets);
-            NdefMessage[][] messages = new NdefMessage[in.readInt()][];
-            for (int i=0; i<messages.length; i++) {
-                messages[i] = new NdefMessage[in.readInt()];
-                in.readTypedArray(messages[i], NdefMessage.CREATOR);
-            }
-            return new NdefTag(id, rawTargets, pollBytes, activationBytes, serviceHandle,
-                    ndefTargets, messages);
-        }
-        public NdefTag[] newArray(int size) {
-            return new NdefTag[size];
-        }
-    };
-}
\ No newline at end of file
diff --git a/core/java/android/nfc/NdefTagConnection.java b/core/java/android/nfc/NdefTagConnection.java
deleted file mode 100644
index aafdbfd..0000000
--- a/core/java/android/nfc/NdefTagConnection.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.nfc;
-
-import java.io.IOException;
-
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * A connection to an NDEF target on an {@link NdefTag}.
- * <p>You can acquire this kind of connection with {@link NfcAdapter#createNdefTagConnection
- * createNdefTagConnection()}. Use the connection to read or write {@link NdefMessage}s.
- * <p class="note"><strong>Note:</strong>
- * Use of this class requires the {@link android.Manifest.permission#NFC}
- * permission.
- * @hide
- */
-public class NdefTagConnection extends RawTagConnection {
-    public static final int NDEF_MODE_READ_ONCE = 1;
-    public static final int NDEF_MODE_READ_ONLY = 2;
-    public static final int NDEF_MODE_WRITE_ONCE = 3;
-    public static final int NDEF_MODE_WRITE_MANY = 4;
-    public static final int NDEF_MODE_UNKNOWN = 5;
-
-    private static final String TAG = "NFC";
-
-    /**
-     * Internal constructor, to be used by NfcAdapter
-     * @hide
-     */
-    /* package private */ NdefTagConnection(NfcAdapter adapter, NdefTag tag, String target) throws RemoteException {
-        super(adapter, tag);
-        String[] targets = tag.getNdefTargets();
-        int i;
-
-        // Check target validity
-        for (i=0; i<targets.length; i++) {
-            if (target.equals(targets[i])) {
-                break;
-            }
-        }
-        if (i >= targets.length) {
-            // Target not found
-            throw new IllegalArgumentException();
-        }
-    }
-
-    /**
-     * Internal constructor, to be used by NfcAdapter
-     * @hide
-     */
-    /* package private */ NdefTagConnection(NfcAdapter adapter, NdefTag tag) throws RemoteException {
-        this(adapter, tag, tag.getNdefTargets()[0]);
-    }
-
-    /**
-     * Read NDEF message(s).
-     * This will always return the most up to date payload, and can block.
-     * It can be canceled with {@link RawTagConnection#close}.
-     * Most NDEF tags will contain just one NDEF message.
-     * <p>Requires {@link android.Manifest.permission#NFC} permission.
-     * @throws FormatException if the tag is not NDEF formatted
-     * @throws IOException if the target is lost or connection closed
-     * @throws FormatException
-     */
-    public NdefMessage[] readNdefMessages() throws IOException, FormatException {
-        //TODO(nxp): do not use getLastError(), it is racy
-        try {
-            NdefMessage[] msgArray = new NdefMessage[1];
-            NdefMessage msg = mTagService.read(mTag.mServiceHandle);
-            if (msg == null) {
-                int errorCode = mTagService.getLastError(mTag.mServiceHandle);
-                switch (errorCode) {
-                    case ErrorCodes.ERROR_IO:
-                        throw new IOException();
-                    case ErrorCodes.ERROR_INVALID_PARAM:
-                        throw new FormatException();
-                    default:
-                        // Should not happen
-                        throw new IOException();
-                }
-            }
-            msgArray[0] = msg;
-            return msgArray;
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            return null;
-        }
-    }
-
-    /**
-     * Attempt to write an NDEF message to a tag.
-     * This method will block until the data is written. It can be canceled
-     * with {@link RawTagConnection#close}.
-     * Many tags are write-once, so use this method carefully.
-     * Specification allows for multiple NDEF messages per NDEF tag, but it is
-     * encourage to only write one message, this so API only takes a single
-     * message. Use {@link NdefRecord} to write several records to a single tag.
-     * For write-many tags, use {@link #makeReadOnly} after this method to attempt
-     * to prevent further modification. For write-once tags this is not
-     * necessary.
-     * <p>Requires {@link android.Manifest.permission#NFC} permission.
-     *
-     * @throws FormatException if the tag is not suitable for NDEF messages
-     * @throws IOException if the target is lost or connection closed or the
-     *                     write failed
-     */
-    public void writeNdefMessage(NdefMessage message) throws IOException, FormatException {
-        try {
-            int errorCode = mTagService.write(mTag.mServiceHandle, message);
-            switch (errorCode) {
-                case ErrorCodes.SUCCESS:
-                    break;
-                case ErrorCodes.ERROR_IO:
-                    throw new IOException();
-                case ErrorCodes.ERROR_INVALID_PARAM:
-                    throw new FormatException();
-                default:
-                    // Should not happen
-                    throw new IOException();
-            }
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-        }
-    }
-
-    /**
-     * Attempts to make the NDEF data in this tag read-only.
-     * This method will block until the action is complete. It can be canceled
-     * with {@link RawTagConnection#close}.
-     * <p>Requires {@link android.Manifest.permission#NFC} permission.
-     * @return true if the tag is now read-only
-     * @throws IOException if the target is lost, or connection closed
-     */
-    public boolean makeReadOnly() throws IOException {
-        try {
-            int errorCode = mTagService.makeReadOnly(mTag.mServiceHandle);
-            switch (errorCode) {
-                case ErrorCodes.SUCCESS:
-                    return true;
-                case ErrorCodes.ERROR_IO:
-                    throw new IOException();
-                case ErrorCodes.ERROR_INVALID_PARAM:
-                    return false;
-                default:
-                    // Should not happen
-                    throw new IOException();
-            }
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            return false;
-        }
-    }
-
-    /**
-     * Read/Write mode hint.
-     * Provides a hint if further reads or writes are likely to succeed.
-     * <p>Requires {@link android.Manifest.permission#NFC} permission.
-     * @return one of NDEF_MODE
-     * @throws IOException if the target is lost or connection closed
-     */
-    public int getModeHint() throws IOException {
-        try {
-            int result = mTagService.getModeHint(mTag.mServiceHandle);
-            if (ErrorCodes.isError(result)) {
-                switch (result) {
-                    case ErrorCodes.ERROR_IO:
-                        throw new IOException();
-                    default:
-                        // Should not happen
-                        throw new IOException();
-                }
-            }
-            return result;
-
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            return NDEF_MODE_UNKNOWN;
-        }
-    }
-}
\ No newline at end of file
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 88b6ea4..a1c22bf 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -1,25 +1,26 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project Licensed under the Apache
- * License, Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
- * or agreed to in writing, software distributed under the License is
- * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language
- * governing permissions and limitations under the License.
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package android.nfc;
 
-import java.lang.UnsupportedOperationException;
-
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.app.ActivityThread;
-import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.nfc.INfcAdapter;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -40,6 +41,12 @@
     public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
 
     /**
+     * Broadcast to only the activity that handles ACTION_TAG_DISCOVERED
+     * @hide
+     */
+    public static final String ACTION_TAG_LEFT_FIELD = "android.nfc.action.TAG_LOST";
+
+    /**
      * Mandatory Tag extra for the ACTION_TAG intents.
      * @hide
      */
@@ -170,6 +177,14 @@
     }
 
     /**
+     * Returns the binder interface to the service.
+     * @hide
+     */
+    public INfcAdapter getService() {
+        return mService;
+    }
+    
+    /**
      * Helper to check if this device has FEATURE_NFC, but without using
      * a context.
      * Equivalent to
@@ -230,8 +245,11 @@
         }
     }
 
-    /** NFC service dead - attempt best effort recovery */
-    /*package*/ void attemptDeadServiceRecovery(Exception e) {
+    /**
+     * NFC service dead - attempt best effort recovery
+     * @hide
+     */
+    public void attemptDeadServiceRecovery(Exception e) {
         Log.e(TAG, "NFC service dead - attempting to recover", e);
         INfcAdapter service = getServiceInterface();
         if (service == null) {
@@ -301,16 +319,41 @@
     }
 
     /**
-     * Create a raw tag connection to the default Target
+     * Set the NDEF Message that this NFC adapter should appear as to Tag
+     * readers.
+     * <p>
+     * Any Tag reader can read the contents of the local tag when it is in
+     * proximity, without any further user confirmation.
+     * <p>
+     * The implementation of this method must either
+     * <ul>
+     * <li>act as a passive tag containing this NDEF message
+     * <li>provide the NDEF message on over LLCP to peer NFC adapters
+     * </ul>
+     * The NDEF message is preserved across reboot.
      * <p>Requires {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param message NDEF message to make public
      * @hide
      */
-    public RawTagConnection createRawTagConnection(Tag tag) {
-        if (tag.mServiceHandle == 0) {
-            throw new IllegalArgumentException("mock tag cannot be used for connections");
-        }
+    public void setLocalNdefMessage(NdefMessage message) {
         try {
-            return new RawTagConnection(this, tag);
+            mService.localSet(message);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
+    }
+
+    /**
+     * Get the NDEF Message that this adapter appears as to Tag readers.
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
+     *
+     * @return NDEF Message that is publicly readable
+     * @hide
+     */
+    public NdefMessage getLocalNdefMessage() {
+        try {
+            return mService.localGet();
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
             return null;
@@ -318,52 +361,14 @@
     }
 
     /**
-     * Create a raw tag connection to the specified Target
-     * <p>Requires {@link android.Manifest.permission#NFC} permission.
+     * Create an Nfc Secure Element Connection
      * @hide
      */
-    public RawTagConnection createRawTagConnection(Tag tag, String target) {
-        if (tag.mServiceHandle == 0) {
-            throw new IllegalArgumentException("mock tag cannot be used for connections");
-        }
+    public NfcSecureElement createNfcSecureElementConnection() {
         try {
-            return new RawTagConnection(this, tag, target);
+            return new NfcSecureElement(mService.getNfcSecureElementInterface());
         } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            return null;
-        }
-    }
-
-    /**
-     * Create an NDEF tag connection to the default Target
-     * <p>Requires {@link android.Manifest.permission#NFC} permission.
-     * @hide
-     */
-    public NdefTagConnection createNdefTagConnection(NdefTag tag) {
-        if (tag.mServiceHandle == 0) {
-            throw new IllegalArgumentException("mock tag cannot be used for connections");
-        }
-        try {
-            return new NdefTagConnection(this, tag);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            return null;
-        }
-    }
-
-    /**
-     * Create an NDEF tag connection to the specified Target
-     * <p>Requires {@link android.Manifest.permission#NFC} permission.
-     * @hide
-     */
-    public NdefTagConnection createNdefTagConnection(NdefTag tag, String target) {
-        if (tag.mServiceHandle == 0) {
-            throw new IllegalArgumentException("mock tag cannot be used for connections");
-        }
-        try {
-            return new NdefTagConnection(this, tag, target);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
+            Log.e(TAG, "createNfcSecureElementConnection failed", e);
             return null;
         }
     }
diff --git a/core/java/android/nfc/NfcSecureElement.java b/core/java/android/nfc/NfcSecureElement.java
new file mode 100755
index 0000000..5f4c066
--- /dev/null
+++ b/core/java/android/nfc/NfcSecureElement.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.nfc.technology.TagTechnology;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+//import android.util.Log;
+
+/**
+ * This class provides the primary API for managing all aspects Secure Element.
+ * Get an instance of this class by calling
+ * Context.getSystemService(Context.NFC_SERVICE).
+ * @hide
+ */
+public final class NfcSecureElement {
+
+    private static final String TAG = "NfcSecureElement";
+
+    private INfcSecureElement mService;
+    
+       
+    /**
+     * @hide
+     */
+    public NfcSecureElement(INfcSecureElement mSecureElementService) {
+        mService = mSecureElementService;
+    }
+
+    public int openSecureElementConnection(String seType) throws IOException {
+        if (seType.equals("SmartMX")) {
+            try {
+                int handle = mService.openSecureElementConnection();
+                // Handle potential errors
+                if (handle != 0) {
+                    return handle;
+                } else {
+                    throw new IOException("SmartMX connection not allowed");
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException in openSecureElementConnection(): ", e);
+                return 0;
+            }
+
+        } else if (seType.equals("UICC")) {
+            return 0;
+        } else {
+        	throw new IOException("Wrong Secure Element type");
+        }
+    }
+
+
+    public byte [] exchangeAPDU(int handle,byte [] data) throws IOException {
+        
+
+        // Perform exchange APDU
+        try {
+            byte[] response = mService.exchangeAPDU(handle, data);
+            // Handle potential errors
+            if (response == null) {
+            	throw new IOException("Exchange APDU failed");
+            }
+            return response;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in exchangeAPDU(): ", e);
+            return null;
+        }
+    }
+
+    public void closeSecureElementConnection(int handle) throws IOException {
+         
+        try {
+            int status = mService.closeSecureElementConnection(handle);
+            // Handle potential errors
+            if (ErrorCodes.isError(status)) {
+            	throw new IOException("Error during the conection close");
+            };
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in closeSecureElement(): ", e);
+        }
+    }
+    
+    
+    /**
+     * Returns target type. constants.
+     * 
+     * @return Secure Element technology type. The possible values are defined in
+     * {@link TagTechnology}
+     * 
+     */
+    public int[] getSecureElementTechList(int handle) throws IOException {
+        try {
+            return mService.getSecureElementTechList(handle);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getType(): ", e);
+            return null;
+        }
+    }
+    
+    /**
+     * Returns Secure Element UID.
+     * 
+     * @return Secure Element UID.
+     */
+    public byte[] getSecureElementUid(int handle) throws IOException {
+        
+        byte[] uid = null;
+        try {            
+            uid = mService.getSecureElementUid(handle);
+            // Handle potential errors
+            if (uid == null) {
+                throw new IOException("Get Secure Element UID failed");
+            }
+            return uid;
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getType(): ", e);
+            return null;
+        }
+    }
+
+}
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index 5d0b04c..04304dd 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -16,8 +16,21 @@
 
 package android.nfc;
 
+import android.nfc.technology.IsoDep;
+import android.nfc.technology.MifareClassic;
+import android.nfc.technology.MifareUltralight;
+import android.nfc.technology.NfcV;
+import android.nfc.technology.Ndef;
+import android.nfc.technology.NfcA;
+import android.nfc.technology.NfcB;
+import android.nfc.technology.NfcF;
+import android.nfc.technology.TagTechnology;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.RemoteException;
+
+import java.util.Arrays;
 
 /**
  * Represents a (generic) discovered tag.
@@ -30,13 +43,13 @@
  * {@link Tag} objects are passed to applications via the {@link NfcAdapter#EXTRA_TAG} extra
  * in {@link NfcAdapter#ACTION_TAG_DISCOVERED} intents. A {@link Tag} object is immutable
  * and represents the state of the tag at the time of discovery. It can be
- * directly queried for its UID and Type, or used to create a {@link RawTagConnection}
- * (with {@link NfcAdapter#createRawTagConnection createRawTagConnection()}).
+ * directly queried for its UID and Type, or used to create a {@link TagTechnology}
+ * (with {@link Tag#getTechnology(int)}).
  * <p>
- * A {@link Tag} can  be used to create a {@link RawTagConnection} only while the tag is in
+ * A {@link Tag} can  be used to create a {@link TagTechnology} only while the tag is in
  * range. If it is removed and then returned to range, then the most recent
  * {@link Tag} object (in {@link NfcAdapter#ACTION_TAG_DISCOVERED}) should be used to create a
- * {@link RawTagConnection}.
+ * {@link TagTechnology}.
  * <p>This is an immutable data class. All properties are set at Tag discovery
  * time and calls on this class will retrieve those read-only properties, and
  * not cause any further RF activity or block. Note however that arrays passed to and
@@ -44,78 +57,39 @@
  * @hide
  */
 public class Tag implements Parcelable {
-    /**
-     * ISO 14443-3A technology.
-     * <p>
-     * Includes Topaz (which is -3A compatible)
-     */
-    public static final String TARGET_ISO_14443_3A = "iso14443_3a";
-
-    /**
-     * ISO 14443-3B technology.
-     */
-    public static final String TARGET_ISO_14443_3B = "iso14443_3b";
-
-    /**
-     * ISO 14443-4 technology.
-     */
-    public static final String TARGET_ISO_14443_4 = "iso14443_4";
-
-    /**
-     * ISO 15693 technology, commonly known as RFID.
-     */
-    public static final String TARGET_ISO_15693 = "iso15693";
-
-    /**
-     * JIS X-6319-4 technology, commonly known as Felica.
-     */
-    public static final String TARGET_JIS_X_6319_4 = "jis_x_6319_4";
-
-    /**
-     * Any other technology.
-     */
-    public static final String TARGET_OTHER = "other";
-
-    /*package*/ final boolean mIsNdef;
     /*package*/ final byte[] mId;
-    /*package*/ final String[] mRawTargets;
-    /*package*/ final byte[] mPollBytes;
-    /*package*/ final byte[] mActivationBytes;
+    /*package*/ final int[] mTechList;
+    /*package*/ final Bundle[] mTechExtras;
     /*package*/ final int mServiceHandle;  // for use by NFC service, 0 indicates a mock
 
     /**
      * Hidden constructor to be used by NFC service and internal classes.
      * @hide
      */
-    public Tag(byte[] id, boolean isNdef, String[] rawTargets, byte[] pollBytes,
-            byte[] activationBytes, int serviceHandle) {
-        if (rawTargets == null) {
+    public Tag(byte[] id, int[] techList, Bundle[] techListExtras, int serviceHandle) {
+        if (techList == null) {
             throw new IllegalArgumentException("rawTargets cannot be null");
         }
-        mIsNdef = isNdef;
         mId = id;
-        mRawTargets = rawTargets;
-        mPollBytes = pollBytes;
-        mActivationBytes = activationBytes;
+        mTechList = Arrays.copyOf(techList, techList.length);
+        // Ensure mTechExtras is as long as mTechList
+        mTechExtras = Arrays.copyOf(techListExtras, techList.length);
         mServiceHandle = serviceHandle;
     }
 
     /**
      * Construct a mock Tag.
      * <p>This is an application constructed tag, so NfcAdapter methods on this
-     * Tag such as {@link NfcAdapter#createRawTagConnection} will fail with
+     * Tag such as {@link #getTechnology} may fail with
      * {@link IllegalArgumentException} since it does not represent a physical Tag.
      * <p>This constructor might be useful for mock testing.
      * @param id The tag identifier, can be null
-     * @param rawTargets must not be null
-     * @param pollBytes can be null
-     * @param activationBytes can be null
+     * @param techList must not be null
      * @return freshly constructed tag
      */
-    public static Tag createMockTag(byte[] id, String[] rawTargets, byte[] pollBytes,
-            byte[] activationBytes) {
+    public static Tag createMockTag(byte[] id, int[] techList, Bundle[] techListExtras) {
         // set serviceHandle to 0 to indicate mock tag
-        return new Tag(id, false, rawTargets, pollBytes, activationBytes, 0);
+        return new Tag(id, techList, techListExtras, 0);
     }
 
     /**
@@ -127,16 +101,6 @@
     }
 
     /**
-     * Return the available targets that this NFC adapter can use to create
-     * a RawTagConnection.
-     *
-     * @return raw targets, will not be null
-     */
-    public String[] getRawTargets() {
-        return mRawTargets;
-    }
-
-    /**
      * Get the Tag Identifier (if it has one).
      * <p>Tag ID is usually a serial number for the tag.
      *
@@ -147,37 +111,72 @@
     }
 
     /**
-     * Get the low-level bytes returned by this Tag at poll-time.
-     * <p>These can be used to help with advanced identification of a Tag.
-     * <p>The meaning of these bytes depends on the Tag technology.
-     * <p>ISO14443-3A: ATQA/SENS_RES
-     * <p>ISO14443-3B: Application data (4 bytes) and Protocol Info (3 bytes) from ATQB/SENSB_RES
-     * <p>JIS_X_6319_4: PAD0 (2 byte), PAD1 (2 byte), MRTI(2 byte), PAD2 (1 byte), RC (2 byte)
-     * <p>ISO15693: response flags (1 byte), DSFID (1 byte)
-     * from SENSF_RES
+     * Returns technologies present in the tag that this implementation understands,
+     * or a zero length array if there are no supported technologies on this tag.
      *
-     * @return poll bytes, or null if they do not exist for this Tag technology
-     * @hide
+     * The elements of the list are guaranteed be one of the constants defined in
+     * {@link TagTechnology}. 
+     *
+     * The ordering of the returned array is undefined and should not be relied upon.
      */
-    public byte[] getPollBytes() {
-        return mPollBytes;
+    public int[] getTechnologyList() { 
+        return Arrays.copyOf(mTechList, mTechList.length);
     }
 
     /**
-     * Get the low-level bytes returned by this Tag at activation-time.
-     * <p>These can be used to help with advanced identification of a Tag.
-     * <p>The meaning of these bytes depends on the Tag technology.
-     * <p>ISO14443-3A: SAK/SEL_RES
-     * <p>ISO14443-3B: null
-     * <p>ISO14443-3A & ISO14443-4: SAK/SEL_RES, historical bytes from ATS  <TODO: confirm>
-     * <p>ISO14443-3B & ISO14443-4: ATTRIB response
-     * <p>JIS_X_6319_4: null
-     * <p>ISO15693: response flags (1 byte), DSFID (1 byte): null
-     * @return activation bytes, or null if they do not exist for this Tag technology
-     * @hide
+     * Returns the technology, or null if not present
      */
-    public byte[] getActivationBytes() {
-        return mActivationBytes;
+    public TagTechnology getTechnology(int tech) {
+        int pos = -1;
+        for (int idx = 0; idx < mTechList.length; idx++) {
+          if (mTechList[idx] == tech) {
+              pos = idx;
+              break;
+          }
+        }
+        if (pos < 0) {
+            return null;
+        }
+
+        Bundle extras = mTechExtras[pos];
+        NfcAdapter adapter = NfcAdapter.getDefaultAdapter();
+        try {
+            switch (tech) {
+                case TagTechnology.NFC_A: {
+                    return new NfcA(adapter, this, extras);
+                }
+                case TagTechnology.NFC_B: {
+                    return new NfcB(adapter, this, extras);
+                }
+                case TagTechnology.ISO_DEP: {
+                    return new IsoDep(adapter, this, extras);
+                }
+                case TagTechnology.NFC_V: {
+                    return new NfcV(adapter, this, extras);
+                }
+                case TagTechnology.TYPE_1:
+                case TagTechnology.TYPE_2:
+                case TagTechnology.TYPE_3:
+                case TagTechnology.TYPE_4: {
+                    return new Ndef(adapter, this, tech, extras);
+                }
+                case TagTechnology.NFC_F: {
+                    return new NfcF(adapter, this, extras);
+                }
+                case TagTechnology.MIFARE_CLASSIC: {
+                    return new MifareClassic(adapter, this, extras);
+                }
+                case TagTechnology.MIFARE_ULTRALIGHT: {
+                    return new MifareUltralight(adapter, this, extras);
+                }
+
+                default: {
+                    throw new UnsupportedOperationException("Tech " + tech + " not supported");
+                }
+            }
+        } catch (RemoteException e) {
+            return null;
+        }
     }
 
     @Override
@@ -185,13 +184,9 @@
         StringBuilder sb = new StringBuilder("TAG ")
             .append("uid = ")
             .append(mId)
-            .append(" poll ")
-            .append(mPollBytes)
-            .append(" activation ")
-            .append(mActivationBytes)
-            .append(" Raw [");
-        for (String s : mRawTargets) {
-            sb.append(s)
+            .append(" Tech [");
+        for (int i : mTechList) {
+            sb.append(i)
             .append(", ");
         }
         return sb.toString();
@@ -221,37 +216,32 @@
         return 0;
     }
 
-
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mIsNdef ? 1 : 0);
         writeBytesWithNull(dest, mId);
-        dest.writeInt(mRawTargets.length);
-        dest.writeStringArray(mRawTargets);
-        writeBytesWithNull(dest, mPollBytes);
-        writeBytesWithNull(dest, mActivationBytes);
+        dest.writeInt(mTechList.length);
+        dest.writeIntArray(mTechList);
+        dest.writeTypedArray(mTechExtras, 0);
         dest.writeInt(mServiceHandle);
     }
 
     public static final Parcelable.Creator<Tag> CREATOR =
             new Parcelable.Creator<Tag>() {
+        @Override
         public Tag createFromParcel(Parcel in) {
-            boolean isNdef = (in.readInt() == 1);
-            if (isNdef) {
-                throw new IllegalArgumentException("Creating Tag from NdefTag parcel");
-            }
             // Tag fields
             byte[] id = Tag.readBytesWithNull(in);
-            String[] rawTargets = new String[in.readInt()];
-            in.readStringArray(rawTargets);
-            byte[] pollBytes = Tag.readBytesWithNull(in);
-            byte[] activationBytes = Tag.readBytesWithNull(in);
+            int[] techList = new int[in.readInt()];
+            in.readIntArray(techList);
+            Bundle[] techExtras = in.createTypedArray(Bundle.CREATOR);
             int serviceHandle = in.readInt();
 
-            return new Tag(id, isNdef, rawTargets, pollBytes, activationBytes, serviceHandle);
+            return new Tag(id, techList, techExtras, serviceHandle);
         }
+
+        @Override
         public Tag[] newArray(int size) {
             return new Tag[size];
         }
     };
-}
\ No newline at end of file
+}
diff --git a/core/java/android/nfc/RawTagConnection.java b/core/java/android/nfc/technology/BasicTagTechnology.java
similarity index 75%
rename from core/java/android/nfc/RawTagConnection.java
rename to core/java/android/nfc/technology/BasicTagTechnology.java
index bfdaa77..6b281b9 100644
--- a/core/java/android/nfc/RawTagConnection.java
+++ b/core/java/android/nfc/technology/BasicTagTechnology.java
@@ -14,31 +14,25 @@
  * limitations under the License.
  */
 
-package android.nfc;
+package android.nfc.technology;
 
 import java.io.IOException;
 
+import android.nfc.INfcAdapter;
+import android.nfc.INfcTag;
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
 import android.os.RemoteException;
 import android.util.Log;
 
 /**
- * A low-level connection to a {@link Tag} target.
- * <p>You can acquire this kind of connection with {@link NfcAdapter#createRawTagConnection
- * createRawTagConnection()}. Use the connection to send and receive data with {@link #transceive
- * transceive()}.
- * <p>
- * Applications must implement their own protocol stack on top of {@link #transceive transceive()}.
- *
- * <p class="note"><strong>Note:</strong>
- * Use of this class requires the {@link android.Manifest.permission#NFC}
- * permission.
- * @hide
+ * A base class for tag technologies that are built on top of transceive().
  */
-public class RawTagConnection {
+/* package */ abstract class BasicTagTechnology implements TagTechnology {
 
     /*package*/ final Tag mTag;
     /*package*/ boolean mIsConnected;
-    /*package*/ String mSelectedTarget;
+    /*package*/ int mSelectedTechnology;
     private final NfcAdapter mAdapter;
 
     // Following fields are final after construction, except for
@@ -49,30 +43,40 @@
 
     private static final String TAG = "NFC";
 
-    /*package*/ RawTagConnection(NfcAdapter adapter, Tag tag, String target) throws RemoteException {
-        String[] targets = tag.getRawTargets();
+    /**
+     * @hide
+     */
+    public BasicTagTechnology(NfcAdapter adapter, Tag tag, int tech) throws RemoteException {
+        int[] techList = tag.getTechnologyList();
         int i;
 
         // Check target validity
-        for (i=0;i<targets.length;i++) {
-            if (target.equals(targets[i])) {
+        for (i = 0; i < techList.length; i++) {
+            if (tech == techList[i]) {
                 break;
             }
         }
-        if (i >= targets.length) {
-            // Target not found
-            throw new IllegalArgumentException();
+        if (i >= techList.length) {
+            // Technology not found
+            throw new IllegalArgumentException("Technology " + tech + " not present on tag " + tag);
         }
 
         mAdapter = adapter;
-        mService = mAdapter.mService;
-        mTagService = mService.getNfcTagInterface();
+        mService = mAdapter.getService();
+        try {
+          mTagService = mService.getNfcTagInterface();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
         mTag = tag;
-        mSelectedTarget = target;
+        mSelectedTechnology = tech;
     }
 
-    /*package*/ RawTagConnection(NfcAdapter adapter, Tag tag) throws RemoteException {
-        this(adapter, tag, tag.getRawTargets()[0]);
+    /**
+     * @hide
+     */
+    public BasicTagTechnology(NfcAdapter adapter, Tag tag) throws RemoteException {
+        this(adapter, tag, tag.getTechnologyList()[0]);
     }
 
     /** NFC service dead - attempt best effort recovery */
@@ -80,7 +84,7 @@
         mAdapter.attemptDeadServiceRecovery(e);
         /* assigning to mService is not thread-safe, but this is best-effort code
          * and on a well-behaved system should never happen */
-        mService = mAdapter.mService;
+        mService = mAdapter.getService();
         try {
             mTagService = mService.getNfcTagInterface();
         } catch (RemoteException e2) {
@@ -92,6 +96,7 @@
      * Get the {@link Tag} this connection is associated with.
      * <p>Requires {@link android.Manifest.permission#NFC} permission.
      */
+    @Override
     public Tag getTag() {
         return mTag;
     }
@@ -99,8 +104,9 @@
     /**
      * <p>Requires {@link android.Manifest.permission#NFC} permission.
      */
-    public String getTagTarget() {
-        return mSelectedTarget;
+    @Override
+    public int getTechnologyId() {
+        return mSelectedTechnology;
     }
 
     /**
@@ -119,7 +125,7 @@
         }
 
         try {
-            return mTagService.isPresent(mTag.mServiceHandle);
+            return mTagService.isPresent(mTag.getServiceHandle());
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
             return false;
@@ -136,6 +142,7 @@
      * <p>Requires {@link android.Manifest.permission#NFC} permission.
      * @throws IOException if the target is lost, or connect canceled
      */
+    @Override
     public void connect() throws IOException {
         //TODO(nxp): enforce exclusivity
         mIsConnected = true;
@@ -151,10 +158,11 @@
      * calls to {@link #transceive transceive()} or {@link #connect} will fail.
      * <p>Requires {@link android.Manifest.permission#NFC} permission.
      */
+    @Override
     public void close() {
         mIsConnected = false;
         try {
-            mTagService.close(mTag.mServiceHandle);
+            mTagService.close(mTag.getServiceHandle());
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
         }
@@ -173,7 +181,7 @@
      */
     public byte[] transceive(byte[] data) throws IOException {
         try {
-            byte[] response = mTagService.transceive(mTag.mServiceHandle, data);
+            byte[] response = mTagService.transceive(mTag.getServiceHandle(), data);
             if (response == null) {
                 throw new IOException("transcieve failed");
             }
diff --git a/core/java/android/nfc/technology/IsoDep.java b/core/java/android/nfc/technology/IsoDep.java
new file mode 100644
index 0000000..5346c67
--- /dev/null
+++ b/core/java/android/nfc/technology/IsoDep.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.technology;
+
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * A low-level connection to a {@link Tag} using the ISO-DEP technology, also known as
+ * ISO1443-4.
+ *
+ * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}.
+ * Use this class to send and receive data with {@link #transceive transceive()}.
+ *
+ * <p>Applications must implement their own protocol stack on top of
+ * {@link #transceive transceive()}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
+ */
+public final class IsoDep extends BasicTagTechnology {
+    /** @hide */
+    public static final String EXTRA_ATTRIB = "attrib";
+    /** @hide */
+    public static final String EXTRA_HIST_BYTES = "histbytes";
+
+    private byte[] mAttrib = null;
+    private byte[] mHistBytes = null;
+
+    public IsoDep(NfcAdapter adapter, Tag tag, Bundle extras)
+            throws RemoteException {
+        super(adapter, tag, TagTechnology.ISO_DEP);
+        if (extras != null) {
+            mAttrib = extras.getByteArray(EXTRA_ATTRIB);
+            mHistBytes = extras.getByteArray(EXTRA_HIST_BYTES);
+        }
+    }
+
+    /**
+     * 3A only
+     */
+    public byte[] getHistoricalBytes() { return mHistBytes; }
+
+    /**
+     * 3B only
+     */
+    public byte[] getAttrib() { return mAttrib; }
+
+    /**
+     * Attempts to select the given application on the tag. Note that this only works
+     * if the tag supports ISO7816-4, which not all IsoDep tags support. If the tag doesn't
+     * support ISO7816-4 this will throw {@link UnsupportedOperationException}.
+     *
+     * This method requires that you call {@link #connect} before calling it.
+     *
+     * @throws IOException, UnsupportedOperationException
+     */
+    public void selectAid(byte[] aid) throws IOException, UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/core/java/android/nfc/technology/MifareClassic.java b/core/java/android/nfc/technology/MifareClassic.java
new file mode 100644
index 0000000..c25b71f
--- /dev/null
+++ b/core/java/android/nfc/technology/MifareClassic.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.technology;
+
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * Concrete class for TagTechnology.MIFARE_CLASSIC
+ *
+ * Mifare classic has n sectors, with varying sizes, although
+ * they are at least the same pattern for any one mifare classic
+ * product. Each sector has two keys. Authentication with the correct
+ * key is needed before access to any sector.
+ *
+ * Each sector has k blocks.
+ * Block size is constant across the whole mifare classic family.
+ */
+public final class MifareClassic extends BasicTagTechnology {
+    /**
+     * The well-known, default factory MIFARE read key.
+     * Use this key to effectively make the payload in this sector
+     * public.
+     */
+    public static final byte[] DEFAULT_KEY_FACTORY =
+            {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF};
+    public static final byte[] DEFAULT_KEY_ZERO =
+            {(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00};
+    /**
+     * The well-known, default Mifare Application Directory read key.
+     */
+    public static final byte[] DEFAULT_KEY_MAD =
+            {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5};
+    /**
+     * The well-known, default read key for NDEF data on a Mifare Classic
+     */
+    public static final byte[] DEFAULT_KEY_NFC_FORUM =
+            {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7};
+
+    public static final int TYPE_CLASSIC = 0;
+    public static final int TYPE_PLUS = 1;
+    public static final int TYPE_PRO = 2;
+    public static final int TYPE_DESFIRE = 3;
+    public static final int TYPE_ULTRALIGHT = 4;
+    public static final int TYPE_UNKNOWN = 5;
+
+    public static final int SIZE_1K = 1024;
+    public static final int SIZE_2K = 2048;
+    public static final int SIZE_4K = 4096;
+    public static final int SIZE_MINI = 320;
+    public static final int SIZE_UNKNOWN = 0;
+
+    private boolean mIsEmulated;
+    private int mType;
+    private int mSize;
+
+    public MifareClassic(NfcAdapter adapter, Tag tag, Bundle extras) throws RemoteException {
+        super(adapter, tag, TagTechnology.MIFARE_CLASSIC);
+
+        // Check if this could actually be a Mifare
+        NfcA a = (NfcA) tag.getTechnology(TagTechnology.NFC_A);
+        //short[] ATQA = getATQA(tag);
+
+        mIsEmulated = false;
+        mType = TYPE_UNKNOWN;
+        mSize = SIZE_UNKNOWN;
+
+        switch (a.getSak()) {
+            case 0x00:
+                // could be UL or UL-C
+                mType = TYPE_ULTRALIGHT;
+                break;
+            case 0x08:
+                // Type == classic
+                // Size = 1K
+                mType = TYPE_CLASSIC;
+                mSize = SIZE_1K;
+                break;
+            case 0x09:
+                // Type == classic mini
+                // Size == ?
+                mType = TYPE_CLASSIC;
+                mSize = SIZE_MINI;
+                break;
+            case 0x10:
+                // Type == MF+
+                // Size == 2K
+                // SecLevel = SL2
+                mType = TYPE_PLUS;
+                mSize = SIZE_2K;
+                break;
+            case 0x11:
+                // Type == MF+
+                // Size == 4K
+                // Seclevel = SL2
+                mType = TYPE_PLUS;
+                mSize = SIZE_4K;
+                break;
+            case 0x18:
+                // Type == classic
+                // Size == 4k
+                mType = TYPE_CLASSIC;
+                mSize = SIZE_4K;
+                break;
+            case 0x20:
+                // TODO this really should be a short, not byte
+                if (a.getAtqa()[0] == 0x03) {
+                    // Type == DESFIRE
+                    mType = TYPE_DESFIRE;
+                } else {
+                    // Type == MF+
+                    // SL = SL3
+                    mType = TYPE_PLUS;
+                    mSize = SIZE_UNKNOWN;
+                }
+                break;
+            case 0x28:
+                // Type == MF Classic
+                // Size == 1K
+                // Emulated == true
+                mType = TYPE_CLASSIC;
+                mSize = SIZE_1K;
+                mIsEmulated = true;
+                break;
+            case 0x38:
+                // Type == MF Classic
+                // Size == 4K
+                // Emulated == true
+                mType = TYPE_CLASSIC;
+                mSize = SIZE_4K;
+                mIsEmulated = true;
+                break;
+            case 0x88:
+                // Type == MF Classic
+                // Size == 1K
+                // NXP-tag: false
+                mType = TYPE_CLASSIC;
+                mSize = SIZE_1K;
+                break;
+            case 0x98:
+            case 0xB8:
+                // Type == MF Pro
+                // Size == 4K
+                mType = TYPE_PRO;
+                mSize = SIZE_4K;
+                break;
+            default:
+                // Unknown, not MIFARE
+                break;
+        }
+    }
+
+    // Immutable data known at discovery time
+    public int getSize() {
+        return mSize;
+    }
+
+    public int getType() {
+        return mType;
+    }
+
+    public boolean isEmulated() {
+        return mIsEmulated;
+    }
+
+    public int getSectorCount() {
+        switch (mSize) {
+            case SIZE_1K: {
+                return 16;
+            }
+            case SIZE_2K: {
+                return 32;
+            }
+            case SIZE_4K: {
+                return 40;
+            }
+            case SIZE_MINI: {
+                return 5;
+            }
+            default: {
+                return 0;
+            }
+        }
+    }
+
+    public int getSectorSize(int sector) {
+        return getBlockCount(sector) * 16;
+    }
+
+    public int getBlockCount(int sector) {
+        if (sector >= getSectorCount()) {
+            throw new IllegalArgumentException("this card only has " + getSectorCount() +
+                    " sectors");
+        }
+
+        if (sector <= 32) {
+            return 4;
+        } else {
+            return 16;
+        }
+    }
+
+    private byte firstBlockInSector(int sector) {
+        if (sector < 32) {
+            return (byte) ((sector * 4) & 0xff);
+        } else {
+            return (byte) ((32 * 4 + ((sector - 32) * 16)) & 0xff);
+        }
+    }
+
+    // Methods that require connect()
+    /**
+     * Authenticate for a given sector.
+     */
+    public boolean authenticateSector(int sector, byte[] key, boolean keyA) {
+        byte[] cmd = new byte[12];
+
+        // First byte is the command
+        if (keyA) {
+            cmd[0] = 0x60; // phHal_eMifareAuthentA
+        } else {
+            cmd[0] = 0x61; // phHal_eMifareAuthentB
+        }
+
+        // Second byte is block address
+        cmd[1] = firstBlockInSector(sector);
+
+        // Next 4 bytes are last 4 bytes of UID
+        byte[] uid = getTag().getId();
+        System.arraycopy(uid, uid.length - 4, cmd, 2, 4);
+
+        // Next 6 bytes are key
+        System.arraycopy(key, 0, cmd, 6, 6);
+
+        try {
+            if ((transceive(cmd) != null)) {
+                return true;
+            }
+        } catch (IOException e) {
+            // No need to deal with, will return false anyway
+        }
+        return false;
+    }
+
+    /**
+     * Sector indexing starts at 0.
+     * Block indexing starts at 0, and resets in each sector.
+     * @throws IOException
+     */
+    public byte[] readBlock(int sector, int block) throws IOException {
+        byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff);
+        byte[] blockread_cmd = { 0x30, addr }; // phHal_eMifareRead
+
+        // TODO deal with authentication problems
+        return transceive(blockread_cmd);
+    }
+
+//    public byte[] readSector(int sector);
+    //TODO: define an enumeration for access control settings
+//    public int readSectorAccessControl(int sector);
+
+    /**
+     * @throws IOException
+     * @throws NotAuthenticatedException
+     */
+/*
+    public void writeBlock(int block, byte[] data);
+    public void writeSector(int block, byte[] sector);
+    public void writeSectorAccessControl(int sector, int access);
+    public void increment(int block);
+    public void decrement(int block);
+*/
+}
diff --git a/core/java/android/nfc/technology/MifareUltralight.java b/core/java/android/nfc/technology/MifareUltralight.java
new file mode 100644
index 0000000..dd1dae9
--- /dev/null
+++ b/core/java/android/nfc/technology/MifareUltralight.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.technology;
+
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * Concrete class for TagTechnology.MIFARE_ULTRALIGHT
+ *
+ * Mifare classic has n sectors, with varying sizes, although
+ * they are at least the same pattern for any one mifare classic
+ * product. Each sector has two keys. Authentication with the correct
+ * key is needed before access to any sector.
+ *
+ * Each sector has k blocks.
+ * Block size is constant across the whole mifare classic family.
+ */
+public final class MifareUltralight extends BasicTagTechnology {
+    public static final int TYPE_ULTRALIGHT = 1;
+    public static final int TYPE_ULTRALIGHT_C = 2;
+    public static final int TYPE_UNKNOWN = 10;
+
+		private static final int NXP_MANUFACTURER_ID = 0x04;
+
+    private int mType;
+
+    public MifareUltralight(NfcAdapter adapter, Tag tag, Bundle extras) throws RemoteException {
+        super(adapter, tag, TagTechnology.MIFARE_ULTRALIGHT);
+
+        // Check if this could actually be a Mifare
+        NfcA a = (NfcA) tag.getTechnology(TagTechnology.NFC_A);
+
+        mType = TYPE_UNKNOWN;
+
+        if( a.getSak() == 0x00 && tag.getId()[0] == NXP_MANUFACTURER_ID ) {
+					// could be UL or UL-C
+					mType = TYPE_ULTRALIGHT;
+        }
+    }
+
+    public int getType() {
+        return mType;
+    }
+
+    // Methods that require connect()
+    /**
+     * @throws IOException
+     */
+    public byte[] readBlock(int block) throws IOException {
+        byte[] blockread_cmd = { 0x30, (byte)block }; // phHal_eMifareRead
+        return transceive(blockread_cmd);
+    }
+
+    /**
+     * @throws IOException
+     */
+/*
+    public byte[] readOTP();
+    public void writePage(int block, byte[] data);
+    public void writeBlock(int block, byte[] data);
+*/
+}
diff --git a/core/java/android/nfc/technology/Ndef.java b/core/java/android/nfc/technology/Ndef.java
new file mode 100644
index 0000000..22460cf
--- /dev/null
+++ b/core/java/android/nfc/technology/Ndef.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.technology;
+
+import android.nfc.ErrorCodes;
+import android.nfc.FormatException;
+import android.nfc.NdefMessage;
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * A high-level connection to a {@link Tag} using one of the NFC type 1, 2, 3, or 4 technologies
+ * to interact with NDEF data. MiFare Classic cards that present NDEF data may also be used
+ * via this class. To determine the exact technology being used call {@link #getTechnologyId()}
+ *
+ * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
+ */
+public final class Ndef extends BasicTagTechnology {
+    public static final int NDEF_MODE_READ_ONCE = 1;
+    public static final int NDEF_MODE_READ_ONLY = 2;
+    public static final int NDEF_MODE_WRITE_ONCE = 3;
+    public static final int NDEF_MODE_WRITE_MANY = 4;
+    public static final int NDEF_MODE_UNKNOWN = 5;
+
+    /**
+     * Internal constructor, to be used by NfcAdapter
+     * @hide
+     */
+    public Ndef(NfcAdapter adapter, Tag tag, int tech, Bundle extras) throws RemoteException {
+        super(adapter, tag, tech);
+    }
+
+    /**
+     * Get the primary NDEF message on this tag. This data is read at discovery time
+     * and does not require a connection.
+     */
+    public NdefMessage getNdefMessage() throws IOException, FormatException {
+        try {
+            int serviceHandle = mTag.getServiceHandle();
+            NdefMessage msg = mTagService.read(serviceHandle);
+            if (msg == null) {
+                int errorCode = mTagService.getLastError(serviceHandle);
+                switch (errorCode) {
+                    case ErrorCodes.ERROR_IO:
+                        throw new IOException();
+                    case ErrorCodes.ERROR_INVALID_PARAM:
+                        throw new FormatException();
+                    default:
+                        // Should not happen
+                        throw new IOException();
+                }
+            }
+            return msg;
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return null;
+        }
+    }
+
+    /**
+     * Get optional extra NDEF messages.
+     * Some tags may contain extra NDEF messages, but not all
+     * implementations will be able to read them.
+     */
+    public NdefMessage[] getExtraNdefMessage() throws IOException, FormatException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Get maximum NDEF message size in bytes
+     */
+    public int getSize() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Read/Write mode hint.
+     * Provides a hint if further reads or writes are likely to succeed.
+     * <p>Requires {@link android.Manifest.permission#NFC} permission.
+     * @return one of NDEF_MODE
+     * @throws IOException if the target is lost or connection closed
+     */
+    public int getModeHint() throws IOException {
+        try {
+            int result = mTagService.getModeHint(mTag.getServiceHandle());
+            if (ErrorCodes.isError(result)) {
+                switch (result) {
+                    case ErrorCodes.ERROR_IO:
+                        throw new IOException();
+                    default:
+                        // Should not happen
+                        throw new IOException();
+                }
+            }
+            return result;
+
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return NDEF_MODE_UNKNOWN;
+        }
+    }
+
+    // Methods that require connect()
+    /**
+     * Overwrite the primary NDEF message
+     * @throws IOException
+     */
+    public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException {
+        try {
+            int errorCode = mTagService.write(mTag.getServiceHandle(), msg);
+            switch (errorCode) {
+                case ErrorCodes.SUCCESS:
+                    break;
+                case ErrorCodes.ERROR_IO:
+                    throw new IOException();
+                case ErrorCodes.ERROR_INVALID_PARAM:
+                    throw new FormatException();
+                default:
+                    // Should not happen
+                    throw new IOException();
+            }
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
+    }
+
+    /**
+     * Attempt to write extra NDEF messages.
+     * Implementations may be able to write extra NDEF
+     * message after the first primary message, but it is not
+     * guaranteed. Even if it can be written, other implementations
+     * may not be able to read NDEF messages after the primary message.
+     * It is recommended to use additional NDEF records instead.
+     *
+     * @throws IOException
+     */
+    public void writeExtraNdefMessage(int i, NdefMessage msg) throws IOException, FormatException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Set the CC field to indicate this tag is read-only
+     * @throws IOException
+     */
+    public boolean makeReadonly() throws IOException {
+        try {
+            int errorCode = mTagService.makeReadOnly(mTag.getServiceHandle());
+            switch (errorCode) {
+                case ErrorCodes.SUCCESS:
+                    return true;
+                case ErrorCodes.ERROR_IO:
+                    throw new IOException();
+                case ErrorCodes.ERROR_INVALID_PARAM:
+                    return false;
+                default:
+                    // Should not happen
+                    throw new IOException();
+            }
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return false;
+        }
+    }
+
+    /**
+     * Attempt to use tag specific technology to really make
+     * the tag read-only
+     * For NFC Forum Type 1 and 2 only.
+     */
+    public void makeLowLevelReadonly() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/core/java/android/nfc/technology/NfcA.java b/core/java/android/nfc/technology/NfcA.java
new file mode 100644
index 0000000..ef46762
--- /dev/null
+++ b/core/java/android/nfc/technology/NfcA.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.technology;
+
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+/**
+ * A low-level connection to a {@link Tag} using the NFC-A technology, also known as
+ * ISO1443-3A.
+ *
+ * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}.
+ * Use this class to send and receive data with {@link #transceive transceive()}.
+ *
+ * <p>Applications must implement their own protocol stack on top of
+ * {@link #transceive transceive()}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
+ */
+public final class NfcA extends BasicTagTechnology {
+    /** @hide */
+    public static final String EXTRA_SAK = "sak";
+    /** @hide */
+    public static final String EXTRA_ATQA = "atqa";
+
+    private short mSak;
+    private byte[] mAtqa;
+
+    public NfcA(NfcAdapter adapter, Tag tag, Bundle extras) throws RemoteException {
+        super(adapter, tag, TagTechnology.NFC_A);
+        mSak = extras.getShort(EXTRA_SAK);
+        mAtqa = extras.getByteArray(EXTRA_ATQA);
+    }
+
+    /**
+     * Returns the ATQA/SENS_RES bytes discovered at tag discovery.
+     */
+    public byte[] getAtqa() {
+        return mAtqa;
+    }
+
+    /**
+     * Returns the SAK/SEL_RES discovered at tag discovery.
+     */
+    public short getSak() {
+        return mSak;
+    }
+}
diff --git a/core/java/android/nfc/technology/NfcB.java b/core/java/android/nfc/technology/NfcB.java
new file mode 100644
index 0000000..64cb08a
--- /dev/null
+++ b/core/java/android/nfc/technology/NfcB.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.technology;
+
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+/**
+ * A low-level connection to a {@link Tag} using the NFC-B technology, also known as
+ * ISO1443-3B.
+ *
+ * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}.
+ * Use this class to send and receive data with {@link #transceive transceive()}.
+ *
+ * <p>Applications must implement their own protocol stack on top of
+ * {@link #transceive transceive()}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
+ */
+public final class NfcB extends BasicTagTechnology {
+    /** @hide */
+    public static final String EXTRA_ATQB = "atqb";
+
+    private byte[] mAtqb;
+
+    public NfcB(NfcAdapter adapter, Tag tag, Bundle extras)
+            throws RemoteException {
+        super(adapter, tag, TagTechnology.NFC_B);
+    }
+
+    /**
+     * Returns the ATQB/SENSB_RES bytes discovered at tag discovery.
+     */
+    public byte[] getAtqb() {
+        return mAtqb;
+    }
+}
diff --git a/core/java/android/nfc/technology/NfcF.java b/core/java/android/nfc/technology/NfcF.java
new file mode 100644
index 0000000..6741ac8
--- /dev/null
+++ b/core/java/android/nfc/technology/NfcF.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.technology;
+
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+/**
+ * A low-level connection to a {@link Tag} using the NFC-F technology, also known as
+ * JIS6319-4.
+ *
+ * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}.
+ * Use this class to send and receive data with {@link #transceive transceive()}.
+ *
+ * <p>Applications must implement their own protocol stack on top of
+ * {@link #transceive transceive()}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
+ */
+public final class NfcF extends BasicTagTechnology {
+    /** @hide */
+    public static final String EXTRA_SC = "systemcode";
+    /** @hide */
+    public static final String EXTRA_PMM = "pmm";
+
+    private byte[] mSystemCode = null;
+    private byte[] mManufacturer = null;
+
+    public NfcF(NfcAdapter adapter, Tag tag, Bundle extras)
+            throws RemoteException {
+        super(adapter, tag, TagTechnology.NFC_F);
+        if (extras != null) {
+            mSystemCode = extras.getByteArray(EXTRA_SC);
+            mManufacturer = extras.getByteArray(EXTRA_PMM);
+        }
+    }
+
+    public byte[] getSystemCode() {
+      return mSystemCode;
+    }
+
+    public byte[] getManufacturer() {
+      return mManufacturer;
+    }
+}
diff --git a/core/java/android/nfc/technology/NfcV.java b/core/java/android/nfc/technology/NfcV.java
new file mode 100644
index 0000000..9b6a16a
--- /dev/null
+++ b/core/java/android/nfc/technology/NfcV.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.technology;
+
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+/**
+ * A low-level connection to a {@link Tag} using the NFC-V technology, also known as
+ * ISO15693.
+ *
+ * <p>You can acquire this kind of connection with {@link Tag#getTechnology(int)}.
+ * Use this class to send and receive data with {@link #transceive transceive()}.
+ *
+ * <p>Applications must implement their own protocol stack on top of
+ * {@link #transceive transceive()}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
+ */
+public final class NfcV extends BasicTagTechnology {
+    public NfcV(NfcAdapter adapter, Tag tag, Bundle extras)
+            throws RemoteException {
+        super(adapter, tag, TagTechnology.NFC_V);
+    }
+}
diff --git a/core/java/android/nfc/technology/TagTechnology.java b/core/java/android/nfc/technology/TagTechnology.java
new file mode 100644
index 0000000..2b4f74c
--- /dev/null
+++ b/core/java/android/nfc/technology/TagTechnology.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.technology;
+
+import android.nfc.Tag;
+
+import java.io.IOException;
+
+public interface TagTechnology {
+    /**
+     * This object is an instance of {@link NfcA}
+     */
+    public static final int NFC_A = 1;
+
+    /**
+     * This object is an instance of {@link NfcB}
+     */
+    public static final int NFC_B = 2;
+
+    /**
+     * This object is an instance of {@link IsoDep}
+     */
+    public static final int ISO_DEP = 3;
+
+    /**
+     * This object is an instance of {@link NfcF}
+     */
+    public static final int NFC_F = 11;
+
+    /**
+     * This object is an instance of {@link NfcV}
+     */
+    public static final int NFC_V = 21;
+
+    /**
+     * This object is an instance of {@link Ndef}
+     */
+    public static final int TYPE_1 = 101;
+
+    /**
+     * This object is an instance of {@link Ndef}
+     */
+    public static final int TYPE_2 = 102;
+
+    /**
+     * This object is an instance of {@link Ndef}
+     */
+    public static final int TYPE_3 = 103;
+
+    /**
+     * This object is an instance of {@link Ndef}
+     */
+    public static final int TYPE_4 = 104;
+
+    /**
+     * This object is an instance of {@link MifareClassic}
+     */
+    public static final int MIFARE_CLASSIC = 200;
+
+    /**
+     * A Mifare Classic tag with NDEF data
+     */
+    public static final int MIFARE_CLASSIC_NDEF = 201;
+
+    /**
+     * This object is an instance of {@link MifareUltralight}
+     */
+    public static final int MIFARE_ULTRALIGHT = 202;
+
+    /**
+     * A Mifare DESFire tag
+     */
+    public static final int MIFARE_DESFIRE = 203;
+
+    /**
+     * Returns the technology type for this tag connection.
+     */
+    public int getTechnologyId();
+
+    /**
+     * Get the backing tag object.
+     */
+    public Tag getTag();
+
+    /**
+     * @throws IOException
+     */
+    public void connect() throws IOException;
+
+    /**
+     * Non-blocking. Immediately causes all blocking calls
+     * to throw IOException.
+     */
+    public void close();
+}
diff --git a/core/java/android/nfc/technology/package.html b/core/java/android/nfc/technology/package.html
new file mode 100644
index 0000000..26b8a32
--- /dev/null
+++ b/core/java/android/nfc/technology/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+{@hide}
+</BODY>
+</HTML>
\ No newline at end of file
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 1647540..0cbd51b 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -621,14 +621,11 @@
     }
 
     /**
-     * Tell the cookie store that this is a good time to flush cookies to flash.
+     * Package level api, called from CookieSyncManager
      *
-     * This should be called when the app is paused. Note that this method only
-     * acts as a hint, and may not have any effect. Flushing is asynchronous.
-     *
-     * @hide pending API council approval.
+     * Flush all cookies managed by the Chrome HTTP stack to flash.
      */
-    public void flushCookieStore() {
+    void flushCookieStore() {
         if (useChromiumHttpStack()) {
             nativeFlushCookieStore();
         }
diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java
index 071eb8c..8393980 100644
--- a/core/java/android/webkit/CookieSyncManager.java
+++ b/core/java/android/webkit/CookieSyncManager.java
@@ -172,17 +172,19 @@
             Log.v(LOGTAG, "CookieSyncManager::syncFromRamToFlash STARTS");
         }
 
-        if (!CookieManager.getInstance().acceptCookie()) {
+        CookieManager manager = CookieManager.getInstance();
+
+        if (!manager.acceptCookie()) {
             return;
         }
 
-        ArrayList<Cookie> cookieList = CookieManager.getInstance()
-                .getUpdatedCookiesSince(mLastUpdate);
+        manager.flushCookieStore();
+
+        ArrayList<Cookie> cookieList = manager.getUpdatedCookiesSince(mLastUpdate);
         mLastUpdate = System.currentTimeMillis();
         syncFromRamToFlash(cookieList);
 
-        ArrayList<Cookie> lruList =
-                CookieManager.getInstance().deleteLRUDomain();
+        ArrayList<Cookie> lruList = manager.deleteLRUDomain();
         syncFromRamToFlash(lruList);
 
         if (DebugFlags.COOKIE_SYNC_MANAGER) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ced8e9b..a86610dd 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7797,6 +7797,17 @@
         return new DragThumbnailBuilder(thumbnail);
     }
 
+    private static class DragLocalState {
+        public TextView sourceTextView;
+        public int start, end;
+
+        public DragLocalState(TextView sourceTextView, int start, int end) {
+            this.sourceTextView = sourceTextView;
+            this.start = start;
+            this.end = end;
+        }
+    }
+
     @Override
     public boolean performLongClick() {
         if (super.performLongClick()) {
@@ -7822,8 +7833,8 @@
                 final int end = getSelectionEnd();
                 CharSequence selectedText = mTransformed.subSequence(start, end);
                 ClipData data = ClipData.newPlainText(null, null, selectedText);
-                startDrag(data, getTextThumbnailBuilder(selectedText), false, null);
-                mDragSourcePositions = packRangeInLong(start, end);
+                DragLocalState localState = new DragLocalState(this, start, end);
+                startDrag(data, getTextThumbnailBuilder(selectedText), false, localState);
                 stopSelectionActionMode();
             } else {
                 selectCurrentWord();
@@ -8209,6 +8220,10 @@
             positionAtCursor();
             coords[0] += mPositionX;
             coords[1] += mPositionY;
+            coords[0] = Math.max(0, coords[0]);
+            final int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
+            coords[0] = Math.min(screenWidth - mContainer.getContentView().getMeasuredWidth(),
+                    coords[0]);
             mContainer.showAtLocation(TextView.this, Gravity.NO_GRAVITY, coords[0], coords[1]);
         }
 
@@ -8352,7 +8367,7 @@
             mContainerPositionY = coords[1] + mPositionY;
             mContainer.showAtLocation(TextView.this, 0, mContainerPositionX, mContainerPositionY);
 
-            // Hide paste view when handle is moved.
+            // Hide paste view when handle is moved on screen.
             if (mPastePopupWindow != null) {
                 mPastePopupWindow.hide();
             }
@@ -8491,12 +8506,6 @@
                 mIsDragging = true;
                 if (mHasPastePopupWindow) {
                     mTouchTimer = SystemClock.uptimeMillis();
-                    if (mPastePopupWindow != null && mPastePopupWindow.isShowing()) {
-                        // Tapping on the handle again dismisses the displayed paste view,
-                        mPastePopupWindow.hide();
-                        // and makes sure the action up does not display the paste view.
-                        mTouchTimer = 0;
-                    }
                 }
                 break;
             }
@@ -8511,10 +8520,7 @@
                 break;
             }
             case MotionEvent.ACTION_UP:
-                if (mPastePopupWindow != null) {
-                    // Will show the paste popup after a delay.
-                    mController.show();
-                    /* TEMP USER TEST: Display Paste as soon as handle is draggged
+                if (mHasPastePopupWindow) {
                     long delay = SystemClock.uptimeMillis() - mTouchTimer;
                     if (delay < ViewConfiguration.getTapTimeout()) {
                         final float touchOffsetX = ev.getRawX() - mPositionX;
@@ -8526,9 +8532,16 @@
                         final int doubleTapSlop = viewConfiguration.getScaledDoubleTapSlop();
                         final int slopSquared = doubleTapSlop * doubleTapSlop;
                         if (distanceSquared < slopSquared) {
-                            showPastePopupWindow();
+                            if (mPastePopupWindow != null && mPastePopupWindow.isShowing()) {
+                                // Tapping on the handle dismisses the displayed paste view,
+                                mPastePopupWindow.hide();
+                            } else {
+                                ((InsertionPointCursorController) mController).show(0);
+                            }
                         }
-                    }*/
+                    } else {
+                        mController.show();
+                    }
                 }
                 mIsDragging = false;
                 break;
@@ -8975,9 +8988,6 @@
                 return true;
 
             case DragEvent.ACTION_DRAG_ENDED:
-                mDragSourcePositions = -1;
-                return true;
-
             case DragEvent.ACTION_DRAG_EXITED:
             default:
                 return true;
@@ -8995,10 +9005,16 @@
 
         final int offset = getOffset((int) event.getX(), (int) event.getY());
 
-        if (mDragSourcePositions != -1) {
-            final int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
-            final int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
-            if (offset >= dragSourceStart && offset < dragSourceEnd) {
+        Object localState = event.getLocalState();
+        DragLocalState dragLocalState = null;
+        if (localState instanceof DragLocalState) {
+            dragLocalState = (DragLocalState) localState;
+        }
+        boolean dragDropIntoItself = dragLocalState != null &&
+                dragLocalState.sourceTextView == this;
+
+        if (dragDropIntoItself) {
+            if (offset >= dragLocalState.start && offset < dragLocalState.end) {
                 // A drop inside the original selection discards the drop.
                 return;
             }
@@ -9012,9 +9028,9 @@
         Selection.setSelection((Spannable) mText, max);
         ((Editable) mText).replace(min, max, content);
 
-        if (mDragSourcePositions != -1) {
-            int dragSourceStart = extractRangeStartFromLong(mDragSourcePositions);
-            int dragSourceEnd = extractRangeEndFromLong(mDragSourcePositions);
+        if (dragDropIntoItself) {
+            int dragSourceStart = dragLocalState.start;
+            int dragSourceEnd = dragLocalState.end;
             if (max <= dragSourceStart) {
                 // Inserting text before selection has shifted positions
                 final int shift = mText.length() - originalLength;
@@ -9198,8 +9214,6 @@
     private InputFilter[] mFilters = NO_FILTERS;
     private static final Spanned EMPTY_SPANNED = new SpannedString("");
     private static int DRAG_THUMBNAIL_MAX_TEXT_LENGTH = 20;
-    // A packed range containing the drag source if it occured in that TextView. -1 otherwise.
-    private long mDragSourcePositions = -1;
     // System wide time for last cut or copy action.
     private static long sLastCutOrCopyTime;
 }
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index 5d1f632..aee1626 100755
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -29,5 +29,6 @@
                 in ParcelFileDescriptor outStream);
     PackageInfoLite getMinimalPackageInfo(in Uri fileUri, int flags);
     boolean checkFreeStorage(boolean external, in Uri fileUri);
-    ObbInfo getObbInfo(String filename);
+    ObbInfo getObbInfo(in String filename);
+    long calculateDirectorySize(in String directory);
 }
diff --git a/core/res/res/layout/text_edit_no_paste_window.xml b/core/res/res/layout/text_edit_no_paste_window.xml
index d409d97..fa50275 100644
--- a/core/res/res/layout/text_edit_no_paste_window.xml
+++ b/core/res/res/layout/text_edit_no_paste_window.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
+<!-- Copyright (C) 2010 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,34 +14,25 @@
      limitations under the License.
 -->
 
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:background="@android:drawable/text_edit_paste_window">
-
-    <ImageView android:id="@+id/paste_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:paddingLeft="16dip"
-        android:paddingRight="8dip"
-        android:paddingTop="8dip"
-        android:paddingBottom="8dip"
-        android:layout_centerVertical="true"
-        android:background="@android:drawable/ic_menu_paste_dark"
-    />
+    android:layout_height="wrap_content">
 
     <TextView android:id="@+id/title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:paddingLeft="0dip"
+        android:paddingLeft="16dip"
         android:paddingRight="16dip"
         android:paddingTop="8dip"
         android:paddingBottom="8dip"
-        android:layout_centerVertical="true"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:text="@android:string/pasteDisabled"
+        android:drawableLeft="@android:drawable/ic_menu_paste_dark"
+        android:drawablePadding="8dip"
+        android:gravity="center"
+        android:textAppearance="?android:attr/textAppearanceMediumInverse"
         android:textColor="@android:color/dim_foreground_dark_inverse_disabled"
-        android:layout_toRightOf="@id/paste_icon"
+        android:background="@android:drawable/text_edit_paste_window"
+        android:text="@android:string/pasteDisabled"
+        android:layout_marginBottom="12dip"
     />
 
-</RelativeLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/text_edit_paste_window.xml b/core/res/res/layout/text_edit_paste_window.xml
index d153365..575b98e 100644
--- a/core/res/res/layout/text_edit_paste_window.xml
+++ b/core/res/res/layout/text_edit_paste_window.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
+<!-- Copyright (C) 2010 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,34 +14,25 @@
      limitations under the License.
 -->
 
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:background="@android:drawable/text_edit_paste_window">
-
-    <ImageView android:id="@+id/paste_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:paddingLeft="16dip"
-        android:paddingRight="8dip"
-        android:paddingTop="8dip"
-        android:paddingBottom="8dip"
-        android:layout_centerVertical="true"
-        android:background="@android:drawable/ic_menu_paste_light"
-    />
+    android:layout_height="wrap_content">
 
     <TextView android:id="@+id/title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:paddingLeft="0dip"
+        android:paddingLeft="16dip"
         android:paddingRight="16dip"
         android:paddingTop="8dip"
         android:paddingBottom="8dip"
-        android:layout_centerVertical="true"
+        android:drawableLeft="@android:drawable/ic_menu_paste_light"
+        android:drawablePadding="8dip"
+        android:gravity="center"
         android:textAppearance="?android:attr/textAppearanceMediumInverse"
-        android:text="@android:string/paste"
         android:textColor="@android:color/black"
-        android:layout_toRightOf="@id/paste_icon"
+        android:background="@android:drawable/text_edit_paste_window"
+        android:text="@android:string/paste"
+        android:layout_marginBottom="12dip"
     />
 
-</RelativeLayout>
+</LinearLayout>
diff --git a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java
index 394b9f2..887b032 100644
--- a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java
+++ b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java
@@ -16,30 +16,42 @@
 
 package android.accounts;
 
-import android.test.AndroidTestCase;
-import android.test.RenamingDelegatingContext;
-import android.test.IsolatedContext;
-import android.test.mock.MockContext;
-import android.test.mock.MockContentResolver;
-import android.content.*;
-import android.accounts.Account;
-import android.accounts.AccountManagerService;
+import android.app.Notification;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.RegisteredServicesCache.ServiceInfo;
+import android.content.pm.RegisteredServicesCacheListener;
 import android.os.Bundle;
+import android.os.Handler;
+import android.test.AndroidTestCase;
+import android.test.IsolatedContext;
+import android.test.RenamingDelegatingContext;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockContext;
+import android.test.mock.MockPackageManager;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Comparator;
 
 public class AccountManagerServiceTest extends AndroidTestCase {
+    private AccountManagerService mAms;
+
     @Override
     protected void setUp() throws Exception {
         final String filenamePrefix = "test.";
         MockContentResolver resolver = new MockContentResolver();
         RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
-                new MockContext(), // The context that most methods are delegated to
+                new MyMockContext(), // The context that most methods are delegated to
                 getContext(), // The context that file methods are delegated to
                 filenamePrefix);
         Context context = new IsolatedContext(resolver, targetContextWrapper);
         setContext(context);
+        mAms = new MyAccountManagerService(getContext(),
+                new MyMockPackageManager(), new MockAccountAuthenticatorCache());
     }
 
     public class AccountSorter implements Comparator<Account> {
@@ -54,21 +66,20 @@
     }
 
     public void testCheckAddAccount() throws Exception {
-        AccountManagerService ams = new AccountManagerService(getContext());
         Account a11 = new Account("account1", "type1");
         Account a21 = new Account("account2", "type1");
         Account a31 = new Account("account3", "type1");
         Account a12 = new Account("account1", "type2");
         Account a22 = new Account("account2", "type2");
         Account a32 = new Account("account3", "type2");
-        ams.addAccount(a11, "p11", null);
-        ams.addAccount(a12, "p12", null);
-        ams.addAccount(a21, "p21", null);
-        ams.addAccount(a22, "p22", null);
-        ams.addAccount(a31, "p31", null);
-        ams.addAccount(a32, "p32", null);
+        mAms.addAccount(a11, "p11", null);
+        mAms.addAccount(a12, "p12", null);
+        mAms.addAccount(a21, "p21", null);
+        mAms.addAccount(a22, "p22", null);
+        mAms.addAccount(a31, "p31", null);
+        mAms.addAccount(a32, "p32", null);
 
-        Account[] accounts = ams.getAccounts(null);
+        Account[] accounts = mAms.getAccounts(null);
         Arrays.sort(accounts, new AccountSorter());
         assertEquals(6, accounts.length);
         assertEquals(a11, accounts[0]);
@@ -78,16 +89,16 @@
         assertEquals(a22, accounts[4]);
         assertEquals(a32, accounts[5]);
 
-        accounts = ams.getAccountsByType("type1" );
+        accounts = mAms.getAccounts("type1" );
         Arrays.sort(accounts, new AccountSorter());
         assertEquals(3, accounts.length);
         assertEquals(a11, accounts[0]);
         assertEquals(a21, accounts[1]);
         assertEquals(a31, accounts[2]);
 
-        ams.removeAccount(null, a21);
+        mAms.removeAccount(a21);
 
-        accounts = ams.getAccountsByType("type1" );
+        accounts = mAms.getAccounts("type1" );
         Arrays.sort(accounts, new AccountSorter());
         assertEquals(2, accounts.length);
         assertEquals(a11, accounts[0]);
@@ -95,23 +106,21 @@
     }
 
     public void testPasswords() throws Exception {
-        AccountManagerService ams = new AccountManagerService(getContext());
         Account a11 = new Account("account1", "type1");
         Account a12 = new Account("account1", "type2");
-        ams.addAccount(a11, "p11", null);
-        ams.addAccount(a12, "p12", null);
+        mAms.addAccount(a11, "p11", null);
+        mAms.addAccount(a12, "p12", null);
 
-        assertEquals("p11", ams.getPassword(a11));
-        assertEquals("p12", ams.getPassword(a12));
+        assertEquals("p11", mAms.getPassword(a11));
+        assertEquals("p12", mAms.getPassword(a12));
 
-        ams.setPassword(a11, "p11b");
+        mAms.setPassword(a11, "p11b");
 
-        assertEquals("p11b", ams.getPassword(a11));
-        assertEquals("p12", ams.getPassword(a12));
+        assertEquals("p11b", mAms.getPassword(a11));
+        assertEquals("p12", mAms.getPassword(a12));
     }
 
     public void testUserdata() throws Exception {
-        AccountManagerService ams = new AccountManagerService(getContext());
         Account a11 = new Account("account1", "type1");
         Bundle u11 = new Bundle();
         u11.putString("a", "a_a11");
@@ -122,57 +131,119 @@
         u12.putString("a", "a_a12");
         u12.putString("b", "b_a12");
         u12.putString("c", "c_a12");
-        ams.addAccount(a11, "p11", u11);
-        ams.addAccount(a12, "p12", u12);
+        mAms.addAccount(a11, "p11", u11);
+        mAms.addAccount(a12, "p12", u12);
 
-        assertEquals("a_a11", ams.getUserData(a11, "a"));
-        assertEquals("b_a11", ams.getUserData(a11, "b"));
-        assertEquals("c_a11", ams.getUserData(a11, "c"));
-        assertEquals("a_a12", ams.getUserData(a12, "a"));
-        assertEquals("b_a12", ams.getUserData(a12, "b"));
-        assertEquals("c_a12", ams.getUserData(a12, "c"));
+        assertEquals("a_a11", mAms.getUserData(a11, "a"));
+        assertEquals("b_a11", mAms.getUserData(a11, "b"));
+        assertEquals("c_a11", mAms.getUserData(a11, "c"));
+        assertEquals("a_a12", mAms.getUserData(a12, "a"));
+        assertEquals("b_a12", mAms.getUserData(a12, "b"));
+        assertEquals("c_a12", mAms.getUserData(a12, "c"));
 
-        ams.setUserData(a11, "b", "b_a11b");
+        mAms.setUserData(a11, "b", "b_a11b");
+        mAms.setUserData(a12, "c", null);
 
-        assertEquals("a_a11", ams.getUserData(a11, "a"));
-        assertEquals("b_a11b", ams.getUserData(a11, "b"));
-        assertEquals("c_a11", ams.getUserData(a11, "c"));
-        assertEquals("a_a12", ams.getUserData(a12, "a"));
-        assertEquals("b_a12", ams.getUserData(a12, "b"));
-        assertEquals("c_a12", ams.getUserData(a12, "c"));
+        assertEquals("a_a11", mAms.getUserData(a11, "a"));
+        assertEquals("b_a11b", mAms.getUserData(a11, "b"));
+        assertEquals("c_a11", mAms.getUserData(a11, "c"));
+        assertEquals("a_a12", mAms.getUserData(a12, "a"));
+        assertEquals("b_a12", mAms.getUserData(a12, "b"));
+        assertNull(mAms.getUserData(a12, "c"));
     }
 
     public void testAuthtokens() throws Exception {
-        AccountManagerService ams = new AccountManagerService(getContext());
         Account a11 = new Account("account1", "type1");
         Account a12 = new Account("account1", "type2");
-        ams.addAccount(a11, "p11", null);
-        ams.addAccount(a12, "p12", null);
+        mAms.addAccount(a11, "p11", null);
+        mAms.addAccount(a12, "p12", null);
 
-        ams.setAuthToken(a11, "att1", "a11_att1");
-        ams.setAuthToken(a11, "att2", "a11_att2");
-        ams.setAuthToken(a11, "att3", "a11_att3");
-        ams.setAuthToken(a12, "att1", "a12_att1");
-        ams.setAuthToken(a12, "att2", "a12_att2");
-        ams.setAuthToken(a12, "att3", "a12_att3");
+        mAms.setAuthToken(a11, "att1", "a11_att1");
+        mAms.setAuthToken(a11, "att2", "a11_att2");
+        mAms.setAuthToken(a11, "att3", "a11_att3");
+        mAms.setAuthToken(a12, "att1", "a12_att1");
+        mAms.setAuthToken(a12, "att2", "a12_att2");
+        mAms.setAuthToken(a12, "att3", "a12_att3");
 
-        assertEquals("a11_att1", ams.peekAuthToken(a11, "att1"));
-        assertEquals("a11_att2", ams.peekAuthToken(a11, "att2"));
-        assertEquals("a11_att3", ams.peekAuthToken(a11, "att3"));
-        assertEquals("a12_att1", ams.peekAuthToken(a12, "att1"));
-        assertEquals("a12_att2", ams.peekAuthToken(a12, "att2"));
-        assertEquals("a12_att3", ams.peekAuthToken(a12, "att3"));
+        assertEquals("a11_att1", mAms.peekAuthToken(a11, "att1"));
+        assertEquals("a11_att2", mAms.peekAuthToken(a11, "att2"));
+        assertEquals("a11_att3", mAms.peekAuthToken(a11, "att3"));
+        assertEquals("a12_att1", mAms.peekAuthToken(a12, "att1"));
+        assertEquals("a12_att2", mAms.peekAuthToken(a12, "att2"));
+        assertEquals("a12_att3", mAms.peekAuthToken(a12, "att3"));
 
-        ams.setAuthToken(a11, "att3", "a11_att3b");
-        ams.invalidateAuthToken(a12.type, "a12_att2");
+        mAms.setAuthToken(a11, "att3", "a11_att3b");
+        mAms.invalidateAuthToken(a12.type, "a12_att2");
 
-        assertEquals("a11_att1", ams.peekAuthToken(a11, "att1"));
-        assertEquals("a11_att2", ams.peekAuthToken(a11, "att2"));
-        assertEquals("a11_att3b", ams.peekAuthToken(a11, "att3"));
-        assertEquals("a12_att1", ams.peekAuthToken(a12, "att1"));
-        assertNull(ams.peekAuthToken(a12, "att2"));
-        assertEquals("a12_att3", ams.peekAuthToken(a12, "att3"));
+        assertEquals("a11_att1", mAms.peekAuthToken(a11, "att1"));
+        assertEquals("a11_att2", mAms.peekAuthToken(a11, "att2"));
+        assertEquals("a11_att3b", mAms.peekAuthToken(a11, "att3"));
+        assertEquals("a12_att1", mAms.peekAuthToken(a12, "att1"));
+        assertNull(mAms.peekAuthToken(a12, "att2"));
+        assertEquals("a12_att3", mAms.peekAuthToken(a12, "att3"));
 
-        assertNull(ams.readAuthTokenFromDatabase(a12, "att2"));
+        assertNull(mAms.peekAuthToken(a12, "att2"));
+    }
+
+    static public class MockAccountAuthenticatorCache implements IAccountAuthenticatorCache {
+        private ArrayList<ServiceInfo<AuthenticatorDescription>> mServices;
+
+        public MockAccountAuthenticatorCache() {
+            mServices = new ArrayList<ServiceInfo<AuthenticatorDescription>>();
+            AuthenticatorDescription d1 = new AuthenticatorDescription("type1", "p1", 0, 0, 0, 0);
+            AuthenticatorDescription d2 = new AuthenticatorDescription("type2", "p2", 0, 0, 0, 0);
+            mServices.add(new ServiceInfo<AuthenticatorDescription>(d1, null, 0));
+            mServices.add(new ServiceInfo<AuthenticatorDescription>(d2, null, 0));
+        }
+
+        public ServiceInfo<AuthenticatorDescription> getServiceInfo(AuthenticatorDescription type) {
+            for (ServiceInfo<AuthenticatorDescription> service : mServices) {
+                if (service.type.equals(type)) {
+                    return service;
+                }
+            }
+            return null;
+        }
+
+        public Collection<ServiceInfo<AuthenticatorDescription>> getAllServices() {
+            return mServices;
+        }
+
+        public void dump(final FileDescriptor fd, final PrintWriter fout, final String[] args) {
+        }
+
+        public void setListener(
+                final RegisteredServicesCacheListener<AuthenticatorDescription> listener,
+                final Handler handler) {
+        }
+    }
+
+    static public class MyMockContext extends MockContext {
+        @Override
+        public int checkCallingOrSelfPermission(final String permission) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+    }
+
+    static public class MyMockPackageManager extends MockPackageManager {
+        @Override
+        public int checkSignatures(final int uid1, final int uid2) {
+            return PackageManager.SIGNATURE_MATCH;
+        }
+    }
+
+    static public class MyAccountManagerService extends AccountManagerService {
+        public MyAccountManagerService(Context context, PackageManager packageManager,
+                IAccountAuthenticatorCache authenticatorCache) {
+            super(context, packageManager, authenticatorCache);
+        }
+
+        @Override
+        protected void installNotification(final int notificationId, final Notification n) {
+        }
+
+        @Override
+        protected void cancelNotification(final int id) {
+        }
     }
 }
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index e6c9f93..794355b 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -215,6 +215,8 @@
     status_t checkFrameRate(const CameraParameters& params,
                     int32_t frameRate);
 
+    void releaseCamera();
+
     CameraSource(const CameraSource &);
     CameraSource &operator=(const CameraSource &);
 };
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh
index 7d1721c..24a36c1 100644
--- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh
@@ -21,7 +21,7 @@
 #define TRANSFORM_ROTATE 2
 #define TRANSFORM_SCALE 3
 
-typedef struct __attribute__((packed, aligned(4))) {
+typedef struct __attribute__((packed, aligned(4))) SgTransform {
     rs_matrix4x4 globalMat;
     rs_matrix4x4 localMat;
 
diff --git a/libs/rs/java/tests/src/com/android/rs/test/vector_array.rs b/libs/rs/java/tests/src/com/android/rs/test/vector_array.rs
index a71a062..49e38c1 100644
--- a/libs/rs/java/tests/src/com/android/rs/test/vector_array.rs
+++ b/libs/rs/java/tests/src/com/android/rs/test/vector_array.rs
@@ -14,7 +14,7 @@
 
 #include "shared.rsh"
 
-typedef struct {
+typedef struct float3Struct{
     float3 arr[2];
 } float3Struct;
 
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index cee92d2..f72d919 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1079,7 +1079,15 @@
                 mCamera, mCameraId, videoSize, mFrameRate,
                 mPreviewSurface, true /*storeMetaDataInVideoBuffers*/);
     }
-    CHECK(*cameraSource != NULL);
+    if (*cameraSource == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    if ((*cameraSource)->initCheck() != OK) {
+        (*cameraSource).clear();
+        *cameraSource = NULL;
+        return NO_INIT;
+    }
 
     // When frame rate is not set, the actual frame rate will be set to
     // the current frame rate being used.
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 8ebbe6c..2743a3a 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -22,7 +22,6 @@
 
 #include "include/ARTSPController.h"
 #include "include/AwesomePlayer.h"
-#include "include/LiveSource.h"
 #include "include/SoftwareRenderer.h"
 #include "include/NuCachedSource2.h"
 #include "include/ThrottledSource.h"
@@ -51,6 +50,7 @@
 #include <surfaceflinger/Surface.h>
 
 #include <media/stagefright/foundation/ALooper.h>
+#include "include/LiveSession.h"
 
 #define USE_SURFACE_ALLOC 1
 
@@ -634,6 +634,11 @@
         mRTSPController.clear();
     }
 
+    if (mLiveSession != NULL) {
+        mLiveSession->disconnect();
+        mLiveSession.clear();
+    }
+
     mRTPPusher.clear();
     mRTCPPusher.clear();
     mRTPSession.clear();
@@ -1659,16 +1664,23 @@
         String8 uri("http://");
         uri.append(mUri.string() + 11);
 
-        sp<LiveSource> liveSource = new LiveSource(uri.string());
+        if (mLooper == NULL) {
+            mLooper = new ALooper;
+            mLooper->setName("httplive");
+            mLooper->start();
+        }
 
-        mCachedSource = new NuCachedSource2(liveSource);
-        dataSource = mCachedSource;
+        mLiveSession = new LiveSession;
+        mLooper->registerHandler(mLiveSession);
+
+        mLiveSession->connect(uri.string());
+        dataSource = mLiveSession->getDataSource();
 
         sp<MediaExtractor> extractor =
             MediaExtractor::Create(dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
 
         static_cast<MPEG2TSExtractor *>(extractor.get())
-            ->setLiveSource(liveSource);
+            ->setLiveSession(mLiveSession);
 
         return setDataSource_l(extractor);
     } else if (!strncmp("rtsp://gtalk/", mUri.string(), 13)) {
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index d9ff723..ed9e865 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -130,13 +130,6 @@
     CameraSource *source = new CameraSource(camera, cameraId,
                     videoSize, frameRate, surface,
                     storeMetaDataInVideoBuffers);
-
-    if (source != NULL) {
-        if (source->initCheck() != OK) {
-            delete source;
-            return NULL;
-        }
-    }
     return source;
 }
 
@@ -293,6 +286,7 @@
     if (width != -1 && height != -1) {
         if (!isVideoSizeSupported(width, height, sizes)) {
             LOGE("Video dimension (%dx%d) is unsupported", width, height);
+            releaseCamera();
             return BAD_VALUE;
         }
         if (isSetVideoSizeSupportedByCamera) {
@@ -306,6 +300,7 @@
         // If one and only one of the width and height is -1
         // we reject such a request.
         LOGE("Requested video size (%dx%d) is not supported", width, height);
+        releaseCamera();
         return BAD_VALUE;
     } else {  // width == -1 && height == -1
         // Do not configure the camera.
@@ -323,6 +318,7 @@
         if (strstr(supportedFrameRates, buf) == NULL) {
             LOGE("Requested frame rate (%d) is not supported: %s",
                 frameRate, supportedFrameRates);
+            releaseCamera();
             return BAD_VALUE;
         }
 
@@ -561,6 +557,18 @@
     mCamera->stopRecording();
 }
 
+void CameraSource::releaseCamera() {
+    LOGV("releaseCamera");
+    if ((mCameraFlags & FLAGS_HOT_CAMERA) == 0) {
+        LOGV("Camera was cold when we started, stopping preview");
+        mCamera->stopPreview();
+    }
+    mCamera->unlock();
+    mCamera.clear();
+    mCamera = 0;
+    mCameraFlags = 0;
+}
+
 status_t CameraSource::stop() {
     LOGV("stop");
     Mutex::Autolock autoLock(mLock);
@@ -575,16 +583,7 @@
                 mFramesBeingEncoded.size());
         mFrameCompleteCondition.wait(mLock);
     }
-
-    LOGV("Disconnect camera");
-    if ((mCameraFlags & FLAGS_HOT_CAMERA) == 0) {
-        LOGV("Camera was cold when we started, stopping preview");
-        mCamera->stopPreview();
-    }
-    mCamera->unlock();
-    mCamera.clear();
-    mCamera = 0;
-    mCameraFlags = 0;
+    releaseCamera();
     IPCThreadState::self()->restoreCallingIdentity(token);
 
     if (mCollectStats) {
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 829ab20..110fb03 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -179,8 +179,7 @@
       mFinalStatus(OK),
       mLastAccessPos(0),
       mFetching(true),
-      mLastFetchTimeUs(-1),
-      mSuspended(false) {
+      mLastFetchTimeUs(-1) {
     mLooper->setName("NuCachedSource2");
     mLooper->registerHandler(mReflector);
     mLooper->start();
@@ -223,12 +222,6 @@
             break;
         }
 
-        case kWhatSuspend:
-        {
-            onSuspend();
-            break;
-        }
-
         default:
             TRESPASS();
     }
@@ -270,7 +263,6 @@
 
     bool keepAlive =
         !mFetching
-            && !mSuspended
             && mFinalStatus == OK
             && ALooper::GetNowUs() >= mLastFetchTimeUs + kKeepAliveIntervalUs;
 
@@ -287,7 +279,7 @@
             LOGI("Cache full, done prefetching for now");
             mFetching = false;
         }
-    } else if (!mSuspended) {
+    } else {
         Mutex::Autolock autoLock(mLock);
         restartPrefetcherIfNecessary_l();
     }
@@ -478,40 +470,6 @@
     return OK;
 }
 
-void NuCachedSource2::clearCacheAndResume() {
-    LOGV("clearCacheAndResume");
-
-    Mutex::Autolock autoLock(mLock);
-
-    CHECK(mSuspended);
-
-    mCacheOffset = 0;
-    mFinalStatus = OK;
-    mLastAccessPos = 0;
-    mLastFetchTimeUs = -1;
-
-    size_t totalSize = mCache->totalSize();
-    CHECK_EQ(mCache->releaseFromStart(totalSize), totalSize);
-
-    mFetching = true;
-    mSuspended = false;
-}
-
-void NuCachedSource2::suspend() {
-    (new AMessage(kWhatSuspend, mReflector->id()))->post();
-
-    while (!mSuspended) {
-        usleep(10000);
-    }
-}
-
-void NuCachedSource2::onSuspend() {
-    Mutex::Autolock autoLock(mLock);
-
-    mFetching = false;
-    mSuspended = true;
-}
-
 void NuCachedSource2::resumeFetchingIfNecessary() {
     Mutex::Autolock autoLock(mLock);
 
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 269b233..4ce7265 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -456,7 +456,7 @@
 bool NuHTTPDataSource::estimateBandwidth(int32_t *bandwidth_bps) {
     Mutex::Autolock autoLock(mLock);
 
-    if (mNumBandwidthHistoryItems < 10) {
+    if (mNumBandwidthHistoryItems < 2) {
         return false;
     }
 
diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk
index 3aabf5f..9225e41 100644
--- a/media/libstagefright/httplive/Android.mk
+++ b/media/libstagefright/httplive/Android.mk
@@ -2,9 +2,10 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:=       \
-        LiveSource.cpp  \
-        M3UParser.cpp   \
+LOCAL_SRC_FILES:=               \
+        LiveDataSource.cpp      \
+        LiveSession.cpp         \
+        M3UParser.cpp           \
 
 LOCAL_C_INCLUDES:= \
 	$(JNI_H_INCLUDE) \
diff --git a/media/libstagefright/httplive/LiveDataSource.cpp b/media/libstagefright/httplive/LiveDataSource.cpp
new file mode 100644
index 0000000..25e2902
--- /dev/null
+++ b/media/libstagefright/httplive/LiveDataSource.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "LiveDataSource"
+#include <utils/Log.h>
+
+#include "LiveDataSource.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#define SAVE_BACKUP     0
+
+namespace android {
+
+LiveDataSource::LiveDataSource()
+    : mOffset(0),
+      mFinalResult(OK),
+      mBackupFile(NULL) {
+#if SAVE_BACKUP
+    mBackupFile = fopen("/data/misc/backup.ts", "wb");
+    CHECK(mBackupFile != NULL);
+#endif
+}
+
+LiveDataSource::~LiveDataSource() {
+    if (mBackupFile != NULL) {
+        fclose(mBackupFile);
+        mBackupFile = NULL;
+    }
+}
+
+status_t LiveDataSource::initCheck() const {
+    return OK;
+}
+
+size_t LiveDataSource::countQueuedBuffers() {
+    Mutex::Autolock autoLock(mLock);
+
+    return mBufferQueue.size();
+}
+
+ssize_t LiveDataSource::readAt(off64_t offset, void *data, size_t size) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (offset != mOffset) {
+        LOGE("Attempt at reading non-sequentially from LiveDataSource.");
+        return -EPIPE;
+    }
+
+    size_t sizeDone = 0;
+
+    while (sizeDone < size) {
+        while (mBufferQueue.empty() && mFinalResult == OK) {
+            mCondition.wait(mLock);
+        }
+
+        if (mBufferQueue.empty()) {
+            if (sizeDone > 0) {
+                mOffset += sizeDone;
+                return sizeDone;
+            }
+
+            return mFinalResult;
+        }
+
+        sp<ABuffer> buffer = *mBufferQueue.begin();
+
+        size_t copy = size - sizeDone;
+
+        if (copy > buffer->size()) {
+            copy = buffer->size();
+        }
+
+        memcpy((uint8_t *)data + sizeDone, buffer->data(), copy);
+
+        sizeDone += copy;
+
+        buffer->setRange(buffer->offset() + copy, buffer->size() - copy);
+
+        if (buffer->size() == 0) {
+            mBufferQueue.erase(mBufferQueue.begin());
+        }
+    }
+
+    mOffset += sizeDone;
+
+    return sizeDone;
+}
+
+void LiveDataSource::queueBuffer(const sp<ABuffer> &buffer) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mFinalResult != OK) {
+        return;
+    }
+
+#if SAVE_BACKUP
+    if (mBackupFile != NULL) {
+        CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mBackupFile),
+                 buffer->size());
+    }
+#endif
+
+    mBufferQueue.push_back(buffer);
+    mCondition.broadcast();
+}
+
+void LiveDataSource::queueEOS(status_t finalResult) {
+    CHECK_NE(finalResult, (status_t)OK);
+
+    Mutex::Autolock autoLock(mLock);
+
+    mFinalResult = finalResult;
+    mCondition.broadcast();
+}
+
+void LiveDataSource::reset() {
+    Mutex::Autolock autoLock(mLock);
+
+    // XXX FIXME: If we've done a partial read and waiting for more buffers,
+    // we'll mix old and new data...
+
+    mFinalResult = OK;
+    mBufferQueue.clear();
+}
+
+}  // namespace android
diff --git a/media/libstagefright/httplive/LiveDataSource.h b/media/libstagefright/httplive/LiveDataSource.h
new file mode 100644
index 0000000..a489ec6
--- /dev/null
+++ b/media/libstagefright/httplive/LiveDataSource.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIVE_DATA_SOURCE_H_
+
+#define LIVE_DATA_SOURCE_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/DataSource.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+
+namespace android {
+
+struct ABuffer;
+
+struct LiveDataSource : public DataSource {
+    LiveDataSource();
+
+    virtual status_t initCheck() const;
+
+    virtual ssize_t readAt(off64_t offset, void *data, size_t size);
+
+    void queueBuffer(const sp<ABuffer> &buffer);
+    void queueEOS(status_t finalResult);
+    void reset();
+
+    size_t countQueuedBuffers();
+
+protected:
+    virtual ~LiveDataSource();
+
+private:
+    Mutex mLock;
+    Condition mCondition;
+
+    off64_t mOffset;
+    List<sp<ABuffer> > mBufferQueue;
+    status_t mFinalResult;
+
+    FILE *mBackupFile;
+
+    DISALLOW_EVIL_CONSTRUCTORS(LiveDataSource);
+};
+
+}  // namespace android
+
+#endif  // LIVE_DATA_SOURCE_H_
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
new file mode 100644
index 0000000..62567be
--- /dev/null
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -0,0 +1,646 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "LiveSession"
+#include <utils/Log.h>
+
+#include "include/LiveSession.h"
+
+#include "LiveDataSource.h"
+
+#include "include/M3UParser.h"
+#include "include/NuHTTPDataSource.h"
+
+#include <cutils/properties.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include <ctype.h>
+#include <openssl/aes.h>
+
+namespace android {
+
+const int64_t LiveSession::kMaxPlaylistAgeUs = 15000000ll;
+
+LiveSession::LiveSession()
+    : mDataSource(new LiveDataSource),
+      mHTTPDataSource(new NuHTTPDataSource),
+      mPrevBandwidthIndex(-1),
+      mLastPlaylistFetchTimeUs(-1),
+      mSeqNumber(-1),
+      mSeekTimeUs(-1),
+      mNumRetries(0),
+      mDurationUs(-1),
+      mSeekDone(false),
+      mMonitorQueueGeneration(0) {
+}
+
+LiveSession::~LiveSession() {
+}
+
+sp<DataSource> LiveSession::getDataSource() {
+    return mDataSource;
+}
+
+void LiveSession::connect(const char *url) {
+    sp<AMessage> msg = new AMessage(kWhatConnect, id());
+    msg->setString("url", url);
+    msg->post();
+}
+
+void LiveSession::disconnect() {
+    (new AMessage(kWhatDisconnect, id()))->post();
+}
+
+void LiveSession::seekTo(int64_t timeUs) {
+    Mutex::Autolock autoLock(mLock);
+    mSeekDone = false;
+
+    sp<AMessage> msg = new AMessage(kWhatSeek, id());
+    msg->setInt64("timeUs", timeUs);
+    msg->post();
+
+    while (!mSeekDone) {
+        mCondition.wait(mLock);
+    }
+}
+
+void LiveSession::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatConnect:
+            onConnect(msg);
+            break;
+
+        case kWhatDisconnect:
+            onDisconnect();
+            break;
+
+        case kWhatMonitorQueue:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+
+            if (generation != mMonitorQueueGeneration) {
+                // Stale event
+                break;
+            }
+
+            onMonitorQueue();
+            break;
+        }
+
+        case kWhatSeek:
+            onSeek(msg);
+            break;
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+// static
+int LiveSession::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) {
+    if (a->mBandwidth < b->mBandwidth) {
+        return -1;
+    } else if (a->mBandwidth == b->mBandwidth) {
+        return 0;
+    }
+
+    return 1;
+}
+
+void LiveSession::onConnect(const sp<AMessage> &msg) {
+    AString url;
+    CHECK(msg->findString("url", &url));
+
+    LOGI("onConnect '%s'", url.c_str());
+
+    mMasterURL = url;
+
+    sp<M3UParser> playlist = fetchPlaylist(url.c_str());
+    CHECK(playlist != NULL);
+
+    if (playlist->isVariantPlaylist()) {
+        for (size_t i = 0; i < playlist->size(); ++i) {
+            BandwidthItem item;
+
+            sp<AMessage> meta;
+            playlist->itemAt(i, &item.mURI, &meta);
+
+            unsigned long bandwidth;
+            CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth));
+
+            mBandwidthItems.push(item);
+        }
+
+        CHECK_GT(mBandwidthItems.size(), 0u);
+
+        mBandwidthItems.sort(SortByBandwidth);
+
+        if (mBandwidthItems.size() > 1) {
+            // XXX Remove the lowest bitrate stream for now...
+            mBandwidthItems.removeAt(0);
+        }
+    }
+
+    postMonitorQueue();
+}
+
+void LiveSession::onDisconnect() {
+    LOGI("onDisconnect");
+
+    mDataSource->queueEOS(ERROR_END_OF_STREAM);
+}
+
+status_t LiveSession::fetchFile(const char *url, sp<ABuffer> *out) {
+    *out = NULL;
+
+    sp<DataSource> source;
+
+    if (!strncasecmp(url, "file://", 7)) {
+        source = new FileSource(url + 7);
+    } else {
+        CHECK(!strncasecmp(url, "http://", 7));
+
+        status_t err = mHTTPDataSource->connect(url);
+
+        if (err != OK) {
+            return err;
+        }
+
+        source = mHTTPDataSource;
+    }
+
+    off64_t size;
+    status_t err = source->getSize(&size);
+
+    if (err != OK) {
+        size = 65536;
+    }
+
+    sp<ABuffer> buffer = new ABuffer(size);
+    buffer->setRange(0, 0);
+
+    for (;;) {
+        size_t bufferRemaining = buffer->capacity() - buffer->size();
+
+        if (bufferRemaining == 0) {
+            bufferRemaining = 32768;
+
+            LOGV("increasing download buffer to %d bytes",
+                 buffer->size() + bufferRemaining);
+
+            sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);
+            memcpy(copy->data(), buffer->data(), buffer->size());
+            copy->setRange(0, buffer->size());
+
+            buffer = copy;
+        }
+
+        ssize_t n = source->readAt(
+                buffer->size(), buffer->data() + buffer->size(),
+                bufferRemaining);
+
+        if (n < 0) {
+            return err;
+        }
+
+        if (n == 0) {
+            break;
+        }
+
+        buffer->setRange(0, buffer->size() + (size_t)n);
+    }
+
+    *out = buffer;
+
+    return OK;
+}
+
+sp<M3UParser> LiveSession::fetchPlaylist(const char *url) {
+    sp<ABuffer> buffer;
+    status_t err = fetchFile(url, &buffer);
+
+    if (err != OK) {
+        return NULL;
+    }
+
+    sp<M3UParser> playlist =
+        new M3UParser(url, buffer->data(), buffer->size());
+
+    if (playlist->initCheck() != OK) {
+        return NULL;
+    }
+
+    return playlist;
+}
+
+static double uniformRand() {
+    return (double)rand() / RAND_MAX;
+}
+
+size_t LiveSession::getBandwidthIndex() {
+    if (mBandwidthItems.size() == 0) {
+        return 0;
+    }
+
+#if 1
+    int32_t bandwidthBps;
+    if (mHTTPDataSource != NULL
+            && mHTTPDataSource->estimateBandwidth(&bandwidthBps)) {
+        LOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
+    } else {
+        LOGV("no bandwidth estimate.");
+        return 0;  // Pick the lowest bandwidth stream by default.
+    }
+
+    char value[PROPERTY_VALUE_MAX];
+    if (property_get("media.httplive.max-bw", value, NULL)) {
+        char *end;
+        long maxBw = strtoul(value, &end, 10);
+        if (end > value && *end == '\0') {
+            if (maxBw > 0 && bandwidthBps > maxBw) {
+                LOGV("bandwidth capped to %ld bps", maxBw);
+                bandwidthBps = maxBw;
+            }
+        }
+    }
+
+    // Consider only 80% of the available bandwidth usable.
+    bandwidthBps = (bandwidthBps * 8) / 10;
+
+    // Pick the highest bandwidth stream below or equal to estimated bandwidth.
+
+    size_t index = mBandwidthItems.size() - 1;
+    while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth
+                            > (size_t)bandwidthBps) {
+        --index;
+    }
+#elif 0
+    // Change bandwidth at random()
+    size_t index = uniformRand() * mBandwidthItems.size();
+#elif 0
+    // There's a 50% chance to stay on the current bandwidth and
+    // a 50% chance to switch to the next higher bandwidth (wrapping around
+    // to lowest)
+    const size_t kMinIndex = 0;
+
+    size_t index;
+    if (mPrevBandwidthIndex < 0) {
+        index = kMinIndex;
+    } else if (uniformRand() < 0.5) {
+        index = (size_t)mPrevBandwidthIndex;
+    } else {
+        index = mPrevBandwidthIndex + 1;
+        if (index == mBandwidthItems.size()) {
+            index = kMinIndex;
+        }
+    }
+#elif 0
+    // Pick the highest bandwidth stream below or equal to 1.2 Mbit/sec
+
+    size_t index = mBandwidthItems.size() - 1;
+    while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth > 1200000) {
+        --index;
+    }
+#else
+    size_t index = mBandwidthItems.size() - 1;  // Highest bandwidth stream
+#endif
+
+    return index;
+}
+
+void LiveSession::onDownloadNext() {
+    size_t bandwidthIndex = getBandwidthIndex();
+
+    int64_t nowUs = ALooper::GetNowUs();
+
+    if (mLastPlaylistFetchTimeUs < 0
+            || (ssize_t)bandwidthIndex != mPrevBandwidthIndex
+            || (!mPlaylist->isComplete()
+                && mLastPlaylistFetchTimeUs + kMaxPlaylistAgeUs <= nowUs)) {
+        AString url;
+        if (mBandwidthItems.size() > 0) {
+            url = mBandwidthItems.editItemAt(bandwidthIndex).mURI;
+        } else {
+            url = mMasterURL;
+        }
+
+        bool firstTime = (mPlaylist == NULL);
+
+        mPlaylist = fetchPlaylist(url.c_str());
+        CHECK(mPlaylist != NULL);
+
+        if (firstTime) {
+            Mutex::Autolock autoLock(mLock);
+
+            int32_t targetDuration;
+            if (!mPlaylist->isComplete()
+                    || !mPlaylist->meta()->findInt32(
+                    "target-duration", &targetDuration)) {
+                mDurationUs = -1;
+            } else {
+                mDurationUs = 1000000ll * targetDuration * mPlaylist->size();
+            }
+        }
+
+        mLastPlaylistFetchTimeUs = ALooper::GetNowUs();
+    }
+
+    int32_t firstSeqNumberInPlaylist;
+    if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32(
+                "media-sequence", &firstSeqNumberInPlaylist)) {
+        firstSeqNumberInPlaylist = 0;
+    }
+
+    bool explicitDiscontinuity = false;
+
+    if (mSeekTimeUs >= 0) {
+        int32_t targetDuration;
+        if (mPlaylist->isComplete() &&
+                mPlaylist->meta()->findInt32(
+                    "target-duration", &targetDuration)) {
+            int64_t seekTimeSecs = (mSeekTimeUs + 500000ll) / 1000000ll;
+            int64_t index = seekTimeSecs / targetDuration;
+
+            if (index >= 0 && index < mPlaylist->size()) {
+                mSeqNumber = firstSeqNumberInPlaylist + index;
+                mDataSource->reset();
+
+                explicitDiscontinuity = true;
+            }
+        }
+
+        mSeekTimeUs = -1;
+
+        Mutex::Autolock autoLock(mLock);
+        mSeekDone = true;
+        mCondition.broadcast();
+    }
+
+    if (mSeqNumber < 0) {
+        if (mPlaylist->isComplete()) {
+            mSeqNumber = firstSeqNumberInPlaylist;
+        } else {
+            mSeqNumber = firstSeqNumberInPlaylist + mPlaylist->size() / 2;
+        }
+    }
+
+    int32_t lastSeqNumberInPlaylist =
+        firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
+
+    if (mSeqNumber < firstSeqNumberInPlaylist
+            || mSeqNumber > lastSeqNumberInPlaylist) {
+        if (!mPlaylist->isComplete()
+                && mSeqNumber > lastSeqNumberInPlaylist
+                && mNumRetries < kMaxNumRetries) {
+            ++mNumRetries;
+
+            mLastPlaylistFetchTimeUs = -1;
+            postMonitorQueue(1000000ll);
+            return;
+        }
+
+        LOGE("Cannot find sequence number %d in playlist "
+             "(contains %d - %d)",
+             mSeqNumber, firstSeqNumberInPlaylist,
+             firstSeqNumberInPlaylist + mPlaylist->size() - 1);
+
+        mDataSource->queueEOS(ERROR_END_OF_STREAM);
+        return;
+    }
+
+    mNumRetries = 0;
+
+    AString uri;
+    sp<AMessage> itemMeta;
+    CHECK(mPlaylist->itemAt(
+                mSeqNumber - firstSeqNumberInPlaylist,
+                &uri,
+                &itemMeta));
+
+    int32_t val;
+    if (itemMeta->findInt32("discontinuity", &val) && val != 0) {
+        explicitDiscontinuity = true;
+    }
+
+    sp<ABuffer> buffer;
+    status_t err = fetchFile(uri.c_str(), &buffer);
+    CHECK_EQ(err, (status_t)OK);
+
+    CHECK_EQ((status_t)OK,
+             decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer));
+
+    if (buffer->size() == 0 || buffer->data()[0] != 0x47) {
+        // Not a transport stream???
+
+        LOGE("This doesn't look like a transport stream...");
+
+        mDataSource->queueEOS(ERROR_UNSUPPORTED);
+        return;
+    }
+
+    if (explicitDiscontinuity
+            || (mPrevBandwidthIndex >= 0
+                && (size_t)mPrevBandwidthIndex != bandwidthIndex)) {
+        // Signal discontinuity.
+
+        sp<ABuffer> tmp = new ABuffer(188);
+        memset(tmp->data(), 0, tmp->size());
+
+        mDataSource->queueBuffer(tmp);
+    }
+
+    mDataSource->queueBuffer(buffer);
+
+    mPrevBandwidthIndex = bandwidthIndex;
+    ++mSeqNumber;
+
+    postMonitorQueue();
+}
+
+void LiveSession::onMonitorQueue() {
+    if (mSeekTimeUs >= 0
+            || mDataSource->countQueuedBuffers() < kMaxNumQueuedFragments) {
+        onDownloadNext();
+    } else {
+        postMonitorQueue(1000000ll);
+    }
+}
+
+status_t LiveSession::decryptBuffer(
+        size_t playlistIndex, const sp<ABuffer> &buffer) {
+    sp<AMessage> itemMeta;
+    bool found = false;
+    AString method;
+
+    for (ssize_t i = playlistIndex; i >= 0; --i) {
+        AString uri;
+        CHECK(mPlaylist->itemAt(i, &uri, &itemMeta));
+
+        if (itemMeta->findString("cipher-method", &method)) {
+            found = true;
+            break;
+        }
+    }
+
+    if (!found) {
+        method = "NONE";
+    }
+
+    if (method == "NONE") {
+        return OK;
+    } else if (!(method == "AES-128")) {
+        LOGE("Unsupported cipher method '%s'", method.c_str());
+        return ERROR_UNSUPPORTED;
+    }
+
+    AString keyURI;
+    if (!itemMeta->findString("cipher-uri", &keyURI)) {
+        LOGE("Missing key uri");
+        return ERROR_MALFORMED;
+    }
+
+    ssize_t index = mAESKeyForURI.indexOfKey(keyURI);
+
+    sp<ABuffer> key;
+    if (index >= 0) {
+        key = mAESKeyForURI.valueAt(index);
+    } else {
+        key = new ABuffer(16);
+
+        sp<NuHTTPDataSource> keySource = new NuHTTPDataSource;
+        status_t err = keySource->connect(keyURI.c_str());
+
+        if (err == OK) {
+            size_t offset = 0;
+            while (offset < 16) {
+                ssize_t n = keySource->readAt(
+                        offset, key->data() + offset, 16 - offset);
+                if (n <= 0) {
+                    err = ERROR_IO;
+                    break;
+                }
+
+                offset += n;
+            }
+        }
+
+        if (err != OK) {
+            LOGE("failed to fetch cipher key from '%s'.", keyURI.c_str());
+            return ERROR_IO;
+        }
+
+        mAESKeyForURI.add(keyURI, key);
+    }
+
+    AES_KEY aes_key;
+    if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) {
+        LOGE("failed to set AES decryption key.");
+        return UNKNOWN_ERROR;
+    }
+
+    unsigned char aes_ivec[16];
+
+    AString iv;
+    if (itemMeta->findString("cipher-iv", &iv)) {
+        if ((!iv.startsWith("0x") && !iv.startsWith("0X"))
+                || iv.size() != 16 * 2 + 2) {
+            LOGE("malformed cipher IV '%s'.", iv.c_str());
+            return ERROR_MALFORMED;
+        }
+
+        memset(aes_ivec, 0, sizeof(aes_ivec));
+        for (size_t i = 0; i < 16; ++i) {
+            char c1 = tolower(iv.c_str()[2 + 2 * i]);
+            char c2 = tolower(iv.c_str()[3 + 2 * i]);
+            if (!isxdigit(c1) || !isxdigit(c2)) {
+                LOGE("malformed cipher IV '%s'.", iv.c_str());
+                return ERROR_MALFORMED;
+            }
+            uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
+            uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
+
+            aes_ivec[i] = nibble1 << 4 | nibble2;
+        }
+    } else {
+        memset(aes_ivec, 0, sizeof(aes_ivec));
+        aes_ivec[15] = mSeqNumber & 0xff;
+        aes_ivec[14] = (mSeqNumber >> 8) & 0xff;
+        aes_ivec[13] = (mSeqNumber >> 16) & 0xff;
+        aes_ivec[12] = (mSeqNumber >> 24) & 0xff;
+    }
+
+    AES_cbc_encrypt(
+            buffer->data(), buffer->data(), buffer->size(),
+            &aes_key, aes_ivec, AES_DECRYPT);
+
+    // hexdump(buffer->data(), buffer->size());
+
+    size_t n = buffer->size();
+    CHECK_GT(n, 0u);
+
+    size_t pad = buffer->data()[n - 1];
+
+    CHECK_GT(pad, 0u);
+    CHECK_LE(pad, 16u);
+    CHECK_GE((size_t)n, pad);
+    for (size_t i = 0; i < pad; ++i) {
+        CHECK_EQ((unsigned)buffer->data()[n - 1 - i], pad);
+    }
+
+    n -= pad;
+
+    buffer->setRange(buffer->offset(), n);
+
+    return OK;
+}
+
+void LiveSession::postMonitorQueue(int64_t delayUs) {
+    sp<AMessage> msg = new AMessage(kWhatMonitorQueue, id());
+    msg->setInt32("generation", ++mMonitorQueueGeneration);
+    msg->post(delayUs);
+}
+
+void LiveSession::onSeek(const sp<AMessage> &msg) {
+    int64_t timeUs;
+    CHECK(msg->findInt64("timeUs", &timeUs));
+
+    mSeekTimeUs = timeUs;
+    postMonitorQueue();
+}
+
+status_t LiveSession::getDuration(int64_t *durationUs) {
+    Mutex::Autolock autoLock(mLock);
+    *durationUs = mDurationUs;
+
+    return OK;
+}
+
+bool LiveSession::isSeekable() {
+    int64_t durationUs;
+    return getDuration(&durationUs) == OK && durationUs >= 0;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp
deleted file mode 100644
index 4451bd5..0000000
--- a/media/libstagefright/httplive/LiveSource.cpp
+++ /dev/null
@@ -1,655 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "LiveSource"
-#include <utils/Log.h>
-
-#include "include/LiveSource.h"
-#include "include/M3UParser.h"
-#include "include/NuHTTPDataSource.h"
-
-#include <cutils/properties.h>
-#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/FileSource.h>
-
-#include <ctype.h>
-#include <openssl/aes.h>
-
-namespace android {
-
-LiveSource::LiveSource(const char *url)
-    : mMasterURL(url),
-      mInitCheck(NO_INIT),
-      mDurationUs(-1),
-      mPlaylistIndex(0),
-      mLastFetchTimeUs(-1),
-      mSource(new NuHTTPDataSource),
-      mSourceSize(0),
-      mOffsetBias(0),
-      mSignalDiscontinuity(false),
-      mPrevBandwidthIndex(-1),
-      mAESKey((AES_KEY *)malloc(sizeof(AES_KEY))),
-      mStreamEncrypted(false) {
-    if (switchToNext()) {
-        mInitCheck = OK;
-
-        determineSeekability();
-    }
-}
-
-LiveSource::~LiveSource() {
-    free(mAESKey);
-    mAESKey = NULL;
-}
-
-status_t LiveSource::initCheck() const {
-    return mInitCheck;
-}
-
-// static
-int LiveSource::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) {
-    if (a->mBandwidth < b->mBandwidth) {
-        return -1;
-    } else if (a->mBandwidth == b->mBandwidth) {
-        return 0;
-    }
-
-    return 1;
-}
-
-static double uniformRand() {
-    return (double)rand() / RAND_MAX;
-}
-
-size_t LiveSource::getBandwidthIndex() {
-    if (mBandwidthItems.size() == 0) {
-        return 0;
-    }
-
-#if 1
-    int32_t bandwidthBps;
-    if (mSource != NULL && mSource->estimateBandwidth(&bandwidthBps)) {
-        LOGI("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
-    } else {
-        LOGI("no bandwidth estimate.");
-        return 0;  // Pick the lowest bandwidth stream by default.
-    }
-
-    char value[PROPERTY_VALUE_MAX];
-    if (property_get("media.httplive.max-bw", value, NULL)) {
-        char *end;
-        long maxBw = strtoul(value, &end, 10);
-        if (end > value && *end == '\0') {
-            if (maxBw > 0 && bandwidthBps > maxBw) {
-                LOGV("bandwidth capped to %ld bps", maxBw);
-                bandwidthBps = maxBw;
-            }
-        }
-    }
-
-    // Consider only 80% of the available bandwidth usable.
-    bandwidthBps = (bandwidthBps * 8) / 10;
-
-    // Pick the highest bandwidth stream below or equal to estimated bandwidth.
-
-    size_t index = mBandwidthItems.size() - 1;
-    while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth
-                            > (size_t)bandwidthBps) {
-        --index;
-    }
-#elif 0
-    // Change bandwidth at random()
-    size_t index = uniformRand() * mBandwidthItems.size();
-#elif 0
-    // There's a 50% chance to stay on the current bandwidth and
-    // a 50% chance to switch to the next higher bandwidth (wrapping around
-    // to lowest)
-    const size_t kMinIndex = 0;
-
-    size_t index;
-    if (mPrevBandwidthIndex < 0) {
-        index = kMinIndex;
-    } else if (uniformRand() < 0.5) {
-        index = (size_t)mPrevBandwidthIndex;
-    } else {
-        index = mPrevBandwidthIndex + 1;
-        if (index == mBandwidthItems.size()) {
-            index = kMinIndex;
-        }
-    }
-#elif 0
-    // Pick the highest bandwidth stream below or equal to 1.2 Mbit/sec
-
-    size_t index = mBandwidthItems.size() - 1;
-    while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth > 1200000) {
-        --index;
-    }
-#else
-    size_t index = mBandwidthItems.size() - 1;  // Highest bandwidth stream
-#endif
-
-    return index;
-}
-
-bool LiveSource::loadPlaylist(bool fetchMaster, size_t bandwidthIndex) {
-    mSignalDiscontinuity = false;
-
-    mPlaylist.clear();
-    mPlaylistIndex = 0;
-
-    if (fetchMaster) {
-        mPrevBandwidthIndex = -1;
-
-        sp<ABuffer> buffer;
-        status_t err = fetchM3U(mMasterURL.c_str(), &buffer);
-
-        if (err != OK) {
-            return false;
-        }
-
-        mPlaylist = new M3UParser(
-                mMasterURL.c_str(), buffer->data(), buffer->size());
-
-        if (mPlaylist->initCheck() != OK) {
-            return false;
-        }
-
-        if (mPlaylist->isVariantPlaylist()) {
-            for (size_t i = 0; i < mPlaylist->size(); ++i) {
-                BandwidthItem item;
-
-                sp<AMessage> meta;
-                mPlaylist->itemAt(i, &item.mURI, &meta);
-
-                unsigned long bandwidth;
-                CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth));
-
-                mBandwidthItems.push(item);
-            }
-            mPlaylist.clear();
-
-            // fall through
-            if (mBandwidthItems.size() == 0) {
-                return false;
-            }
-
-            mBandwidthItems.sort(SortByBandwidth);
-
-#if 1  // XXX
-            if (mBandwidthItems.size() > 1) {
-                // Remove the lowest bandwidth stream, this is sometimes
-                // an AAC program stream, which we don't support at this point.
-                mBandwidthItems.removeItemsAt(0);
-            }
-#endif
-
-            for (size_t i = 0; i < mBandwidthItems.size(); ++i) {
-                const BandwidthItem &item = mBandwidthItems.itemAt(i);
-                LOGV("item #%d: %s", i, item.mURI.c_str());
-            }
-
-            bandwidthIndex = getBandwidthIndex();
-        }
-    }
-
-    if (mBandwidthItems.size() > 0) {
-        mURL = mBandwidthItems.editItemAt(bandwidthIndex).mURI;
-
-        if (mPrevBandwidthIndex >= 0
-                && (size_t)mPrevBandwidthIndex != bandwidthIndex) {
-            // If we switched streams because of bandwidth changes,
-            // we'll signal this discontinuity by inserting a
-            // special transport stream packet into the stream.
-            mSignalDiscontinuity = true;
-        }
-
-        mPrevBandwidthIndex = bandwidthIndex;
-    } else {
-        mURL = mMasterURL;
-    }
-
-    if (mPlaylist == NULL) {
-        sp<ABuffer> buffer;
-        status_t err = fetchM3U(mURL.c_str(), &buffer);
-
-        if (err != OK) {
-            return false;
-        }
-
-        mPlaylist = new M3UParser(mURL.c_str(), buffer->data(), buffer->size());
-
-        if (mPlaylist->initCheck() != OK) {
-            return false;
-        }
-
-        if (mPlaylist->isVariantPlaylist()) {
-            return false;
-        }
-    }
-
-    if (!mPlaylist->meta()->findInt32(
-                "media-sequence", &mFirstItemSequenceNumber)) {
-        mFirstItemSequenceNumber = 0;
-    }
-
-    return true;
-}
-
-static int64_t getNowUs() {
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-
-    return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
-}
-
-bool LiveSource::switchToNext() {
-    mSignalDiscontinuity = false;
-
-    mOffsetBias += mSourceSize;
-    mSourceSize = 0;
-
-    size_t bandwidthIndex = getBandwidthIndex();
-
-    if (mLastFetchTimeUs < 0 || getNowUs() >= mLastFetchTimeUs + 15000000ll
-        || mPlaylistIndex == mPlaylist->size()
-        || (ssize_t)bandwidthIndex != mPrevBandwidthIndex) {
-        int32_t nextSequenceNumber =
-            mPlaylistIndex + mFirstItemSequenceNumber;
-
-        if (!loadPlaylist(mLastFetchTimeUs < 0, bandwidthIndex)) {
-            LOGE("failed to reload playlist");
-            return false;
-        }
-
-        if (mLastFetchTimeUs < 0) {
-            if (isSeekable()) {
-                mPlaylistIndex = 0;
-            } else {
-                // This is live streamed content, the first seqnum in the
-                // various bandwidth' streams may be slightly off, so don't
-                // start at the very first entry.
-                // With a segment duration of 6-10secs, this really only
-                // delays playback up to 30secs compared to real time.
-                mPlaylistIndex = 3;
-                if (mPlaylistIndex >= mPlaylist->size()) {
-                    mPlaylistIndex = mPlaylist->size() - 1;
-                }
-            }
-        } else {
-            if (nextSequenceNumber < mFirstItemSequenceNumber
-                    || nextSequenceNumber
-                            >= mFirstItemSequenceNumber + (int32_t)mPlaylist->size()) {
-                LOGE("Cannot find sequence number %d in new playlist",
-                     nextSequenceNumber);
-
-                return false;
-            }
-
-            mPlaylistIndex = nextSequenceNumber - mFirstItemSequenceNumber;
-        }
-
-        mLastFetchTimeUs = getNowUs();
-    }
-
-    if (!setupCipher()) {
-        return false;
-    }
-
-    AString uri;
-    sp<AMessage> itemMeta;
-    CHECK(mPlaylist->itemAt(mPlaylistIndex, &uri, &itemMeta));
-    LOGV("switching to %s", uri.c_str());
-
-    if (mSource->connect(uri.c_str()) != OK
-            || mSource->getSize(&mSourceSize) != OK) {
-        return false;
-    }
-
-    int32_t val;
-    if (itemMeta->findInt32("discontinuity", &val) && val != 0) {
-        mSignalDiscontinuity = true;
-    }
-
-    mPlaylistIndex++;
-
-    return true;
-}
-
-bool LiveSource::setupCipher() {
-    sp<AMessage> itemMeta;
-    bool found = false;
-    AString method;
-
-    for (ssize_t i = mPlaylistIndex; i >= 0; --i) {
-        AString uri;
-        CHECK(mPlaylist->itemAt(i, &uri, &itemMeta));
-
-        if (itemMeta->findString("cipher-method", &method)) {
-            found = true;
-            break;
-        }
-    }
-
-    if (!found) {
-        method = "NONE";
-    }
-
-    mStreamEncrypted = false;
-
-    if (method == "AES-128") {
-        AString keyURI;
-        if (!itemMeta->findString("cipher-uri", &keyURI)) {
-            LOGE("Missing key uri");
-            return false;
-        }
-
-        ssize_t index = mAESKeyForURI.indexOfKey(keyURI);
-
-        sp<ABuffer> key;
-        if (index >= 0) {
-            key = mAESKeyForURI.valueAt(index);
-        } else {
-            key = new ABuffer(16);
-
-            sp<NuHTTPDataSource> keySource = new NuHTTPDataSource;
-            status_t err = keySource->connect(keyURI.c_str());
-
-            if (err == OK) {
-                size_t offset = 0;
-                while (offset < 16) {
-                    ssize_t n = keySource->readAt(
-                            offset, key->data() + offset, 16 - offset);
-                    if (n <= 0) {
-                        err = ERROR_IO;
-                        break;
-                    }
-
-                    offset += n;
-                }
-            }
-
-            if (err != OK) {
-                LOGE("failed to fetch cipher key from '%s'.", keyURI.c_str());
-                return false;
-            }
-
-            mAESKeyForURI.add(keyURI, key);
-        }
-
-        if (AES_set_decrypt_key(key->data(), 128, (AES_KEY *)mAESKey) != 0) {
-            LOGE("failed to set AES decryption key.");
-            return false;
-        }
-
-        AString iv;
-        if (itemMeta->findString("cipher-iv", &iv)) {
-            if ((!iv.startsWith("0x") && !iv.startsWith("0X"))
-                    || iv.size() != 16 * 2 + 2) {
-                LOGE("malformed cipher IV '%s'.", iv.c_str());
-                return false;
-            }
-
-            memset(mAESIVec, 0, sizeof(mAESIVec));
-            for (size_t i = 0; i < 16; ++i) {
-                char c1 = tolower(iv.c_str()[2 + 2 * i]);
-                char c2 = tolower(iv.c_str()[3 + 2 * i]);
-                if (!isxdigit(c1) || !isxdigit(c2)) {
-                    LOGE("malformed cipher IV '%s'.", iv.c_str());
-                    return false;
-                }
-                uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
-                uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
-
-                mAESIVec[i] = nibble1 << 4 | nibble2;
-            }
-        } else {
-            size_t seqNum = mPlaylistIndex + mFirstItemSequenceNumber;
-
-            memset(mAESIVec, 0, sizeof(mAESIVec));
-            mAESIVec[15] = seqNum & 0xff;
-            mAESIVec[14] = (seqNum >> 8) & 0xff;
-            mAESIVec[13] = (seqNum >> 16) & 0xff;
-            mAESIVec[12] = (seqNum >> 24) & 0xff;
-        }
-
-        mStreamEncrypted = true;
-    } else if (!(method == "NONE")) {
-        LOGE("Unsupported cipher method '%s'", method.c_str());
-        return false;
-    }
-
-    return true;
-}
-
-static const ssize_t kHeaderSize = 188;
-
-ssize_t LiveSource::readAt(off64_t offset, void *data, size_t size) {
-    CHECK(offset >= mOffsetBias);
-    offset -= mOffsetBias;
-
-    off64_t delta = mSignalDiscontinuity ? kHeaderSize : 0;
-
-    if (offset >= mSourceSize + delta) {
-        CHECK_EQ(offset, mSourceSize + delta);
-
-        offset -= mSourceSize + delta;
-        if (!switchToNext()) {
-            return ERROR_END_OF_STREAM;
-        }
-
-        if (mSignalDiscontinuity) {
-            LOGV("switchToNext changed streams");
-        } else {
-            LOGV("switchToNext stayed within the same stream");
-        }
-
-        mOffsetBias += delta;
-
-        delta = mSignalDiscontinuity ? kHeaderSize : 0;
-    }
-
-    if (offset < delta) {
-        size_t avail = delta - offset;
-        memset(data, 0, avail);
-        return avail;
-    }
-
-    bool done = false;
-    size_t numRead = 0;
-    while (numRead < size) {
-        ssize_t n = mSource->readAt(
-                offset + numRead - delta,
-                (uint8_t *)data + numRead, size - numRead);
-
-        if (n <= 0) {
-            break;
-        }
-
-        if (mStreamEncrypted) {
-            size_t nmod = n % 16;
-            CHECK(nmod == 0);
-
-            sp<ABuffer> tmp = new ABuffer(n);
-
-            AES_cbc_encrypt((const unsigned char *)data + numRead,
-                            tmp->data(),
-                            n,
-                            (const AES_KEY *)mAESKey,
-                            mAESIVec,
-                            AES_DECRYPT);
-
-            if (mSourceSize == (off64_t)(offset + numRead - delta + n)) {
-                // check for padding at the end of the file.
-
-                size_t pad = tmp->data()[n - 1];
-                CHECK_GT(pad, 0u);
-                CHECK_LE(pad, 16u);
-                CHECK_GE((size_t)n, pad);
-                for (size_t i = 0; i < pad; ++i) {
-                    CHECK_EQ((unsigned)tmp->data()[n - 1 - i], pad);
-                }
-
-                n -= pad;
-                mSourceSize -= pad;
-
-                done = true;
-            }
-
-            memcpy((uint8_t *)data + numRead, tmp->data(), n);
-        }
-
-        numRead += n;
-
-        if (done) {
-            break;
-        }
-    }
-
-    return numRead;
-}
-
-status_t LiveSource::fetchM3U(const char *url, sp<ABuffer> *out) {
-    *out = NULL;
-
-    sp<DataSource> source;
-
-    if (!strncasecmp(url, "file://", 7)) {
-        source = new FileSource(url + 7);
-    } else {
-        CHECK(!strncasecmp(url, "http://", 7));
-
-        status_t err = mSource->connect(url);
-
-        if (err != OK) {
-            return err;
-        }
-
-        source = mSource;
-    }
-
-    off64_t size;
-    status_t err = source->getSize(&size);
-
-    if (err != OK) {
-        size = 65536;
-    }
-
-    sp<ABuffer> buffer = new ABuffer(size);
-    buffer->setRange(0, 0);
-
-    for (;;) {
-        size_t bufferRemaining = buffer->capacity() - buffer->size();
-
-        if (bufferRemaining == 0) {
-            bufferRemaining = 32768;
-
-            LOGV("increasing download buffer to %d bytes",
-                 buffer->size() + bufferRemaining);
-
-            sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);
-            memcpy(copy->data(), buffer->data(), buffer->size());
-            copy->setRange(0, buffer->size());
-
-            buffer = copy;
-        }
-
-        ssize_t n = source->readAt(
-                buffer->size(), buffer->data() + buffer->size(),
-                bufferRemaining);
-
-        if (n < 0) {
-            return err;
-        }
-
-        if (n == 0) {
-            break;
-        }
-
-        buffer->setRange(0, buffer->size() + (size_t)n);
-    }
-
-    *out = buffer;
-
-    return OK;
-}
-
-bool LiveSource::seekTo(int64_t seekTimeUs) {
-    LOGV("seek to %lld us", seekTimeUs);
-
-    if (!mPlaylist->isComplete()) {
-        return false;
-    }
-
-    int32_t targetDuration;
-    if (!mPlaylist->meta()->findInt32("target-duration", &targetDuration)) {
-        return false;
-    }
-
-    int64_t seekTimeSecs = (seekTimeUs + 500000ll) / 1000000ll;
-
-    int64_t index = seekTimeSecs / targetDuration;
-
-    if (index < 0 || index >= mPlaylist->size()) {
-        return false;
-    }
-
-    if (index == mPlaylistIndex) {
-        return false;
-    }
-
-    mPlaylistIndex = index;
-
-    LOGV("seeking to index %lld", index);
-
-    switchToNext();
-    mOffsetBias = 0;
-
-    return true;
-}
-
-bool LiveSource::getDuration(int64_t *durationUs) const {
-    if (mDurationUs >= 0) {
-        *durationUs = mDurationUs;
-        return true;
-    }
-
-    *durationUs = 0;
-    return false;
-}
-
-bool LiveSource::isSeekable() const {
-    return mDurationUs >= 0;
-}
-
-void LiveSource::determineSeekability() {
-    mDurationUs = -1;
-
-    if (!mPlaylist->isComplete()) {
-        return;
-    }
-
-    int32_t targetDuration;
-    if (!mPlaylist->meta()->findInt32("target-duration", &targetDuration)) {
-        return;
-    }
-
-    mDurationUs = targetDuration * 1000000ll * mPlaylist->size();
-}
-
-}  // namespace android
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 46f4a35..0c67432 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -45,6 +45,8 @@
 class DrmManagerClinet;
 class DecryptHandle;
 
+struct LiveSession;
+
 struct AwesomeRenderer : public RefBase {
     AwesomeRenderer() {}
 
@@ -193,6 +195,8 @@
     sp<ARTPSession> mRTPSession;
     sp<UDPPusher> mRTPPusher, mRTCPPusher;
 
+    sp<LiveSession> mLiveSession;
+
     DrmManagerClient *mDrmManagerClient;
     DecryptHandle *mDecryptHandle;
 
diff --git a/media/libstagefright/include/LiveSession.h b/media/libstagefright/include/LiveSession.h
new file mode 100644
index 0000000..50c0a99
--- /dev/null
+++ b/media/libstagefright/include/LiveSession.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIVE_SESSION_H_
+
+#define LIVE_SESSION_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct ABuffer;
+struct DataSource;
+struct LiveDataSource;
+struct M3UParser;
+struct NuHTTPDataSource;
+
+struct LiveSession : public AHandler {
+    LiveSession();
+
+    sp<DataSource> getDataSource();
+
+    void connect(const char *url);
+    void disconnect();
+
+    // Blocks until seek is complete.
+    void seekTo(int64_t timeUs);
+
+    status_t getDuration(int64_t *durationUs);
+    bool isSeekable();
+
+protected:
+    virtual ~LiveSession();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kMaxNumQueuedFragments = 2,
+        kMaxNumRetries         = 3,
+    };
+
+    static const int64_t kMaxPlaylistAgeUs;
+
+    enum {
+        kWhatConnect        = 'conn',
+        kWhatDisconnect     = 'disc',
+        kWhatMonitorQueue   = 'moni',
+        kWhatSeek           = 'seek',
+    };
+
+    struct BandwidthItem {
+        AString mURI;
+        unsigned long mBandwidth;
+    };
+
+    sp<LiveDataSource> mDataSource;
+
+    sp<NuHTTPDataSource> mHTTPDataSource;
+
+    AString mMasterURL;
+    Vector<BandwidthItem> mBandwidthItems;
+
+    KeyedVector<AString, sp<ABuffer> > mAESKeyForURI;
+
+    ssize_t mPrevBandwidthIndex;
+    int64_t mLastPlaylistFetchTimeUs;
+    sp<M3UParser> mPlaylist;
+    int32_t mSeqNumber;
+    int64_t mSeekTimeUs;
+    int32_t mNumRetries;
+
+    Mutex mLock;
+    Condition mCondition;
+    int64_t mDurationUs;
+    bool mSeekDone;
+
+    int32_t mMonitorQueueGeneration;
+
+    void onConnect(const sp<AMessage> &msg);
+    void onDisconnect();
+    void onDownloadNext();
+    void onMonitorQueue();
+    void onSeek(const sp<AMessage> &msg);
+
+    status_t fetchFile(const char *url, sp<ABuffer> *out);
+    sp<M3UParser> fetchPlaylist(const char *url);
+    size_t getBandwidthIndex();
+
+    status_t decryptBuffer(
+            size_t playlistIndex, const sp<ABuffer> &buffer);
+
+    void postMonitorQueue(int64_t delayUs = 0);
+
+    static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);
+
+    DISALLOW_EVIL_CONSTRUCTORS(LiveSession);
+};
+
+}  // namespace android
+
+#endif  // LIVE_SESSION_H_
diff --git a/media/libstagefright/include/LiveSource.h b/media/libstagefright/include/LiveSource.h
deleted file mode 100644
index 38fe328..0000000
--- a/media/libstagefright/include/LiveSource.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LIVE_SOURCE_H_
-
-#define LIVE_SOURCE_H_
-
-#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AString.h>
-#include <media/stagefright/DataSource.h>
-#include <utils/KeyedVector.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-struct ABuffer;
-struct NuHTTPDataSource;
-struct M3UParser;
-
-struct LiveSource : public DataSource {
-    LiveSource(const char *url);
-
-    virtual status_t initCheck() const;
-
-    virtual ssize_t readAt(off64_t offset, void *data, size_t size);
-
-    virtual uint32_t flags() {
-        return kWantsPrefetching;
-    }
-
-    bool getDuration(int64_t *durationUs) const;
-
-    bool isSeekable() const;
-    bool seekTo(int64_t seekTimeUs);
-
-protected:
-    virtual ~LiveSource();
-
-private:
-    struct BandwidthItem {
-        AString mURI;
-        unsigned long mBandwidth;
-    };
-    Vector<BandwidthItem> mBandwidthItems;
-
-    AString mMasterURL;
-    AString mURL;
-    status_t mInitCheck;
-    int64_t mDurationUs;
-
-    sp<M3UParser> mPlaylist;
-    int32_t mFirstItemSequenceNumber;
-    size_t mPlaylistIndex;
-    int64_t mLastFetchTimeUs;
-
-    sp<NuHTTPDataSource> mSource;
-    off64_t mSourceSize;
-    off64_t mOffsetBias;
-
-    bool mSignalDiscontinuity;
-    ssize_t mPrevBandwidthIndex;
-
-    void *mAESKey;
-    unsigned char mAESIVec[16];
-    bool mStreamEncrypted;
-
-    KeyedVector<AString, sp<ABuffer> > mAESKeyForURI;
-
-    status_t fetchM3U(const char *url, sp<ABuffer> *buffer);
-
-    static int SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b);
-
-    bool switchToNext();
-    bool loadPlaylist(bool fetchMaster, size_t bandwidthIndex);
-    void determineSeekability();
-
-    size_t getBandwidthIndex();
-    bool setupCipher();
-
-    DISALLOW_EVIL_CONSTRUCTORS(LiveSource);
-};
-
-}  // namespace android
-
-#endif  // LIVE_SOURCE_H_
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index 58401d4..efe7496 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -15,7 +15,7 @@
 struct DataSource;
 struct MPEG2TSSource;
 struct String8;
-struct LiveSource;
+struct LiveSession;
 
 struct MPEG2TSExtractor : public MediaExtractor {
     MPEG2TSExtractor(const sp<DataSource> &source);
@@ -28,7 +28,7 @@
 
     virtual uint32_t flags() const;
 
-    void setLiveSource(const sp<LiveSource> &liveSource);
+    void setLiveSession(const sp<LiveSession> &liveSession);
     void seekTo(int64_t seekTimeUs);
 
 private:
@@ -37,7 +37,7 @@
     mutable Mutex mLock;
 
     sp<DataSource> mDataSource;
-    sp<LiveSource> mLiveSource;
+    sp<LiveSession> mLiveSession;
 
     sp<ATSParser> mParser;
 
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index f0f7daf..78719c1 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -45,9 +45,6 @@
     size_t cachedSize();
     size_t approxDataRemaining(bool *eos);
 
-    void suspend();
-    void clearCacheAndResume();
-
     void resumeFetchingIfNecessary();
 
 protected:
@@ -69,7 +66,6 @@
     enum {
         kWhatFetchMore  = 'fetc',
         kWhatRead       = 'read',
-        kWhatSuspend    = 'susp',
     };
 
     sp<DataSource> mSource;
@@ -87,12 +83,10 @@
     sp<AMessage> mAsyncResult;
     bool mFetching;
     int64_t mLastFetchTimeUs;
-    bool mSuspended;
 
     void onMessageReceived(const sp<AMessage> &msg);
     void onFetch();
     void onRead(const sp<AMessage> &msg);
-    void onSuspend();
 
     void fetchInternal();
     ssize_t readInternal(off64_t offset, void *data, size_t size);
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 37bcb23..1fb7c39 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -49,6 +49,33 @@
     mFormat.clear();
 }
 
+static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) {
+    if (size < 3) {
+        // Not enough data to verify header.
+        return false;
+    }
+
+    if (ptr[0] != 0xff || (ptr[1] >> 4) != 0x0f) {
+        return false;
+    }
+
+    unsigned layer = (ptr[1] >> 1) & 3;
+
+    if (layer != 0) {
+        return false;
+    }
+
+    unsigned ID = (ptr[1] >> 3) & 1;
+    unsigned profile_ObjectType = ptr[2] >> 6;
+
+    if (ID == 1 && profile_ObjectType == 3) {
+        // MPEG-2 profile 3 is reserved.
+        return false;
+    }
+
+    return true;
+}
+
 status_t ElementaryStreamQueue::appendData(
         const void *data, size_t size, int64_t timeUs) {
     if (mBuffer == NULL || mBuffer->size() == 0) {
@@ -96,8 +123,8 @@
                 }
 #else
                 ssize_t startOffset = -1;
-                for (size_t i = 0; i + 1 < size; ++i) {
-                    if (ptr[i] == 0xff && (ptr[i + 1] >> 4) == 0x0f) {
+                for (size_t i = 0; i < size; ++i) {
+                    if (IsSeeminglyValidADTSHeader(&ptr[i], size - i)) {
                         startOffset = i;
                         break;
                     }
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 3176810..600116e 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -19,7 +19,7 @@
 #include <utils/Log.h>
 
 #include "include/MPEG2TSExtractor.h"
-#include "include/LiveSource.h"
+#include "include/LiveSession.h"
 #include "include/NuCachedSource2.h"
 
 #include <media/stagefright/DataSource.h>
@@ -82,8 +82,8 @@
     sp<MetaData> meta = mImpl->getFormat();
 
     int64_t durationUs;
-    if (mExtractor->mLiveSource != NULL
-            && mExtractor->mLiveSource->getDuration(&durationUs)) {
+    if (mExtractor->mLiveSession != NULL
+            && mExtractor->mLiveSession->getDuration(&durationUs) == OK) {
         meta->setInt64(kKeyDuration, durationUs);
     }
 
@@ -226,32 +226,20 @@
     return OK;
 }
 
-void MPEG2TSExtractor::setLiveSource(const sp<LiveSource> &liveSource) {
+void MPEG2TSExtractor::setLiveSession(const sp<LiveSession> &liveSession) {
     Mutex::Autolock autoLock(mLock);
 
-    mLiveSource = liveSource;
+    mLiveSession = liveSession;
 }
 
 void MPEG2TSExtractor::seekTo(int64_t seekTimeUs) {
     Mutex::Autolock autoLock(mLock);
 
-    if (mLiveSource == NULL) {
+    if (mLiveSession == NULL) {
         return;
     }
 
-    if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
-        static_cast<NuCachedSource2 *>(mDataSource.get())->suspend();
-    }
-
-    if (mLiveSource->seekTo(seekTimeUs)) {
-        mParser->signalDiscontinuity(true  /* isSeek */);
-        mOffset = 0;
-    }
-
-    if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
-        static_cast<NuCachedSource2 *>(mDataSource.get())
-            ->clearCacheAndResume();
-    }
+    mLiveSession->seekTo(seekTimeUs);
 }
 
 uint32_t MPEG2TSExtractor::flags() const {
@@ -259,7 +247,7 @@
 
     uint32_t flags = CAN_PAUSE;
 
-    if (mLiveSource != NULL && mLiveSource->isSeekable()) {
+    if (mLiveSession != NULL && mLiveSession->isSeekable()) {
         flags |= CAN_SEEK_FORWARD | CAN_SEEK_BACKWARD | CAN_SEEK;
     }
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
index 2332657..ce6db68 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
@@ -253,7 +253,7 @@
     }
     
     //Format: QVGA h263
-    @LargeTest
+    @Suppress
     public void testQVGAH263() throws Exception {  
         boolean videoRecordedResult = false;
         recordVideo(15, 320, 240, MediaRecorder.VideoEncoder.H263, 
@@ -263,7 +263,7 @@
     }
     
     //Format: SQVGA h263
-    @LargeTest
+    @Suppress
     public void testSQVGAH263() throws Exception {  
         boolean videoRecordedResult = false;
         recordVideo(15, 240, 160, MediaRecorder.VideoEncoder.H263, 
@@ -341,7 +341,7 @@
         assertTrue("HVGAMP4", videoRecordedResult);
     }
      
-    @LargeTest
+    @Suppress
     public void testQVGAMP4() throws Exception {  
         boolean videoRecordedResult = false;
         recordVideo(15, 320, 240, MediaRecorder.VideoEncoder.MPEG_4_SP, 
@@ -350,7 +350,7 @@
         assertTrue("QVGAMP4", videoRecordedResult);
     }
     
-    @LargeTest
+    @Suppress
     public void testSQVGAMP4() throws Exception {  
         boolean videoRecordedResult = false;
         recordVideo(15, 240, 160, MediaRecorder.VideoEncoder.MPEG_4_SP, 
diff --git a/packages/DefaultContainerService/Android.mk b/packages/DefaultContainerService/Android.mk
index 2f1d6ab..986b6c8 100755
--- a/packages/DefaultContainerService/Android.mk
+++ b/packages/DefaultContainerService/Android.mk
@@ -7,6 +7,10 @@
 
 LOCAL_PACKAGE_NAME := DefaultContainerService
 
+LOCAL_JNI_SHARED_LIBRARIES := libdefcontainer_jni
+
 LOCAL_CERTIFICATE := platform
 
 include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/DefaultContainerService/jni/Android.mk b/packages/DefaultContainerService/jni/Android.mk
new file mode 100644
index 0000000..a2febec
--- /dev/null
+++ b/packages/DefaultContainerService/jni/Android.mk
@@ -0,0 +1,39 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_SRC_FILES := \
+    com_android_defcontainer_MeasurementUtils.cpp
+
+LOCAL_C_INCLUDES += \
+    $(JNI_H_INCLUDES)
+
+LOCAL_SHARED_LIBRARIES := \
+    libnativehelper \
+    libutils
+
+LOCAL_STATIC_LIBRARIES := \
+    libdiskusage
+
+LOCAL_MODULE := libdefcontainer_jni
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
diff --git a/packages/DefaultContainerService/jni/com_android_defcontainer_MeasurementUtils.cpp b/packages/DefaultContainerService/jni/com_android_defcontainer_MeasurementUtils.cpp
new file mode 100644
index 0000000..6579f95
--- /dev/null
+++ b/packages/DefaultContainerService/jni/com_android_defcontainer_MeasurementUtils.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DefContainer-JNI"
+
+#include <JNIHelp.h>
+
+#include <diskusage/dirsize.h>
+#include <utils/Log.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+namespace android {
+
+static jlong native_measureDirectory(JNIEnv* env, jobject clazz, jstring directory) {
+    jlong ret = 0L;
+
+    const char* path = env->GetStringUTFChars(directory, NULL);
+    if (path == NULL) {
+        return ret;
+    }
+
+    int dirfd = open(path, O_DIRECTORY, O_RDONLY);
+    if (dirfd < 0) {
+        LOGI("error opening: %s: %s", path, strerror(errno));
+    } else {
+        ret = calculate_dir_size(dirfd);
+        close(dirfd);
+    }
+
+    env->ReleaseStringUTFChars(directory, path);
+
+    return ret;
+}
+
+static const JNINativeMethod g_methods[] = {
+    { "native_measureDirectory", "(Ljava/lang/String;)J", (void*)native_measureDirectory },
+};
+
+int register_com_android_defcontainer(JNIEnv *env) {
+    if (jniRegisterNativeMethods(
+            env, "com/android/defcontainer/MeasurementUtils", g_methods, NELEM(g_methods)) < 0) {
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_6;
+}
+
+} // namespace android
+
+int JNI_OnLoad(JavaVM *jvm, void* reserved) {
+    JNIEnv *env;
+
+    if (jvm->GetEnv((void**)&env, JNI_VERSION_1_6)) {
+        return JNI_ERR;
+    }
+
+    return android::register_com_android_defcontainer(env);
+}
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index e6624ae..0c85af8 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -163,6 +163,11 @@
                 return null;
             }
         }
+
+        @Override
+        public long calculateDirectorySize(String directory) throws RemoteException {
+            return MeasurementUtils.measureDirectory(directory);
+        }
     };
 
     public DefaultContainerService() {
diff --git a/core/java/android/nfc/INdefTag.aidl b/packages/DefaultContainerService/src/com/android/defcontainer/MeasurementUtils.java
similarity index 66%
copy from core/java/android/nfc/INdefTag.aidl
copy to packages/DefaultContainerService/src/com/android/defcontainer/MeasurementUtils.java
index d131ebe..6f5f53b 100644
--- a/core/java/android/nfc/INdefTag.aidl
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/MeasurementUtils.java
@@ -14,15 +14,16 @@
  * limitations under the License.
  */
 
-package android.nfc;
+package com.android.defcontainer;
 
-import android.nfc.NdefMessage;
+public class MeasurementUtils {
+    static {
+        System.loadLibrary("defcontainer_jni");
+    }
 
-/**
- * @hide
- */
-interface INdefTag
-{
-    NdefMessage read(int nativeHandle);
-    boolean write(int nativeHandle, in NdefMessage msg);
-}
\ No newline at end of file
+    public static long measureDirectory(String path) {
+        return native_measureDirectory(path);
+    }
+
+    private native static long native_measureDirectory(String path);
+}
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index 666bfdc..cd67d1a 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -76,8 +76,23 @@
                         android:textColor="#2e2e2e" />
                 </com.android.systemui.statusbar.tablet.HoloClock>
 
+                <TextView
+                    android:id="@+id/network_text"
+                    android:layout_width="wrap_content"
+                    android:layout_height="match_parent"
+                    android:layout_marginTop="12dp"
+                    android:layout_marginRight="6dip"
+                    android:layout_marginLeft="6dip"
+                    android:gravity="center"
+                    android:singleLine="true"
+                    android:visibility="gone"
+                    android:textSize="14dip"
+                    android:textColor="#606060"
+                    />
+
                 <LinearLayout
-                    android:layout_width="48dip"
+                    android:id="@+id/signal_battery_cluster"
+                    android:layout_width="wrap_content"
                     android:layout_height="match_parent"
                     android:orientation="horizontal"
                     android:gravity="center"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 563b8ef..b0f6a18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -165,6 +165,8 @@
                 (ImageView)mNotificationPanel.findViewById(R.id.network_type));
         mNetworkController.addLabelView(
                 (TextView)mNotificationPanel.findViewById(R.id.network_text));
+        mNetworkController.addLabelView(
+                (TextView)mBarContents.findViewById(R.id.network_text));
 
         mStatusBarView.setIgnoreChildren(0, mNotificationTrigger, mNotificationPanel);
 
@@ -579,12 +581,28 @@
         setAreThereNotifications();
     }
 
+    public void showClock(boolean show) {
+        View clock = mBarContents.findViewById(R.id.clock);
+        View network_text = mBarContents.findViewById(R.id.network_text);
+        if (clock != null) {
+            clock.setVisibility(show ? View.VISIBLE : View.GONE);
+        }
+        if (network_text != null) {
+            network_text.setVisibility((!show) ? View.VISIBLE : View.GONE);
+        }
+    }
+
     public void disable(int state) {
         int old = mDisabled;
         int diff = state ^ old;
         mDisabled = state;
 
         // act accordingly
+        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
+            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
+            Slog.d(TAG, "DISABLE_CLOCK: " + (show ? "no" : "yes"));
+            showClock(show);
+        }
         if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
             if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
                 Slog.d(TAG, "DISABLE_EXPAND: yes");
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 72af34d..c0c3afd 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -1100,7 +1100,9 @@
             boolean enable = !mShowing || (mHidden && !isSecure());
             mStatusBarManager.disable(enable ?
                          StatusBarManager.DISABLE_NONE :
-                         StatusBarManager.DISABLE_EXPAND);
+                         ( StatusBarManager.DISABLE_EXPAND
+                         | StatusBarManager.DISABLE_NAVIGATION
+                         | StatusBarManager.DISABLE_CLOCK));
         }
     }
 
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 7e76ea5..43c857a 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -264,11 +264,13 @@
         mHandler = new MyHandler(handlerThread.getLooper());
 
         // setup our unique device name
-        String id = Settings.Secure.getString(context.getContentResolver(),
-                Settings.Secure.ANDROID_ID);
-        if (id != null && id.length() > 0) {
-            String name = new String("android_").concat(id);
-            SystemProperties.set("net.hostname", name);
+        if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
+            String id = Settings.Secure.getString(context.getContentResolver(),
+                    Settings.Secure.ANDROID_ID);
+            if (id != null && id.length() > 0) {
+                String name = new String("android_").concat(id);
+                SystemProperties.set("net.hostname", name);
+            }
         }
 
         // read our default dns server ip
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 7101bb0..ba8af3a 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -17,7 +17,6 @@
 package com.android.server;
 
 import com.android.internal.statusbar.StatusBarNotification;
-import com.android.server.StatusBarManagerService;
 
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
@@ -41,12 +40,11 @@
 import android.hardware.Usb;
 import android.media.AudioManager;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
-import android.os.Power;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -55,8 +53,8 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.EventLog;
-import android.util.Slog;
 import android.util.Log;
+import android.util.Slog;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.Toast;
@@ -158,8 +156,6 @@
         final int id;
         final int uid;
         final int initialPid;
-        ITransientNotification callback;
-        int duration;
         final Notification notification;
         IBinder statusBarKey;
 
@@ -642,6 +638,7 @@
 
     // Notifications
     // ============================================================================
+    @Deprecated
     public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut)
     {
         enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
@@ -975,7 +972,7 @@
         }
     }
 
-
+    @Deprecated
     public void cancelNotification(String pkg, int id) {
         cancelNotificationWithTag(pkg, null /* tag */, id);
     }
@@ -1030,12 +1027,6 @@
         }
     }
 
-    private void updateLights() {
-        synchronized (mNotificationList) {
-            updateLightsLocked();
-        }
-    }
-
     // lock on mNotificationList
     private void updateLightsLocked()
     {
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index fce7b9b..b64b45d 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -115,6 +115,7 @@
     int mCallRingContinueToken = 0;
     int mCallRingDelay;
     public boolean mIsTheCurrentActivePhone = true;
+    boolean mIsVoiceCapable = true;
 
     /**
      * Set a system property, unless we're in unit test mode
@@ -205,6 +206,15 @@
         mDnsCheckDisabled = sp.getBoolean(DNS_SERVER_CHECK_DISABLED_KEY, false);
         mCM.setOnCallRing(this, EVENT_CALL_RING, null);
 
+        /* "Voice capable" means that this device supports circuit-switched
+        * (i.e. voice) phone calls over the telephony network, and is allowed
+        * to display the in-call UI while a cellular voice call is active.
+        * This will be false on "data only" devices which can't make voice
+        * calls and don't support any in-call UI.
+        */
+        mIsVoiceCapable = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_voice_capable);
+
         /**
          *  Some RIL's don't always send RIL_UNSOL_CALL_RING so it needs
          *  to be generated locally. Ideally all ring tones should be loops
@@ -1006,6 +1016,8 @@
      * version scoped to their packages
      */
     protected void notifyNewRingingConnectionP(Connection cn) {
+        if (!mIsVoiceCapable)
+            return;
         AsyncResult ar = new AsyncResult(null, cn, null);
         mNewRingingConnectionRegistrants.notifyRegistrants(ar);
     }
@@ -1014,6 +1026,8 @@
      * Notify registrants of a RING event.
      */
     private void notifyIncomingRing() {
+        if (!mIsVoiceCapable)
+            return;
         AsyncResult ar = new AsyncResult(null, this, null);
         mIncomingRingRegistrants.notifyRegistrants(ar);
     }
@@ -1022,7 +1036,8 @@
      * Send the incoming call Ring notification if conditions are right.
      */
     private void sendIncomingCallRingNotification(int token) {
-        if (!mDoesRilSendMultipleCallRing && (token == mCallRingContinueToken)) {
+        if (mIsVoiceCapable && !mDoesRilSendMultipleCallRing &&
+                (token == mCallRingContinueToken)) {
             Log.d(LOG_TAG, "Sending notifyIncomingRing");
             notifyIncomingRing();
             sendMessageDelayed(
@@ -1031,7 +1046,8 @@
             Log.d(LOG_TAG, "Ignoring ring notification request,"
                     + " mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing
                     + " token=" + token
-                    + " mCallRingContinueToken=" + mCallRingContinueToken);
+                    + " mCallRingContinueToken=" + mCallRingContinueToken
+                    + " mIsVoiceCapable=" + mIsVoiceCapable);
         }
     }
 
diff --git a/tests/DumpRenderTree/AndroidManifest.xml b/tests/DumpRenderTree/AndroidManifest.xml
index dc44b25..86b3f06 100644
--- a/tests/DumpRenderTree/AndroidManifest.xml
+++ b/tests/DumpRenderTree/AndroidManifest.xml
@@ -27,7 +27,7 @@
         </activity>
         <activity android:name="TestShellActivity"
           android:launchMode="singleTop"
-          android:hardwareAccelerated="true"
+          android:hardwareAccelerated="false"
           android:screenOrientation="portrait"
           android:theme="@android:style/Theme.Light"/>
         <activity android:name="ReliabilityTestActivity" android:screenOrientation="portrait"
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index 132f17a..8aa3c69 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -20,6 +20,7 @@
 import com.android.dumprendertree.forwarder.AdbUtils;
 import com.android.dumprendertree.forwarder.ForwardService;
 
+import android.content.Context;
 import android.content.Intent;
 import android.os.Environment;
 import android.test.ActivityInstrumentationTestCase2;
@@ -471,11 +472,14 @@
 
     public void copyResultsAndRunnerAssetsToCache() {
         try {
-            String out_dir = getActivity().getApplicationContext().getCacheDir().getPath() + "/";
+            Context targetContext = getInstrumentation().getTargetContext();
+            File cacheDir = targetContext.getCacheDir();
 
             for( int i=0; i< LAYOUT_TESTS_RESULTS_REFERENCE_FILES.length; i++) {
-                InputStream in = getActivity().getAssets().open(LAYOUT_TESTS_RESULTS_REFERENCE_FILES[i]);
-                OutputStream out = new FileOutputStream(out_dir + LAYOUT_TESTS_RESULTS_REFERENCE_FILES[i]);
+                InputStream in = targetContext.getAssets().open(
+                        LAYOUT_TESTS_RESULTS_REFERENCE_FILES[i]);
+                OutputStream out = new FileOutputStream(new File(cacheDir,
+                        LAYOUT_TESTS_RESULTS_REFERENCE_FILES[i]));
 
                 byte[] buf = new byte[2048];
                 int len;
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
index 9352f39..4982c46 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
@@ -19,6 +19,7 @@
 import dalvik.system.VMRuntime;
 
 import android.app.Instrumentation;
+import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Debug;
@@ -27,6 +28,7 @@
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
 
+import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -202,14 +204,14 @@
 
     public void copyRunnerAssetsToCache() {
         try {
-            String out_dir = getActivity().getApplicationContext()
-                .getCacheDir().getPath() + "/";
+            Context targetContext = getInstrumentation().getTargetContext();
+            File cacheDir = targetContext.getCacheDir();
 
             for( int i=0; i< LOAD_TEST_RUNNER_FILES.length; i++) {
-                InputStream in = getActivity().getAssets().open(
+                InputStream in = targetContext.getAssets().open(
                         LOAD_TEST_RUNNER_FILES[i]);
                 OutputStream out = new FileOutputStream(
-                        out_dir + LOAD_TEST_RUNNER_FILES[i]);
+                        new File(cacheDir, LOAD_TEST_RUNNER_FILES[i]));
 
                 byte[] buf = new byte[2048];
                 int len;
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 7e69ee4..44c7d72 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -173,22 +173,23 @@
                 mStatusBarManager.disable(StatusBarManager.DISABLE_NAVIGATION);
             }
         },
+        new Test("Disable Clock") {
+            public void run() {
+                mStatusBarManager.disable(StatusBarManager.DISABLE_CLOCK);
+            }
+        },
         new Test("Disable everything in 3 sec") {
             public void run() {
                 mHandler.postDelayed(new Runnable() {
                         public void run() {
-                            mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND
-                                    | StatusBarManager.DISABLE_NOTIFICATION_ICONS
-                                    | StatusBarManager.DISABLE_NOTIFICATION_ALERTS
-                                    | StatusBarManager.DISABLE_SYSTEM_INFO
-                                    | StatusBarManager.DISABLE_NAVIGATION);
+                            mStatusBarManager.disable(~StatusBarManager.DISABLE_NONE);
                         }
                     }, 3000);
             }
         },
         new Test("Enable everything") {
             public void run() {
-                mStatusBarManager.disable(0);
+                mStatusBarManager.disable(StatusBarManager.DISABLE_NONE);
             }
         },
         new Test("Enable everything in 3 sec.") {
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
index 212223c..c36b37b 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import com.android.layoutlib.api.IDensityBasedResourceValue.Density;
+import com.android.layoutlib.bridge.Bridge;
 
 import android.content.res.AssetManager;
 import android.content.res.Resources;
@@ -448,7 +449,9 @@
         Bitmap  bm;
 
         if (is instanceof AssetManager.AssetInputStream) {
-            // FIXME: log this.
+            Bridge.getLog().error(null,
+                    "Bitmap.decodeStream: " +
+                    "InputStream is unsupported (AssetManager.AssetInputStream)");
             return null;
         } else {
             // pass some temp storage down to the native code. 1024 is made up,
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index bc6ad64..b116d2b 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -16,7 +16,7 @@
 
 package android.graphics;
 
-import com.android.layoutlib.api.ILayoutLog;
+import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.layoutlib.bridge.impl.Stack;
 
@@ -59,7 +59,6 @@
     // ---- delegate data ----
     private BufferedImage mBufferedImage;
     private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>();
-    private ILayoutLog mLogger;
 
     // ---- Public Helper methods ----
 
@@ -78,14 +77,6 @@
     }
 
     /**
-     * Sets the layoutlib logger into the canvas.
-     * @param logger
-     */
-    public void setLogger(ILayoutLog logger) {
-        mLogger = logger;
-    }
-
-    /**
      * Returns the current {@link Graphics2D} used to draw.
      */
     public Graphics2D getGraphics2d() {
@@ -408,10 +399,11 @@
         // give it to the graphics2D as a new matrix replacing all previous transform
         g.setTransform(matrixTx);
 
-        // FIXME: log
-//        if (mLogger != null && matrixDelegate.hasPerspective()) {
-//            mLogger.warning("android.graphics.Canvas#setMatrix(android.graphics.Matrix) only supports affine transformations in the Layout Editor.");
-//        }
+        if (matrixDelegate.hasPerspective()) {
+            Bridge.getLog().warning(null,
+                    "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " +
+                    "supports affine transformations in the Layout Preview.");
+        }
     }
 
     /*package*/ static boolean native_clipRect(int nCanvas,
@@ -1042,11 +1034,10 @@
                 g.setPaint(shaderPaint);
                 useColorPaint = false;
             } else {
-                if (mLogger != null) {
-                    mLogger.warning(String.format(
-                            "Shader '%1$s' is not supported in the Layout Editor.",
+                Bridge.getLog().warning(null,
+                        String.format(
+                            "Shader '%1$s' is not supported in the Layout Preview.",
                             shaderDelegate.getClass().getCanonicalName()));
-                }
             }
         }
 
@@ -1089,10 +1080,11 @@
             g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
 
             // if xfermode wasn't null, then it's something we don't support. log it.
-            if (mLogger != null && xfermodeDelegate != null) {
-                mLogger.warning(String.format(
-                        "Xfermode '%1$s' is not supported in the Layout Editor.",
-                        xfermodeDelegate.getClass().getCanonicalName()));
+            if (xfermodeDelegate != null) {
+                Bridge.getLog().warning(null,
+                        String.format(
+                            "Xfermode '%1$s' is not supported in the Layout Preview.",
+                            xfermodeDelegate.getClass().getCanonicalName()));
             }
         }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
index 3d26e47..a6c6dfd 100644
--- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.ninepatch.NinePatchChunk;
 
@@ -71,7 +72,7 @@
             oos = new ObjectOutputStream(baos);
             oos.writeObject(chunk);
         } catch (IOException e) {
-            //FIXME log this.
+            Bridge.getLog().error("Failed to serialize NinePatchChunk.", e);
             return null;
         } finally {
             if (oos != null) {
@@ -205,10 +206,10 @@
                     sChunkCache.put(array, new SoftReference<NinePatchChunk>(chunk));
                 }
             } catch (IOException e) {
-                // FIXME: log this
+                Bridge.getLog().error("Failed to deserialize NinePatchChunk content.", e);
                 return null;
             } catch (ClassNotFoundException e) {
-                // FIXME: log this
+                Bridge.getLog().error("Failed to deserialize NinePatchChunk class.", e);
                 return null;
             } finally {
                 if (ois != null) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 93f757a..c09f8ad 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -340,6 +340,7 @@
         }
 
         delegate.mTextSize = textSize;
+        delegate.updateFontObject();
     }
 
     /*package*/ static float getTextScaleX(Paint thisPaint) {
@@ -362,6 +363,7 @@
         }
 
         delegate.mTextScaleX = scaleX;
+        delegate.updateFontObject();
     }
 
     /*package*/ static float getTextSkewX(Paint thisPaint) {
@@ -384,6 +386,7 @@
         }
 
         delegate.mTextSkewX = skewX;
+        delegate.updateFontObject();
     }
 
     /*package*/ static float ascent(Paint thisPaint) {
@@ -662,7 +665,9 @@
             return 0;
         }
 
-        return delegate.mTypeface = typeface;
+        delegate.mTypeface = typeface;
+        delegate.updateFontObject();
+        return delegate.mTypeface;
     }
 
     /*package*/ static int native_setRasterizer(int native_object, int rasterizer) {
@@ -670,7 +675,6 @@
         throw new UnsupportedOperationException();
     }
 
-
     /*package*/ static int native_getTextAlign(int native_object) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
@@ -810,14 +814,10 @@
 
     private Paint_Delegate() {
         reset();
-
-        mTypeface = Typeface.sDefaults[0].native_instance;
-        updateFontObject();
     }
 
     private Paint_Delegate(Paint_Delegate paint) {
         set(paint);
-        updateFontObject();
     }
 
     private void set(Paint_Delegate paint) {
@@ -838,6 +838,7 @@
         mShader = paint.mShader;
         mPathEffect = paint.mPathEffect;
         mMaskFilter = paint.mMaskFilter;
+        updateFontObject();
     }
 
     private void reset() {
@@ -847,7 +848,7 @@
         mCap = 0;
         mJoin = 0;
         mTextAlign = 0;
-        mTypeface = 0;
+        mTypeface = Typeface.sDefaults[0].native_instance;
         mStrokeWidth = 1.f;
         mStrokeMiter = 2.f;
         mTextSize = 20.f;
@@ -858,6 +859,7 @@
         mShader = 0;
         mPathEffect = 0;
         mMaskFilter = 0;
+        updateFontObject();
     }
 
     /**
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 2a94774..38e03ca 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -17,7 +17,7 @@
 package com.android.layoutlib.bridge;
 
 import com.android.layoutlib.api.Capabilities;
-import com.android.layoutlib.api.ILayoutLog;
+import com.android.layoutlib.api.LayoutLog;
 import com.android.layoutlib.api.IProjectCallback;
 import com.android.layoutlib.api.IResourceValue;
 import com.android.layoutlib.api.IXmlPullParser;
@@ -133,14 +133,16 @@
     private final static IntArray sIntArrayWrapper = new IntArray();
 
     /**
-     * A default logger than prints to stdout/stderr.
+     * A default log than prints to stdout/stderr.
      */
-    private final static ILayoutLog sDefaultLogger = new ILayoutLog() {
-        public void error(String message) {
+    private final static LayoutLog sDefaultLog = new LayoutLog() {
+        @Override
+        public void error(String tag, String message) {
             System.err.println(message);
         }
 
-        public void error(Throwable t) {
+        @Override
+        public void error(String tag, Throwable t) {
             String message = t.getMessage();
             if (message == null) {
                 message = t.getClass().getName();
@@ -149,11 +151,23 @@
             System.err.println(message);
         }
 
-        public void warning(String message) {
+        @Override
+        public void error(String tag, String message, Throwable throwable) {
+            System.err.println(message);
+        }
+
+        @Override
+        public void warning(String tag, String message) {
             System.out.println(message);
         }
     };
 
+    /**
+     * Current log.
+     */
+    private static LayoutLog sCurrentLog = sDefaultLog;
+
+
     private EnumSet<Capabilities> mCapabilities;
 
 
@@ -203,7 +217,7 @@
             OverrideMethod.setDefaultListener(new MethodAdapter() {
                 @Override
                 public void onInvokeV(String signature, boolean isNative, Object caller) {
-                    sDefaultLogger.error("Missing Stub: " + signature +
+                    sDefaultLog.error(null, "Missing Stub: " + signature +
                             (isNative ? " (native)" : ""));
 
                     if (debug.equalsIgnoreCase("throw")) {
@@ -311,7 +325,7 @@
     @Override
     public BridgeLayoutScene createScene(SceneParams params) {
         try {
-            SceneResult lastResult = SceneStatus.SUCCESS.getResult();
+            SceneResult lastResult = SceneStatus.SUCCESS.createResult();
             LayoutSceneImpl scene = new LayoutSceneImpl(params);
             try {
                 prepareThread();
@@ -335,7 +349,7 @@
                 t2 = t.getCause();
             }
             return new BridgeLayoutScene(null,
-                    SceneStatus.ERROR_UNKNOWN.getResult(t2.getMessage(), t2));
+                    SceneStatus.ERROR_UNKNOWN.createResult(t2.getMessage(), t2));
         }
     }
 
@@ -383,6 +397,23 @@
         Looper.sThreadLocal.remove();
     }
 
+    public static LayoutLog getLog() {
+        return sCurrentLog;
+    }
+
+    public static void setLog(LayoutLog log) {
+        // check only the thread currently owning the lock can do this.
+        if (sLock.isHeldByCurrentThread() == false) {
+            throw new IllegalStateException("scene must be acquired first. see #acquire(long)");
+        }
+
+        if (log != null) {
+            sCurrentLog = log;
+        } else {
+            sCurrentLog = sDefaultLog;
+        }
+    }
+
     /**
      * Returns details of a framework resource from its integer value.
      * @param value the integer value
@@ -391,7 +422,6 @@
      */
     public static String[] resolveResourceValue(int value) {
         return sRMap.get(value);
-
     }
 
     /**
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 688f240..32b2fd4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -16,7 +16,6 @@
 
 package com.android.layoutlib.bridge.android;
 
-import com.android.layoutlib.api.ILayoutLog;
 import com.android.layoutlib.api.IProjectCallback;
 import com.android.layoutlib.api.IResourceValue;
 import com.android.layoutlib.api.IStyleResourceValue;
@@ -97,7 +96,6 @@
     private BridgeInflater mInflater;
 
     private final IProjectCallback mProjectCallback;
-    private final ILayoutLog mLogger;
     private BridgeContentResolver mContentResolver;
 
     private final Stack<BridgeXmlBlockParser> mParserStack = new Stack<BridgeXmlBlockParser>();
@@ -122,11 +120,10 @@
             Map<String, Map<String, IResourceValue>> projectResources,
             Map<String, Map<String, IResourceValue>> frameworkResources,
             Map<IStyleResourceValue, IStyleResourceValue> styleInheritanceMap,
-            IProjectCallback projectCallback, ILayoutLog logger) {
+            IProjectCallback projectCallback) {
         mProjectKey = projectKey;
         mMetrics = metrics;
         mProjectCallback = projectCallback;
-        mLogger = logger;
 
         mThemeValues = currentTheme;
         mProjectResources = projectResources;
@@ -183,10 +180,6 @@
         return mProjectCallback;
     }
 
-    public ILayoutLog getLogger() {
-        return mLogger;
-    }
-
     public Map<String, String> getDefaultPropMap(Object key) {
         return mDefaultPropMaps.get(key);
     }
@@ -340,7 +333,7 @@
             // good, nothing to do.
         } else if (set != null) { // null parser is ok
             // really this should not be happening since its instantiated in Bridge
-            mLogger.error("Parser is not a BridgeXmlBlockParser!");
+            Bridge.getLog().error(null, "Parser is not a BridgeXmlBlockParser!");
             return null;
         }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
index d9e26e2..057c9c3 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
@@ -178,7 +178,7 @@
 
                         return inflate(bridgeParser, root);
                     } catch (Exception e) {
-                        bridgeContext.getLogger().error(e);
+                        Bridge.getLog().error(null, e);
                         // return null below.
                     }
                 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
index affd1c6..678be9c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
@@ -174,11 +174,11 @@
                         return ColorStateList.createFromXml(this,
                                 new BridgeXmlBlockParser(parser, mContext, resValue.isFramework()));
                     } catch (XmlPullParserException e) {
-                        mContext.getLogger().error(e);
+                        Bridge.getLog().error(null, e);
                     } catch (FileNotFoundException e) {
                         // will not happen, since we pre-check
                     } catch (IOException e) {
-                        mContext.getLogger().error(e);
+                        Bridge.getLog().error(null, e);
                     }
 
                 } else {
@@ -245,7 +245,7 @@
                     return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
                 }
             } catch (XmlPullParserException e) {
-                mContext.getLogger().error(e);
+                Bridge.getLog().error(null, e);
                 // we'll return null below.
             } catch (FileNotFoundException e) {
                 // this shouldn't happen since we check above.
@@ -279,7 +279,7 @@
                     return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
                 }
             } catch (XmlPullParserException e) {
-                mContext.getLogger().error(e);
+                Bridge.getLog().error(null, e);
                 // we'll return null below.
             } catch (FileNotFoundException e) {
                 // this shouldn't happen since we check above.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
index c8dc9e6..800256b2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
@@ -205,7 +205,7 @@
                 if (i != null) {
                     result |= i.intValue();
                 } else {
-                    mContext.getLogger().warning(String.format(
+                    Bridge.getLog().warning(null, String.format(
                             "Unknown constant \"%s\" in attribute \"%2$s\"",
                             keyword, mNames[index]));
                 }
@@ -235,7 +235,7 @@
             try {
                 return Float.parseFloat(s);
             } catch (NumberFormatException e) {
-                mContext.getLogger().warning(String.format(
+                Bridge.getLog().warning(null, String.format(
                         "Unable to convert \"%s\" into a float in attribute \"%2$s\"",
                         s, mNames[index]));
 
@@ -267,7 +267,7 @@
         try {
             return ResourceHelper.getColor(s);
         } catch (NumberFormatException e) {
-            mContext.getLogger().warning(String.format(
+            Bridge.getLog().warning(null, String.format(
                     "Unable to convert \"%s\" into a color in attribute \"%2$s\"",
                     s, mNames[index]));
 
@@ -322,13 +322,13 @@
         } catch (Exception e) {
             // this is an error and not warning since the file existence is checked before
             // attempting to parse it.
-            mContext.getLogger().error(e);
+            Bridge.getLog().error(null, e);
 
             // return null below.
         }
 
         // looks like were unable to resolve the color value.
-        mContext.getLogger().warning(String.format(
+        Bridge.getLog().warning(null, String.format(
                 "Unable to resolve color value \"%1$s\" in attribute \"%2$s\"",
                 value, mNames[index]));
 
@@ -356,7 +356,7 @@
             try {
                 return Integer.parseInt(s);
             } catch (NumberFormatException e) {
-                mContext.getLogger().warning(String.format(
+                Bridge.getLog().warning(null, String.format(
                         "Unable to convert \"%s\" into a integer in attribute \"%2$s\"",
                         s, mNames[index]));
 
@@ -405,7 +405,7 @@
         }
 
         // looks like we were unable to resolve the dimension value
-        mContext.getLogger().warning(String.format(
+        Bridge.getLog().warning(null, String.format(
                 "Unable to resolve dimension value \"%1$s\" in attribute \"%2$s\"",
                 s, mNames[index]));
 
@@ -534,7 +534,7 @@
         }
 
         // looks like we were unable to resolve the fraction value
-        mContext.getLogger().warning(String.format(
+        Bridge.getLog().warning(null, String.format(
                 "Unable to resolve fraction value \"%1$s\" in attribute \"%2$s\"",
                 value, mNames[index]));
 
@@ -641,7 +641,7 @@
             return idValue.intValue();
         }
 
-        mContext.getLogger().warning(String.format(
+        Bridge.getLog().warning(null, String.format(
                 "Unable to resolve id \"%1$s\" for attribute \"%2$s\"", value, mNames[index]));
         return defValue;
     }
@@ -675,7 +675,7 @@
         }
 
         // looks like we were unable to resolve the drawable
-        mContext.getLogger().warning(String.format(
+        Bridge.getLog().warning(null, String.format(
                 "Unable to resolve drawable \"%1$s\" in attribute \"%2$s\"", stringValue,
                 mNames[index]));
 
@@ -704,7 +704,7 @@
             return new CharSequence[] { value };
         }
 
-        mContext.getLogger().warning(String.format(
+        Bridge.getLog().warning(null, String.format(
                 String.format("Unknown value for getTextArray(%d) => %s", //DEBUG
                 index, mData[index].getName())));
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
index 400a05f..4ee813c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
@@ -146,7 +146,7 @@
                 }
             } while (mListener.isCanceled() == false && mQueue.size() > 0);
 
-            mListener.done(SceneStatus.SUCCESS.getResult());
+            mListener.done(SceneStatus.SUCCESS.createResult());
         } finally {
             postAnimation();
             Handler_Delegate.setCallback(null);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
index 359180f..d58cde8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
@@ -50,7 +50,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Bitmap_Delegate;
 import android.graphics.Canvas;
-import android.graphics.Canvas_Delegate;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.util.DisplayMetrics;
@@ -174,7 +173,7 @@
         // build the context
         mContext = new BridgeContext(mParams.getProjectKey(), metrics, mCurrentTheme,
                 mParams.getProjectResources(), mParams.getFrameworkResources(),
-                styleParentMap, mParams.getProjectCallback(), mParams.getLogger());
+                styleParentMap, mParams.getProjectCallback());
 
         // set the current rendering context
         sCurrentContext = mContext;
@@ -202,7 +201,7 @@
         mBlockParser = new BridgeXmlBlockParser(mParams.getLayoutDescription(),
                 mContext, false /* platformResourceFlag */);
 
-        return SceneStatus.SUCCESS.getResult();
+        return SceneStatus.SUCCESS.createResult();
     }
 
     /**
@@ -243,8 +242,9 @@
         // scene
         mContext.initResources();
         sCurrentContext = mContext;
+        Bridge.setLog(mParams.getLog());
 
-        return SUCCESS.getResult();
+        return SUCCESS.createResult();
     }
 
     /**
@@ -267,10 +267,10 @@
                 boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
 
                 if (acquired == false) {
-                    return ERROR_TIMEOUT.getResult();
+                    return ERROR_TIMEOUT.createResult();
                 }
             } catch (InterruptedException e) {
-                return ERROR_LOCK_INTERRUPTED.getResult();
+                return ERROR_LOCK_INTERRUPTED.createResult();
             }
         } else {
             // This thread holds the lock already. Checks that this wasn't for a different context.
@@ -279,7 +279,7 @@
             if (mContext != sCurrentContext) {
                 throw new IllegalStateException("Acquiring different scenes from same thread without releases");
             }
-            return SUCCESS.getResult();
+            return SUCCESS.createResult();
         }
 
         return null;
@@ -297,6 +297,7 @@
         if (lock.isHeldByCurrentThread()) {
             // Make sure to remove static references, otherwise we could not unload the lib
             mContext.disposeResources();
+            Bridge.setLog(null);
             sCurrentContext = null;
 
             lock.unlock();
@@ -344,9 +345,9 @@
                 mViewRoot.setBackgroundDrawable(d);
             }
 
-            return SceneStatus.SUCCESS.getResult();
+            return SceneStatus.SUCCESS.createResult();
         } catch (PostInflateException e) {
-            return SceneStatus.ERROR_INFLATION.getResult(e.getMessage(), e);
+            return SceneStatus.ERROR_INFLATION.createResult(e.getMessage(), e);
         } catch (Throwable e) {
             // get the real cause of the exception.
             Throwable t = e;
@@ -355,9 +356,9 @@
             }
 
             // log it
-            mParams.getLogger().error(t);
+            mParams.getLog().error("Scene inflate failed", t);
 
-            return SceneStatus.ERROR_INFLATION.getResult(t.getMessage(), t);
+            return SceneStatus.ERROR_INFLATION.createResult(t.getMessage(), t);
         }
     }
 
@@ -377,7 +378,7 @@
 
         try {
             if (mViewRoot == null) {
-                return SceneStatus.ERROR_NOT_INFLATED.getResult();
+                return SceneStatus.ERROR_NOT_INFLATED.createResult();
             }
             // measure the views
             int w_spec, h_spec;
@@ -457,10 +458,6 @@
                 mCanvas.setDensity(mParams.getDensity());
             }
 
-            // to set the logger, get the native delegate
-            Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(mCanvas);
-            canvasDelegate.setLogger(mParams.getLogger());
-
             long preDrawTime = System.currentTimeMillis();
 
             mViewRoot.draw(mCanvas);
@@ -472,7 +469,7 @@
             System.out.println(String.format("rendering (ms): %03d", drawTime - preDrawTime));
 
             // success!
-            return SceneStatus.SUCCESS.getResult();
+            return SceneStatus.SUCCESS.createResult();
         } catch (Throwable e) {
             // get the real cause of the exception.
             Throwable t = e;
@@ -481,9 +478,9 @@
             }
 
             // log it
-            mParams.getLogger().error(t);
+            mParams.getLog().error("Scene Render failed", t);
 
-            return SceneStatus.ERROR_UNKNOWN.getResult(t.getMessage(), t);
+            return SceneStatus.ERROR_UNKNOWN.createResult(t.getMessage(), t);
         }
     }
 
@@ -524,7 +521,7 @@
 
                     new PlayAnimationThread(anim, this, animationName, listener).start();
 
-                    return SceneStatus.SUCCESS.getResult();
+                    return SceneStatus.SUCCESS.createResult();
                 }
             } catch (Exception e) {
                 // get the real cause of the exception.
@@ -533,11 +530,11 @@
                     t = t.getCause();
                 }
 
-                return SceneStatus.ERROR_UNKNOWN.getResult(t.getMessage(), t);
+                return SceneStatus.ERROR_UNKNOWN.createResult(t.getMessage(), t);
             }
         }
 
-        return SceneStatus.ERROR_ANIM_NOT_FOUND.getResult();
+        return SceneStatus.ERROR_ANIM_NOT_FOUND.createResult();
     }
 
     /**
@@ -581,7 +578,7 @@
             }.start();
 
             // always return success since the real status will come through the listener.
-            return SceneStatus.SUCCESS.getResult(child);
+            return SceneStatus.SUCCESS.createResult(child);
         }
 
         // add it to the parentView in the correct location
@@ -612,10 +609,10 @@
     private SceneResult addView(ViewGroup parent, View view, int index) {
         try {
             parent.addView(view, index);
-            return SceneStatus.SUCCESS.getResult();
+            return SceneStatus.SUCCESS.createResult();
         } catch (UnsupportedOperationException e) {
             // looks like this is a view class that doesn't support children manipulation!
-            return SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN.getResult();
+            return SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN.createResult();
         }
     }
 
@@ -659,7 +656,7 @@
             }.start();
 
             // always return success since the real status will come through the listener.
-            return SceneStatus.SUCCESS.getResult(layoutParams);
+            return SceneStatus.SUCCESS.createResult(layoutParams);
         }
 
         SceneResult result = moveView(parentView, childView, index, layoutParams);
@@ -701,10 +698,10 @@
                 parent.addView(view, index);
             }
 
-            return SceneStatus.SUCCESS.getResult();
+            return SceneStatus.SUCCESS.createResult();
         } catch (UnsupportedOperationException e) {
             // looks like this is a view class that doesn't support children manipulation!
-            return SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN.getResult();
+            return SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN.createResult();
         }
     }
 
@@ -741,7 +738,7 @@
             }.start();
 
             // always return success since the real status will come through the listener.
-            return SceneStatus.SUCCESS.getResult();
+            return SceneStatus.SUCCESS.createResult();
         }
 
         SceneResult result = removeView(parent, childView);
@@ -764,10 +761,10 @@
     private SceneResult removeView(ViewGroup parent, View view) {
         try {
             parent.removeView(view);
-            return SceneStatus.SUCCESS.getResult();
+            return SceneStatus.SUCCESS.createResult();
         } catch (UnsupportedOperationException e) {
             // looks like this is a view class that doesn't support children manipulation!
-            return SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN.getResult();
+            return SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN.createResult();
         }
     }
 
@@ -927,7 +924,7 @@
             return (IStyleResourceValue)parent;
         }
 
-        mParams.getLogger().error(
+        mParams.getLog().error(null,
                 String.format("Unable to resolve parent style name: %s", parentName));
 
         return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java
index bbc7f4b..2e2c5f4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java
@@ -38,7 +38,7 @@
         // the queue is filled when this method returns.
         mAnimator.start();
 
-        return SceneStatus.SUCCESS.getResult();
+        return SceneStatus.SUCCESS.createResult();
     }
 
     @Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index ceb8a0d..5a4a0a5 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -17,8 +17,8 @@
 package com.android.layoutlib.bridge.impl;
 
 import com.android.layoutlib.api.IDensityBasedResourceValue;
-import com.android.layoutlib.api.IDensityBasedResourceValue.Density;
 import com.android.layoutlib.api.IResourceValue;
+import com.android.layoutlib.api.IDensityBasedResourceValue.Density;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
@@ -188,11 +188,11 @@
                             new BridgeXmlBlockParser(parser, context, isFramework));
                     return d;
                 } catch (XmlPullParserException e) {
-                    context.getLogger().error(e);
+                    Bridge.getLog().error(null, e);
                 } catch (FileNotFoundException e) {
                     // will not happen, since we pre-check
                 } catch (IOException e) {
-                    context.getLogger().error(e);
+                    Bridge.getLog().error(null, e);
                 }
             }
 
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 4828700b..18385b7 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -924,7 +924,9 @@
     }
 
     /**
-     * TODO: doc
+     * Request a wakelock with connectivity service to
+     * keep the device awake until we hand-off from wifi
+     * to an alternate network
      */
     public void requestCmWakeLock() {
         sendMessage(CMD_REQUEST_CM_WAKELOCK);
@@ -2143,6 +2145,8 @@
                     Log.d(TAG, "set frequency band " + band);
                     if (WifiNative.setBandCommand(band)) {
                         mFrequencyBand.set(band);
+                        //Fetch the latest scan results when frequency band is set
+                        startScan(true);
                     } else {
                         Log.e(TAG, "Failed to set frequency band " + band);
                     }
@@ -2153,13 +2157,6 @@
                     transitionTo(mDriverStoppingState);
                     mWakeLock.release();
                     break;
-                case CMD_REQUEST_CM_WAKELOCK:
-                    if (mCm == null) {
-                        mCm = (ConnectivityManager)mContext.getSystemService(
-                                Context.CONNECTIVITY_SERVICE);
-                    }
-                    mCm.requestNetworkTransitionWakelock(TAG);
-                    break;
                 case CMD_START_PACKET_FILTERING:
                     WifiNative.startPacketFiltering();
                     break;
@@ -2637,6 +2634,13 @@
                     sendMessage(CMD_DISCONNECT);
                     deferMessage(message);
                     break;
+                case CMD_REQUEST_CM_WAKELOCK:
+                    if (mCm == null) {
+                        mCm = (ConnectivityManager)mContext.getSystemService(
+                                Context.CONNECTIVITY_SERVICE);
+                    }
+                    mCm.requestNetworkTransitionWakelock(TAG);
+                    break;
                 case CMD_SET_SCAN_MODE:
                     if (message.arg1 == SCAN_ONLY_MODE) {
                         sendMessage(CMD_DISCONNECT);