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);