Merge "Work on more low memory reporting to apps."
diff --git a/api/current.txt b/api/current.txt
index 261d9ab..1c4190e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6909,7 +6909,7 @@
method public boolean onMove(int, int);
}
- public abstract interface Cursor {
+ public abstract interface Cursor implements java.io.Closeable {
method public abstract void close();
method public abstract void copyStringToBuffer(int, android.database.CharArrayBuffer);
method public abstract deprecated void deactivate();
@@ -6982,7 +6982,6 @@
ctor public deprecated CursorWindow(boolean);
method public boolean allocRow();
method public void clear();
- method public void close();
method public void copyStringToBuffer(int, int, android.database.CharArrayBuffer);
method public int describeContents();
method public void freeLastRow();
@@ -7241,13 +7240,14 @@
ctor public SQLiteCantOpenDatabaseException(java.lang.String);
}
- public abstract class SQLiteClosable {
+ public abstract class SQLiteClosable implements java.io.Closeable {
ctor public SQLiteClosable();
method public void acquireReference();
+ method public void close();
method protected abstract void onAllReferencesReleased();
- method protected void onAllReferencesReleasedFromContainer();
+ method protected deprecated void onAllReferencesReleasedFromContainer();
method public void releaseReference();
- method public void releaseReferenceFromContainer();
+ method public deprecated void releaseReferenceFromContainer();
}
public class SQLiteConstraintException extends android.database.sqlite.SQLiteException {
@@ -7277,7 +7277,6 @@
method public void beginTransactionNonExclusive();
method public void beginTransactionWithListener(android.database.sqlite.SQLiteTransactionListener);
method public void beginTransactionWithListenerNonExclusive(android.database.sqlite.SQLiteTransactionListener);
- method public void close();
method public android.database.sqlite.SQLiteStatement compileStatement(java.lang.String) throws android.database.SQLException;
method public static android.database.sqlite.SQLiteDatabase create(android.database.sqlite.SQLiteDatabase.CursorFactory);
method public int delete(java.lang.String, java.lang.String, java.lang.String[]);
@@ -7420,7 +7419,6 @@
method public void bindNull(int);
method public void bindString(int, java.lang.String);
method public void clearBindings();
- method public void close();
method public final deprecated int getUniqueId();
method protected void onAllReferencesReleased();
}
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index 59ec89d..907833d 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -20,6 +20,8 @@
import android.net.Uri;
import android.os.Bundle;
+import java.io.Closeable;
+
/**
* This interface provides random read-write access to the result set returned
* by a database query.
@@ -27,7 +29,7 @@
* Cursor implementations are not required to be synchronized so code using a Cursor from multiple
* threads should perform its own synchronization when using the Cursor.
*/
-public interface Cursor {
+public interface Cursor extends Closeable {
/*
* Values returned by {@link #getType(int)}.
* These should be consistent with the corresponding types defined in CursorWindow.h
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 85f570c1..f1f3017 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -169,14 +169,6 @@
}
/**
- * Closes the cursor window and frees its underlying resources when all other
- * remaining references have been released.
- */
- public void close() {
- releaseReference();
- }
-
- /**
* Clears out the existing contents of the window, making it safe to reuse
* for new data.
* <p>
@@ -703,8 +695,13 @@
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mStartPos);
- nativeWriteToParcel(mWindowPtr, dest);
+ acquireReference();
+ try {
+ dest.writeInt(mStartPos);
+ nativeWriteToParcel(mWindowPtr, dest);
+ } finally {
+ releaseReference();
+ }
if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
releaseReference();
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 0022118..99d260e 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -269,63 +269,56 @@
if (position < 0 || position >= cursor.getCount()) {
return;
}
- window.acquireReference();
- try {
- final int oldPos = cursor.getPosition();
- final int numColumns = cursor.getColumnCount();
- window.clear();
- window.setStartPosition(position);
- window.setNumColumns(numColumns);
- if (cursor.moveToPosition(position)) {
- do {
- if (!window.allocRow()) {
- break;
- }
- for (int i = 0; i < numColumns; i++) {
- final int type = cursor.getType(i);
- final boolean success;
- switch (type) {
- case Cursor.FIELD_TYPE_NULL:
- success = window.putNull(position, i);
- break;
+ final int oldPos = cursor.getPosition();
+ final int numColumns = cursor.getColumnCount();
+ window.clear();
+ window.setStartPosition(position);
+ window.setNumColumns(numColumns);
+ if (cursor.moveToPosition(position)) {
+ do {
+ if (!window.allocRow()) {
+ break;
+ }
+ for (int i = 0; i < numColumns; i++) {
+ final int type = cursor.getType(i);
+ final boolean success;
+ switch (type) {
+ case Cursor.FIELD_TYPE_NULL:
+ success = window.putNull(position, i);
+ break;
- case Cursor.FIELD_TYPE_INTEGER:
- success = window.putLong(cursor.getLong(i), position, i);
- break;
+ case Cursor.FIELD_TYPE_INTEGER:
+ success = window.putLong(cursor.getLong(i), position, i);
+ break;
- case Cursor.FIELD_TYPE_FLOAT:
- success = window.putDouble(cursor.getDouble(i), position, i);
- break;
+ case Cursor.FIELD_TYPE_FLOAT:
+ success = window.putDouble(cursor.getDouble(i), position, i);
+ break;
- case Cursor.FIELD_TYPE_BLOB: {
- final byte[] value = cursor.getBlob(i);
- success = value != null ? window.putBlob(value, position, i)
- : window.putNull(position, i);
- break;
- }
-
- default: // assume value is convertible to String
- case Cursor.FIELD_TYPE_STRING: {
- final String value = cursor.getString(i);
- success = value != null ? window.putString(value, position, i)
- : window.putNull(position, i);
- break;
- }
+ case Cursor.FIELD_TYPE_BLOB: {
+ final byte[] value = cursor.getBlob(i);
+ success = value != null ? window.putBlob(value, position, i)
+ : window.putNull(position, i);
+ break;
}
- if (!success) {
- window.freeLastRow();
+
+ default: // assume value is convertible to String
+ case Cursor.FIELD_TYPE_STRING: {
+ final String value = cursor.getString(i);
+ success = value != null ? window.putString(value, position, i)
+ : window.putNull(position, i);
break;
}
}
- position += 1;
- } while (cursor.moveToNext());
- }
- cursor.moveToPosition(oldPos);
- } catch (IllegalStateException e){
- // simply ignore it
- } finally {
- window.releaseReference();
+ if (!success) {
+ window.freeLastRow();
+ break;
+ }
+ }
+ position += 1;
+ } while (cursor.moveToNext());
}
+ cursor.moveToPosition(oldPos);
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java
index 7e91a7b..adfbc6e 100644
--- a/core/java/android/database/sqlite/SQLiteClosable.java
+++ b/core/java/android/database/sqlite/SQLiteClosable.java
@@ -16,15 +16,39 @@
package android.database.sqlite;
+import java.io.Closeable;
+
/**
* An object created from a SQLiteDatabase that can be closed.
+ *
+ * This class implements a primitive reference counting scheme for database objects.
*/
-public abstract class SQLiteClosable {
+public abstract class SQLiteClosable implements Closeable {
private int mReferenceCount = 1;
+ /**
+ * Called when the last reference to the object was released by
+ * a call to {@link #releaseReference()} or {@link #close()}.
+ */
protected abstract void onAllReferencesReleased();
- protected void onAllReferencesReleasedFromContainer() {}
+ /**
+ * Called when the last reference to the object was released by
+ * a call to {@link #releaseReferenceFromContainer()}.
+ *
+ * @deprecated Do not use.
+ */
+ @Deprecated
+ protected void onAllReferencesReleasedFromContainer() {
+ onAllReferencesReleased();
+ }
+
+ /**
+ * Acquires a reference to the object.
+ *
+ * @throws IllegalStateException if the last reference to the object has already
+ * been released.
+ */
public void acquireReference() {
synchronized(this) {
if (mReferenceCount <= 0) {
@@ -35,6 +59,12 @@
}
}
+ /**
+ * Releases a reference to the object, closing the object if the last reference
+ * was released.
+ *
+ * @see #onAllReferencesReleased()
+ */
public void releaseReference() {
boolean refCountIsZero = false;
synchronized(this) {
@@ -45,6 +75,14 @@
}
}
+ /**
+ * Releases a reference to the object that was owned by the container of the object,
+ * closing the object if the last reference was released.
+ *
+ * @see #onAllReferencesReleasedFromContainer()
+ * @deprecated Do not use.
+ */
+ @Deprecated
public void releaseReferenceFromContainer() {
boolean refCountIsZero = false;
synchronized(this) {
@@ -54,4 +92,17 @@
onAllReferencesReleasedFromContainer();
}
}
+
+ /**
+ * Releases a reference to the object, closing the object if the last reference
+ * was released.
+ *
+ * Calling this method is equivalent to calling {@link #releaseReference}.
+ *
+ * @see #releaseReference()
+ * @see #onAllReferencesReleased()
+ */
+ public void close() {
+ releaseReference();
+ }
}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index d16f29f..0db3e4f 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -704,44 +704,49 @@
throw new IllegalArgumentException("window must not be null.");
}
- int actualPos = -1;
- int countedRows = -1;
- int filledRows = -1;
- final int cookie = mRecentOperations.beginOperation("executeForCursorWindow",
- sql, bindArgs);
+ window.acquireReference();
try {
- final PreparedStatement statement = acquirePreparedStatement(sql);
+ int actualPos = -1;
+ int countedRows = -1;
+ int filledRows = -1;
+ final int cookie = mRecentOperations.beginOperation("executeForCursorWindow",
+ sql, bindArgs);
try {
- throwIfStatementForbidden(statement);
- bindArguments(statement, bindArgs);
- applyBlockGuardPolicy(statement);
- attachCancellationSignal(cancellationSignal);
+ final PreparedStatement statement = acquirePreparedStatement(sql);
try {
- final long result = nativeExecuteForCursorWindow(
- mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
- startPos, requiredPos, countAllRows);
- actualPos = (int)(result >> 32);
- countedRows = (int)result;
- filledRows = window.getNumRows();
- window.setStartPosition(actualPos);
- return countedRows;
+ throwIfStatementForbidden(statement);
+ bindArguments(statement, bindArgs);
+ applyBlockGuardPolicy(statement);
+ attachCancellationSignal(cancellationSignal);
+ try {
+ final long result = nativeExecuteForCursorWindow(
+ mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
+ startPos, requiredPos, countAllRows);
+ actualPos = (int)(result >> 32);
+ countedRows = (int)result;
+ filledRows = window.getNumRows();
+ window.setStartPosition(actualPos);
+ return countedRows;
+ } finally {
+ detachCancellationSignal(cancellationSignal);
+ }
} finally {
- detachCancellationSignal(cancellationSignal);
+ releasePreparedStatement(statement);
}
+ } catch (RuntimeException ex) {
+ mRecentOperations.failOperation(cookie, ex);
+ throw ex;
} finally {
- releasePreparedStatement(statement);
+ if (mRecentOperations.endOperationDeferLog(cookie)) {
+ mRecentOperations.logOperation(cookie, "window='" + window
+ + "', startPos=" + startPos
+ + ", actualPos=" + actualPos
+ + ", filledRows=" + filledRows
+ + ", countedRows=" + countedRows);
+ }
}
- } catch (RuntimeException ex) {
- mRecentOperations.failOperation(cookie, ex);
- throw ex;
} finally {
- if (mRecentOperations.endOperationDeferLog(cookie)) {
- mRecentOperations.logOperation(cookie, "window='" + window
- + "', startPos=" + startPos
- + ", actualPos=" + actualPos
- + ", filledRows=" + filledRows
- + ", countedRows=" + countedRows);
- }
+ window.releaseReference();
}
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 604247e..d41b484 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -492,9 +492,16 @@
private void beginTransaction(SQLiteTransactionListener transactionListener,
boolean exclusive) {
- getThreadSession().beginTransaction(exclusive ? SQLiteSession.TRANSACTION_MODE_EXCLUSIVE :
- SQLiteSession.TRANSACTION_MODE_IMMEDIATE, transactionListener,
- getThreadDefaultConnectionFlags(false /*readOnly*/), null);
+ acquireReference();
+ try {
+ getThreadSession().beginTransaction(
+ exclusive ? SQLiteSession.TRANSACTION_MODE_EXCLUSIVE :
+ SQLiteSession.TRANSACTION_MODE_IMMEDIATE,
+ transactionListener,
+ getThreadDefaultConnectionFlags(false /*readOnly*/), null);
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -502,7 +509,12 @@
* are committed and rolled back.
*/
public void endTransaction() {
- getThreadSession().endTransaction(null);
+ acquireReference();
+ try {
+ getThreadSession().endTransaction(null);
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -515,7 +527,12 @@
* transaction is already marked as successful.
*/
public void setTransactionSuccessful() {
- getThreadSession().setTransactionSuccessful();
+ acquireReference();
+ try {
+ getThreadSession().setTransactionSuccessful();
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -524,7 +541,12 @@
* @return True if the current thread is in a transaction.
*/
public boolean inTransaction() {
- return getThreadSession().hasTransaction();
+ acquireReference();
+ try {
+ return getThreadSession().hasTransaction();
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -540,7 +562,12 @@
* @return True if the current thread is holding an active connection to the database.
*/
public boolean isDbLockedByCurrentThread() {
- return getThreadSession().hasConnection();
+ acquireReference();
+ try {
+ return getThreadSession().hasConnection();
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -599,7 +626,12 @@
}
private boolean yieldIfContendedHelper(boolean throwIfUnsafe, long sleepAfterYieldDelay) {
- return getThreadSession().yieldTransaction(sleepAfterYieldDelay, throwIfUnsafe, null);
+ acquireReference();
+ try {
+ return getThreadSession().yieldTransaction(sleepAfterYieldDelay, throwIfUnsafe, null);
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -788,13 +820,6 @@
}
/**
- * Close the database.
- */
- public void close() {
- dispose(false);
- }
-
- /**
* Registers a CustomFunction callback as a function that can be called from
* SQLite database triggers.
*
@@ -948,8 +973,12 @@
* {@link SQLiteStatement}s are not synchronized, see the documentation for more details.
*/
public SQLiteStatement compileStatement(String sql) throws SQLException {
- throwIfNotOpen(); // fail fast
- return new SQLiteStatement(this, sql, null);
+ acquireReference();
+ try {
+ return new SQLiteStatement(this, sql, null);
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -1110,12 +1139,16 @@
boolean distinct, String table, String[] columns,
String selection, String[] selectionArgs, String groupBy,
String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
- throwIfNotOpen(); // fail fast
- String sql = SQLiteQueryBuilder.buildQueryString(
- distinct, table, columns, selection, groupBy, having, orderBy, limit);
+ acquireReference();
+ try {
+ String sql = SQLiteQueryBuilder.buildQueryString(
+ distinct, table, columns, selection, groupBy, having, orderBy, limit);
- return rawQueryWithFactory(cursorFactory, sql, selectionArgs,
- findEditTable(table), cancellationSignal);
+ return rawQueryWithFactory(cursorFactory, sql, selectionArgs,
+ findEditTable(table), cancellationSignal);
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -1260,12 +1293,15 @@
public Cursor rawQueryWithFactory(
CursorFactory cursorFactory, String sql, String[] selectionArgs,
String editTable, CancellationSignal cancellationSignal) {
- throwIfNotOpen(); // fail fast
-
- SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
- cancellationSignal);
- return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
- selectionArgs);
+ acquireReference();
+ try {
+ SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
+ cancellationSignal);
+ return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
+ selectionArgs);
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -1384,38 +1420,44 @@
*/
public long insertWithOnConflict(String table, String nullColumnHack,
ContentValues initialValues, int conflictAlgorithm) {
- StringBuilder sql = new StringBuilder();
- sql.append("INSERT");
- sql.append(CONFLICT_VALUES[conflictAlgorithm]);
- sql.append(" INTO ");
- sql.append(table);
- sql.append('(');
+ acquireReference();
+ try {
+ StringBuilder sql = new StringBuilder();
+ sql.append("INSERT");
+ sql.append(CONFLICT_VALUES[conflictAlgorithm]);
+ sql.append(" INTO ");
+ sql.append(table);
+ sql.append('(');
- Object[] bindArgs = null;
- int size = (initialValues != null && initialValues.size() > 0) ? initialValues.size() : 0;
- if (size > 0) {
- bindArgs = new Object[size];
- int i = 0;
- for (String colName : initialValues.keySet()) {
- sql.append((i > 0) ? "," : "");
- sql.append(colName);
- bindArgs[i++] = initialValues.get(colName);
+ Object[] bindArgs = null;
+ int size = (initialValues != null && initialValues.size() > 0)
+ ? initialValues.size() : 0;
+ if (size > 0) {
+ bindArgs = new Object[size];
+ int i = 0;
+ for (String colName : initialValues.keySet()) {
+ sql.append((i > 0) ? "," : "");
+ sql.append(colName);
+ bindArgs[i++] = initialValues.get(colName);
+ }
+ sql.append(')');
+ sql.append(" VALUES (");
+ for (i = 0; i < size; i++) {
+ sql.append((i > 0) ? ",?" : "?");
+ }
+ } else {
+ sql.append(nullColumnHack + ") VALUES (NULL");
}
sql.append(')');
- sql.append(" VALUES (");
- for (i = 0; i < size; i++) {
- sql.append((i > 0) ? ",?" : "?");
- }
- } else {
- sql.append(nullColumnHack + ") VALUES (NULL");
- }
- sql.append(')');
- SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
- try {
- return statement.executeInsert();
+ SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
+ try {
+ return statement.executeInsert();
+ } finally {
+ statement.close();
+ }
} finally {
- statement.close();
+ releaseReference();
}
}
@@ -1430,12 +1472,17 @@
* whereClause.
*/
public int delete(String table, String whereClause, String[] whereArgs) {
- SQLiteStatement statement = new SQLiteStatement(this, "DELETE FROM " + table +
- (!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs);
+ acquireReference();
try {
- return statement.executeUpdateDelete();
+ SQLiteStatement statement = new SQLiteStatement(this, "DELETE FROM " + table +
+ (!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs);
+ try {
+ return statement.executeUpdateDelete();
+ } finally {
+ statement.close();
+ }
} finally {
- statement.close();
+ releaseReference();
}
}
@@ -1470,38 +1517,43 @@
throw new IllegalArgumentException("Empty values");
}
- StringBuilder sql = new StringBuilder(120);
- sql.append("UPDATE ");
- sql.append(CONFLICT_VALUES[conflictAlgorithm]);
- sql.append(table);
- sql.append(" SET ");
-
- // move all bind args to one array
- int setValuesSize = values.size();
- int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
- Object[] bindArgs = new Object[bindArgsSize];
- int i = 0;
- for (String colName : values.keySet()) {
- sql.append((i > 0) ? "," : "");
- sql.append(colName);
- bindArgs[i++] = values.get(colName);
- sql.append("=?");
- }
- if (whereArgs != null) {
- for (i = setValuesSize; i < bindArgsSize; i++) {
- bindArgs[i] = whereArgs[i - setValuesSize];
- }
- }
- if (!TextUtils.isEmpty(whereClause)) {
- sql.append(" WHERE ");
- sql.append(whereClause);
- }
-
- SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
+ acquireReference();
try {
- return statement.executeUpdateDelete();
+ StringBuilder sql = new StringBuilder(120);
+ sql.append("UPDATE ");
+ sql.append(CONFLICT_VALUES[conflictAlgorithm]);
+ sql.append(table);
+ sql.append(" SET ");
+
+ // move all bind args to one array
+ int setValuesSize = values.size();
+ int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
+ Object[] bindArgs = new Object[bindArgsSize];
+ int i = 0;
+ for (String colName : values.keySet()) {
+ sql.append((i > 0) ? "," : "");
+ sql.append(colName);
+ bindArgs[i++] = values.get(colName);
+ sql.append("=?");
+ }
+ if (whereArgs != null) {
+ for (i = setValuesSize; i < bindArgsSize; i++) {
+ bindArgs[i] = whereArgs[i - setValuesSize];
+ }
+ }
+ if (!TextUtils.isEmpty(whereClause)) {
+ sql.append(" WHERE ");
+ sql.append(whereClause);
+ }
+
+ SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
+ try {
+ return statement.executeUpdateDelete();
+ } finally {
+ statement.close();
+ }
} finally {
- statement.close();
+ releaseReference();
}
}
@@ -1579,24 +1631,29 @@
}
private int executeSql(String sql, Object[] bindArgs) throws SQLException {
- if (DatabaseUtils.getSqlStatementType(sql) == DatabaseUtils.STATEMENT_ATTACH) {
- boolean disableWal = false;
- synchronized (mLock) {
- if (!mHasAttachedDbsLocked) {
- mHasAttachedDbsLocked = true;
- disableWal = true;
+ acquireReference();
+ try {
+ if (DatabaseUtils.getSqlStatementType(sql) == DatabaseUtils.STATEMENT_ATTACH) {
+ boolean disableWal = false;
+ synchronized (mLock) {
+ if (!mHasAttachedDbsLocked) {
+ mHasAttachedDbsLocked = true;
+ disableWal = true;
+ }
+ }
+ if (disableWal) {
+ disableWriteAheadLogging();
}
}
- if (disableWal) {
- disableWriteAheadLogging();
- }
- }
- SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
- try {
- return statement.executeUpdateDelete();
+ SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
+ try {
+ return statement.executeUpdateDelete();
+ } finally {
+ statement.close();
+ }
} finally {
- statement.close();
+ releaseReference();
}
}
@@ -1881,26 +1938,32 @@
attachedDbs.add(new Pair<String, String>("main", mConfigurationLocked.path));
return attachedDbs;
}
+
+ acquireReference();
}
- // has attached databases. query sqlite to get the list of attached databases.
- Cursor c = null;
try {
- c = rawQuery("pragma database_list;", null);
- while (c.moveToNext()) {
- // sqlite returns a row for each database in the returned list of databases.
- // in each row,
- // 1st column is the database name such as main, or the database
- // name specified on the "ATTACH" command
- // 2nd column is the database file path.
- attachedDbs.add(new Pair<String, String>(c.getString(1), c.getString(2)));
+ // has attached databases. query sqlite to get the list of attached databases.
+ Cursor c = null;
+ try {
+ c = rawQuery("pragma database_list;", null);
+ while (c.moveToNext()) {
+ // sqlite returns a row for each database in the returned list of databases.
+ // in each row,
+ // 1st column is the database name such as main, or the database
+ // name specified on the "ATTACH" command
+ // 2nd column is the database file path.
+ attachedDbs.add(new Pair<String, String>(c.getString(1), c.getString(2)));
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
}
+ return attachedDbs;
} finally {
- if (c != null) {
- c.close();
- }
+ releaseReference();
}
- return attachedDbs;
}
/**
@@ -1917,35 +1980,38 @@
* false otherwise.
*/
public boolean isDatabaseIntegrityOk() {
- throwIfNotOpen(); // fail fast
-
- List<Pair<String, String>> attachedDbs = null;
+ acquireReference();
try {
- attachedDbs = getAttachedDbs();
- if (attachedDbs == null) {
- throw new IllegalStateException("databaselist for: " + getPath() + " couldn't " +
- "be retrieved. probably because the database is closed");
- }
- } catch (SQLiteException e) {
- // can't get attachedDb list. do integrity check on the main database
- attachedDbs = new ArrayList<Pair<String, String>>();
- attachedDbs.add(new Pair<String, String>("main", getPath()));
- }
-
- for (int i = 0; i < attachedDbs.size(); i++) {
- Pair<String, String> p = attachedDbs.get(i);
- SQLiteStatement prog = null;
+ List<Pair<String, String>> attachedDbs = null;
try {
- prog = compileStatement("PRAGMA " + p.first + ".integrity_check(1);");
- String rslt = prog.simpleQueryForString();
- if (!rslt.equalsIgnoreCase("ok")) {
- // integrity_checker failed on main or attached databases
- Log.e(TAG, "PRAGMA integrity_check on " + p.second + " returned: " + rslt);
- return false;
+ attachedDbs = getAttachedDbs();
+ if (attachedDbs == null) {
+ throw new IllegalStateException("databaselist for: " + getPath() + " couldn't " +
+ "be retrieved. probably because the database is closed");
}
- } finally {
- if (prog != null) prog.close();
+ } catch (SQLiteException e) {
+ // can't get attachedDb list. do integrity check on the main database
+ attachedDbs = new ArrayList<Pair<String, String>>();
+ attachedDbs.add(new Pair<String, String>("main", getPath()));
}
+
+ for (int i = 0; i < attachedDbs.size(); i++) {
+ Pair<String, String> p = attachedDbs.get(i);
+ SQLiteStatement prog = null;
+ try {
+ prog = compileStatement("PRAGMA " + p.first + ".integrity_check(1);");
+ String rslt = prog.simpleQueryForString();
+ if (!rslt.equalsIgnoreCase("ok")) {
+ // integrity_checker failed on main or attached databases
+ Log.e(TAG, "PRAGMA integrity_check on " + p.second + " returned: " + rslt);
+ return false;
+ }
+ } finally {
+ if (prog != null) prog.close();
+ }
+ }
+ } finally {
+ releaseReference();
}
return true;
}
@@ -1955,12 +2021,6 @@
return "SQLiteDatabase: " + getPath();
}
- private void throwIfNotOpen() {
- synchronized (mConnectionPoolLocked) {
- throwIfNotOpenLocked();
- }
- }
-
private void throwIfNotOpenLocked() {
if (mConnectionPoolLocked == null) {
throw new IllegalStateException("The database '" + mConfigurationLocked.label
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 9f0edfb..94a23cb 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -190,13 +190,6 @@
}
/**
- * Release this program's resources, making it invalid.
- */
- public void close() {
- releaseReference();
- }
-
- /**
* Given an array of String bindArgs, this method binds all of them in one single call.
*
* @param bindArgs the String array of bind args, none of which must be null.
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index a52e2ba..715d1f2 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -222,23 +222,27 @@
return wid;
}
- int breakText(int start, int limit, boolean forwards, float width) {
+ int breakText(int limit, boolean forwards, float width) {
float[] w = mWidths;
if (forwards) {
- for (int i = start; i < limit; ++i) {
- if ((width -= w[i]) < 0) {
- return i - start;
- }
+ int i = 0;
+ while (i < limit) {
+ width -= w[i];
+ if (width < 0.0f) break;
+ i++;
}
+ while (i > 0 && mChars[i - 1] == ' ') i--;
+ return i;
} else {
- for (int i = limit; --i >= start;) {
- if ((width -= w[i]) < 0) {
- return limit - i -1;
- }
+ int i = limit - 1;
+ while (i >= 0) {
+ width -= w[i];
+ if (width < 0.0f) break;
+ i--;
}
+ while (i < limit - 1 && mChars[i + 1] == ' ') i++;
+ return limit - i - 1;
}
-
- return limit - start;
}
float measure(int start, int limit) {
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index afae5bb2..270624c 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1091,13 +1091,13 @@
if (avail < 0) {
// it all goes
} else if (where == TruncateAt.START) {
- right = len - mt.breakText(0, len, false, avail);
+ right = len - mt.breakText(len, false, avail);
} else if (where == TruncateAt.END || where == TruncateAt.END_SMALL) {
- left = mt.breakText(0, len, true, avail);
+ left = mt.breakText(len, true, avail);
} else {
- right = len - mt.breakText(0, len, false, avail / 2);
+ right = len - mt.breakText(len, false, avail / 2);
avail -= mt.measure(right, len);
- left = mt.breakText(0, right, true, avail);
+ left = mt.breakText(right, true, avail);
}
if (callback != null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ecfca74..c982d7a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5247,6 +5247,7 @@
if (mNextFocusForwardId == View.NO_ID) return null;
return findViewInsideOutShouldExist(root, mNextFocusForwardId);
case FOCUS_BACKWARD: {
+ if (mID == View.NO_ID) return null;
final int id = mID;
return root.findViewByPredicateInsideOut(this, new Predicate<View>() {
@Override
diff --git a/core/java/android/webkit/ViewStateSerializer.java b/core/java/android/webkit/ViewStateSerializer.java
index a22fc26..e672b62 100644
--- a/core/java/android/webkit/ViewStateSerializer.java
+++ b/core/java/android/webkit/ViewStateSerializer.java
@@ -52,12 +52,12 @@
throws IOException {
DataInputStream dis = new DataInputStream(stream);
int version = dis.readInt();
- if (version != VERSION) {
+ if (version > VERSION) {
throw new IOException("Unexpected version: " + version);
}
int contentWidth = dis.readInt();
int contentHeight = dis.readInt();
- int baseLayer = nativeDeserializeViewState(dis,
+ int baseLayer = nativeDeserializeViewState(version, dis,
new byte[WORKING_STREAM_STORAGE]);
final WebViewCore.DrawData draw = new WebViewCore.DrawData();
@@ -76,7 +76,7 @@
OutputStream stream, byte[] storage);
// Returns a pointer to the BaseLayer
- private static native int nativeDeserializeViewState(
+ private static native int nativeDeserializeViewState(int version,
InputStream stream, byte[] storage);
private ViewStateSerializer() {}
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index c9a3ff1..04a698f 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -1121,7 +1121,7 @@
static final int WEBCORE_INITIALIZED_MSG_ID = 107;
static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 108;
static final int UPDATE_ZOOM_RANGE = 109;
- static final int UNHANDLED_NAV_KEY = 110;
+ static final int TAKE_FOCUS = 110;
static final int CLEAR_TEXT_ENTRY = 111;
static final int UPDATE_TEXT_SELECTION_MSG_ID = 112;
static final int SHOW_RECT_MSG_ID = 113;
@@ -5309,8 +5309,6 @@
if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
&& keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
switchOutDrawHistory();
- letPageHandleNavKey(keyCode, event.getEventTime(), true, event.getMetaState());
- return true;
}
if (isEnterActionKey(keyCode)) {
@@ -5342,7 +5340,7 @@
}
// pass the key to DOM
- mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
+ sendKeyEvent(event);
// return true as DOM handles the key
return true;
}
@@ -5405,12 +5403,6 @@
}
}
- if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
- && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
- letPageHandleNavKey(keyCode, event.getEventTime(), false, event.getMetaState());
- return true;
- }
-
if (isEnterActionKey(keyCode)) {
// remove the long press message first
mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
@@ -5424,7 +5416,7 @@
}
// pass the key to DOM
- mWebViewCore.sendMessage(EventHub.KEY_UP, event);
+ sendKeyEvent(event);
// return true as DOM handles the key
return true;
}
@@ -6956,9 +6948,7 @@
case KeyEvent.KEYCODE_DPAD_LEFT:
return SoundEffectConstants.NAVIGATION_LEFT;
}
- throw new IllegalArgumentException("keyCode must be one of " +
- "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
- "KEYCODE_DPAD_LEFT}.");
+ return 0;
}
private void doTrackball(long time, int metaState) {
@@ -8232,8 +8222,12 @@
case FORM_DID_BLUR:
// TODO: Figure out if this is needed for something (b/6111763)
break;
- case UNHANDLED_NAV_KEY:
- // TODO: Support this (b/6109044)
+ case TAKE_FOCUS:
+ int direction = msg.arg1;
+ View focusSearch = mWebView.focusSearch(direction);
+ if (focusSearch != null && focusSearch != mWebView) {
+ focusSearch.requestFocus();
+ }
break;
case CLEAR_TEXT_ENTRY:
hideSoftKeyboard();
@@ -8529,7 +8523,7 @@
if (mFocusedNode.mHasFocus && !mWebView.isInTouchMode()) {
return !mFocusedNode.mEditable;
}
- if (mInitialHitTestResult.getType() == HitTestResult.UNKNOWN_TYPE) {
+ if (mFocusedNode.mHasFocus && mFocusedNode.mEditable) {
return false;
}
long delay = System.currentTimeMillis() - mTouchHighlightRequested;
@@ -9129,14 +9123,10 @@
*/
private void letPageHandleNavKey(int keyCode, long time, boolean down, int metaState) {
int keyEventAction;
- int eventHubAction;
if (down) {
keyEventAction = KeyEvent.ACTION_DOWN;
- eventHubAction = EventHub.KEY_DOWN;
- mWebView.playSoundEffect(keyCodeToSoundsEffect(keyCode));
} else {
keyEventAction = KeyEvent.ACTION_UP;
- eventHubAction = EventHub.KEY_UP;
}
KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode,
@@ -9144,7 +9134,41 @@
| (metaState & KeyEvent.META_ALT_ON)
| (metaState & KeyEvent.META_SYM_ON)
, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0);
- mWebViewCore.sendMessage(eventHubAction, event);
+ sendKeyEvent(event);
+ }
+
+ private void sendKeyEvent(KeyEvent event) {
+ int direction = 0;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ direction = View.FOCUS_DOWN;
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ direction = View.FOCUS_UP;
+ break;
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ direction = View.FOCUS_LEFT;
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ direction = View.FOCUS_RIGHT;
+ break;
+ case KeyEvent.KEYCODE_TAB:
+ direction = event.isShiftPressed() ? View.FOCUS_BACKWARD : View.FOCUS_FORWARD;
+ break;
+ }
+ if (direction != 0 && mWebView.focusSearch(direction) == null) {
+ // Can't take focus in that direction
+ direction = 0;
+ }
+ int eventHubAction = EventHub.KEY_UP;
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ eventHubAction = EventHub.KEY_DOWN;
+ int sound = keyCodeToSoundsEffect(event.getKeyCode());
+ if (sound != 0) {
+ mWebView.playSoundEffect(sound);
+ }
+ }
+ mWebViewCore.sendMessage(eventHubAction, direction, event);
}
/**
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 65356f5..09aa286c 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -139,6 +139,8 @@
private int mHighMemoryUsageThresholdMb;
private int mHighUsageDeltaMb;
+ private int mChromeCanFocusDirection;
+
// The thread name used to identify the WebCore thread and for use in
// debugging other classes that require operation within the WebCore thread.
/* package */ static final String THREAD_NAME = "WebViewCoreThread";
@@ -344,6 +346,58 @@
}
/**
+ * Called by JNI to advance focus to the next view.
+ */
+ private void chromeTakeFocus(int webkitDirection) {
+ if (mWebView == null) return;
+ Message m = mWebView.mPrivateHandler.obtainMessage(
+ WebViewClassic.TAKE_FOCUS);
+ m.arg1 = mapDirection(webkitDirection);
+ m.sendToTarget();
+ }
+
+ /**
+ * Called by JNI to see if we can take focus in the given direction.
+ */
+ private boolean chromeCanTakeFocus(int webkitDirection) {
+ int direction = mapDirection(webkitDirection);
+ return direction == mChromeCanFocusDirection && direction != 0;
+ }
+
+ /**
+ * Maps a Webkit focus direction to a framework one
+ */
+ private int mapDirection(int webkitDirection) {
+ /*
+ * This is WebKit's FocusDirection enum (from FocusDirection.h)
+ enum FocusDirection {
+ FocusDirectionNone = 0,
+ FocusDirectionForward,
+ FocusDirectionBackward,
+ FocusDirectionUp,
+ FocusDirectionDown,
+ FocusDirectionLeft,
+ FocusDirectionRight
+ };
+ */
+ switch (webkitDirection) {
+ case 1:
+ return View.FOCUS_FORWARD;
+ case 2:
+ return View.FOCUS_BACKWARD;
+ case 3:
+ return View.FOCUS_UP;
+ case 4:
+ return View.FOCUS_DOWN;
+ case 5:
+ return View.FOCUS_LEFT;
+ case 6:
+ return View.FOCUS_RIGHT;
+ }
+ return 0;
+ }
+
+ /**
* Called by JNI. Open a file chooser to upload a file.
* @param acceptType The value of the 'accept' attribute of the
* input tag associated with this file picker.
@@ -1311,11 +1365,11 @@
break;
case KEY_DOWN:
- key((KeyEvent) msg.obj, true);
+ key((KeyEvent) msg.obj, msg.arg1, true);
break;
case KEY_UP:
- key((KeyEvent) msg.obj, false);
+ key((KeyEvent) msg.obj, msg.arg1, false);
break;
case KEY_PRESS:
@@ -1950,11 +2004,12 @@
return mBrowserFrame.saveWebArchive(filename, autoname);
}
- private void key(KeyEvent evt, boolean isDown) {
+ private void key(KeyEvent evt, int canTakeFocusDirection, boolean isDown) {
if (DebugFlags.WEB_VIEW_CORE) {
Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
+ evt);
}
+ mChromeCanFocusDirection = canTakeFocusDirection;
int keyCode = evt.getKeyCode();
int unicodeChar = evt.getUnicodeChar();
@@ -1964,18 +2019,18 @@
unicodeChar = evt.getCharacters().codePointAt(0);
}
- if (!nativeKey(mNativeClass, keyCode, unicodeChar, evt.getRepeatCount(),
+ boolean handled = nativeKey(mNativeClass, keyCode, unicodeChar, evt.getRepeatCount(),
evt.isShiftPressed(), evt.isAltPressed(),
- evt.isSymPressed(), isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
+ evt.isSymPressed(), isDown);
+ mChromeCanFocusDirection = 0;
+ if (!handled && keyCode != KeyEvent.KEYCODE_ENTER) {
if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
&& keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
- if (DebugFlags.WEB_VIEW_CORE) {
- Log.v(LOGTAG, "key: arrow unused by page: " + keyCode);
- }
- if (mWebView != null && evt.isDown()) {
- Message.obtain(mWebView.mPrivateHandler,
- WebViewClassic.UNHANDLED_NAV_KEY, keyCode,
- 0).sendToTarget();
+ if (canTakeFocusDirection != 0 && isDown) {
+ Message m = mWebView.mPrivateHandler.obtainMessage(
+ WebViewClassic.TAKE_FOCUS);
+ m.arg1 = canTakeFocusDirection;
+ m.sendToTarget();
}
return;
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index a10d241..d5c2018 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -27,28 +27,26 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
+import android.widget.TextView;
import android.widget.Toast;
/**
* @hide
*/
-public class ActionMenuItemView extends LinearLayout
+public class ActionMenuItemView extends TextView
implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,
ActionMenuView.ActionMenuChildView {
private static final String TAG = "ActionMenuItemView";
private MenuItemImpl mItemData;
private CharSequence mTitle;
+ private Drawable mIcon;
private MenuBuilder.ItemInvoker mItemInvoker;
- private ImageButton mImageButton;
- private Button mTextButton;
private boolean mAllowTextWithIcon;
private boolean mExpandedFormat;
private int mMinWidth;
+ private int mSavedPaddingLeft;
public ActionMenuItemView(Context context) {
this(context, null);
@@ -68,17 +66,12 @@
mMinWidth = a.getDimensionPixelSize(
com.android.internal.R.styleable.ActionMenuItemView_minWidth, 0);
a.recycle();
- }
- @Override
- public void onFinishInflate() {
- mImageButton = (ImageButton) findViewById(com.android.internal.R.id.imageButton);
- mTextButton = (Button) findViewById(com.android.internal.R.id.textButton);
- mImageButton.setOnClickListener(this);
- mTextButton.setOnClickListener(this);
- mImageButton.setOnLongClickListener(this);
setOnClickListener(this);
setOnLongClickListener(this);
+
+ // Save the inflated padding for later, we'll need it.
+ mSavedPaddingLeft = getPaddingLeft();
}
public MenuItemImpl getItemData() {
@@ -96,13 +89,6 @@
setEnabled(itemData.isEnabled());
}
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- mImageButton.setEnabled(enabled);
- mTextButton.setEnabled(enabled);
- }
-
public void onClick(View v) {
if (mItemInvoker != null) {
mItemInvoker.invokeItem(mItemData);
@@ -135,26 +121,22 @@
}
private void updateTextButtonVisibility() {
- boolean visible = !TextUtils.isEmpty(mTextButton.getText());
- visible &= mImageButton.getDrawable() == null ||
+ boolean visible = !TextUtils.isEmpty(mTitle);
+ visible &= mIcon == null ||
(mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat));
- mTextButton.setVisibility(visible ? VISIBLE : GONE);
+ setText(visible ? mTitle : null);
}
public void setIcon(Drawable icon) {
- mImageButton.setImageDrawable(icon);
- if (icon != null) {
- mImageButton.setVisibility(VISIBLE);
- } else {
- mImageButton.setVisibility(GONE);
- }
+ mIcon = icon;
+ setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
updateTextButtonVisibility();
}
public boolean hasText() {
- return mTextButton.getVisibility() != GONE;
+ return !TextUtils.isEmpty(getText());
}
public void setShortcut(boolean showShortcut, char shortcutKey) {
@@ -164,8 +146,6 @@
public void setTitle(CharSequence title) {
mTitle = title;
- mTextButton.setText(mTitle);
-
setContentDescription(mTitle);
updateTextButtonVisibility();
}
@@ -236,12 +216,17 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final boolean textVisible = hasText();
+ if (textVisible) {
+ setPadding(mSavedPaddingLeft, getPaddingTop(), getPaddingRight(), getPaddingBottom());
+ }
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- final int specSize = MeasureSpec.getSize(widthMeasureSpec);
+ final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
final int oldMeasuredWidth = getMeasuredWidth();
- final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(specSize, mMinWidth)
+ final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(widthSize, mMinWidth)
: mMinWidth;
if (widthMode != MeasureSpec.EXACTLY && mMinWidth > 0 && oldMeasuredWidth < targetWidth) {
@@ -249,5 +234,13 @@
super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
heightMeasureSpec);
}
+
+ if (!textVisible && mIcon != null) {
+ // TextView won't center compound drawables in both dimensions without
+ // a little coercion. Pad in to center the icon after we've measured.
+ final int w = getMeasuredWidth();
+ final int dw = mIcon.getIntrinsicWidth();
+ setPadding((w - dw) / 2, getPaddingTop(), getPaddingRight(), getPaddingBottom());
+ }
}
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 530809b..dca45a9 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -116,9 +116,9 @@
if (!mMaxItemsSet) {
mMaxItems = mContext.getResources().getInteger(
com.android.internal.R.integer.max_action_buttons);
- if (mMenu != null) {
- mMenu.onItemsChanged(true);
- }
+ }
+ if (mMenu != null) {
+ mMenu.onItemsChanged(true);
}
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index 8d8c72c..e00fe9f 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -96,6 +96,13 @@
if (mFormatItems) {
onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec);
} else {
+ // Previous measurement at exact format may have set margins - reset them.
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ lp.leftMargin = lp.rightMargin = 0;
+ }
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 2f325bf..8c05459 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -324,13 +324,31 @@
if (mSplitView != null) {
mSplitView.addView(mMenuView);
}
+ mMenuView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
} else {
addView(mMenuView);
+ mMenuView.getLayoutParams().width = LayoutParams.WRAP_CONTENT;
}
+ mMenuView.requestLayout();
}
if (mSplitView != null) {
mSplitView.setVisibility(splitActionBar ? VISIBLE : GONE);
}
+
+ if (mActionMenuPresenter != null) {
+ if (!splitActionBar) {
+ mActionMenuPresenter.setExpandedActionViewsExclusive(
+ getResources().getBoolean(
+ com.android.internal.R.bool.action_bar_expanded_action_views_exclusive));
+ } else {
+ mActionMenuPresenter.setExpandedActionViewsExclusive(false);
+ // Allow full screen width in split mode.
+ mActionMenuPresenter.setWidthLimit(
+ getContext().getResources().getDisplayMetrics().widthPixels, true);
+ // No limit to the item count; use whatever will fit.
+ mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
+ }
+ }
super.setSplitActionBar(splitActionBar);
}
}
diff --git a/core/res/res/layout/action_menu_item_layout.xml b/core/res/res/layout/action_menu_item_layout.xml
index dca6c52..ba7cf3b 100644
--- a/core/res/res/layout/action_menu_item_layout.xml
+++ b/core/res/res/layout/action_menu_item_layout.xml
@@ -18,39 +18,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:addStatesFromChildren="true"
android:gravity="center"
android:focusable="true"
- android:paddingLeft="4dip"
- android:paddingRight="4dip"
- style="?android:attr/actionButtonStyle">
- <ImageButton android:id="@+id/imageButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:visibility="gone"
- android:layout_marginTop="4dip"
- android:layout_marginBottom="4dip"
- android:layout_marginLeft="4dip"
- android:layout_marginRight="4dip"
- android:scaleType="fitCenter"
- android:adjustViewBounds="true"
- android:background="@null"
- android:focusable="false" />
- <Button android:id="@+id/textButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:visibility="gone"
- android:textAppearance="?attr/actionMenuTextAppearance"
- style="?attr/buttonStyleSmall"
- android:textColor="?attr/actionMenuTextColor"
- android:singleLine="true"
- android:ellipsize="none"
- android:background="@null"
- android:paddingTop="4dip"
- android:paddingBottom="4dip"
- android:paddingLeft="4dip"
- android:paddingRight="4dip"
- android:focusable="false" />
-</com.android.internal.view.menu.ActionMenuItemView>
+ android:paddingTop="4dip"
+ android:paddingBottom="4dip"
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip"
+ android:textAppearance="?attr/actionMenuTextAppearance"
+ android:textColor="?attr/actionMenuTextColor"
+ style="?android:attr/actionButtonStyle" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 519ab87..a089021 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -88,7 +88,6 @@
<java-symbol type="id" name="hour" />
<java-symbol type="id" name="icon" />
<java-symbol type="id" name="image" />
- <java-symbol type="id" name="imageButton" />
<java-symbol type="id" name="increment" />
<java-symbol type="id" name="internalEmpty" />
<java-symbol type="id" name="info" />
@@ -173,7 +172,6 @@
<java-symbol type="id" name="switch_old" />
<java-symbol type="id" name="switchWidget" />
<java-symbol type="id" name="text" />
- <java-symbol type="id" name="textButton" />
<java-symbol type="id" name="time" />
<java-symbol type="id" name="time_current" />
<java-symbol type="id" name="timeDisplayBackground" />
diff --git a/core/tests/coretests/res/layout/textview_test.xml b/core/tests/coretests/res/layout/textview_test.xml
deleted file mode 100644
index f0c7b9e..0000000
--- a/core/tests/coretests/res/layout/textview_test.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/textviewtest_layout"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <TextView android:id="@+id/textviewtest_textview"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/textview_hebrew_text"/>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index d4dbced..af6df1a 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -16,25 +16,18 @@
package android.widget;
-import android.test.ActivityInstrumentationTestCase2;
+import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.GetChars;
-import android.view.View;
-
-import com.android.frameworks.coretests.R;
/**
* TextViewTest tests {@link TextView}.
*/
-public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewTestActivity> {
-
- public TextViewTest() {
- super(TextViewTestActivity.class);
- }
+public class TextViewTest extends AndroidTestCase {
@SmallTest
public void testArray() throws Exception {
- TextView tv = new TextView(getActivity());
+ TextView tv = new TextView(mContext);
char[] c = new char[] { 'H', 'e', 'l', 'l', 'o', ' ',
'W', 'o', 'r', 'l', 'd', '!' };
@@ -61,181 +54,4 @@
assertEquals('o', c2[4]);
assertEquals('\0', c2[5]);
}
-
- @SmallTest
- public void testTextDirectionDefault() {
- TextView tv = new TextView(getActivity());
- assertEquals(View.TEXT_DIRECTION_INHERIT, tv.getTextDirection());
- }
-
- @SmallTest
- public void testSetGetTextDirection() {
- TextView tv = new TextView(getActivity());
-
- tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
- assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
- assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
- assertEquals(View.TEXT_DIRECTION_INHERIT, tv.getTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_LTR);
- assertEquals(View.TEXT_DIRECTION_LTR, tv.getTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_RTL);
- assertEquals(View.TEXT_DIRECTION_RTL, tv.getTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_LOCALE);
- assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getTextDirection());
- }
-
- @SmallTest
- public void testGetResolvedTextDirectionLtr() {
- TextView tv = new TextView(getActivity());
- tv.setText("this is a test");
-
- assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
- assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
- assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
- assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_LTR);
- assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_RTL);
- assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_LOCALE);
- assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getResolvedTextDirection());
- }
-
- @SmallTest
- public void testGetResolvedTextDirectionLtrWithInheritance() {
- LinearLayout ll = new LinearLayout(getActivity());
- ll.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
-
- TextView tv = new TextView(getActivity());
- tv.setText("this is a test");
- ll.addView(tv);
-
- tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
- assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
- assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
- assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_LTR);
- assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_RTL);
- assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_LOCALE);
- assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getResolvedTextDirection());
- }
-
- @SmallTest
- public void testGetResolvedTextDirectionRtl() {
- TextView tv = new TextView(getActivity());
- tv.setText("\u05DD\u05DE"); // hebrew
-
- assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
- assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
- assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
- assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_LTR);
- assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_RTL);
- assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_LOCALE);
- assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getResolvedTextDirection());
- }
-
- @SmallTest
- public void testGetResolvedTextDirectionRtlWithInheritance() {
- LinearLayout ll = new LinearLayout(getActivity());
- ll.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
-
- TextView tv = new TextView(getActivity());
- tv.setText("\u05DD\u05DE"); // hebrew
- ll.addView(tv);
-
- tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
- assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
- assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
- assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_LTR);
- assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_RTL);
- assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_LOCALE);
- assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getResolvedTextDirection());
-
- // Force to RTL text direction on the layout
- ll.setTextDirection(View.TEXT_DIRECTION_RTL);
-
- tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
- assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
- assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
- assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_LTR);
- assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_RTL);
- assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
-
- tv.setTextDirection(View.TEXT_DIRECTION_LOCALE);
- assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getResolvedTextDirection());
- }
-
- @SmallTest
- public void testResetTextDirection() {
- final TextViewTestActivity activity = getActivity();
-
- final LinearLayout ll = (LinearLayout) activity.findViewById(R.id.textviewtest_layout);
- final TextView tv = (TextView) activity.findViewById(R.id.textviewtest_textview);
-
- getActivity().runOnUiThread(new Runnable() {
- public void run() {
- ll.setTextDirection(View.TEXT_DIRECTION_RTL);
- tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
- assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
-
- ll.removeView(tv);
- assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
- }
- });
- }
}
diff --git a/core/tests/coretests/src/android/widget/TextViewTestActivity.java b/core/tests/coretests/src/android/widget/TextViewTestActivity.java
deleted file mode 100644
index 1bb4d24..0000000
--- a/core/tests/coretests/src/android/widget/TextViewTestActivity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 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.widget;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-import com.android.frameworks.coretests.R;
-
-public class TextViewTestActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.textview_test);
- }
-}
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 9f2bd3a..95b9d86 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -135,8 +135,10 @@
* format: Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
* 16 bits per sample).
* channelMask: Channel mask: see audio_channels_t.
- * frameCount: Total size of track PCM buffer in frames. This defines the
- * latency of the track.
+ * frameCount: Minimum size of track PCM buffer in frames. This defines the
+ * latency of the track. The actual size selected by the AudioTrack could be
+ * larger if the requested size is not compatible with current audio HAL
+ * latency.
* flags: Reserved for future use.
* cbf: Callback function. If not null, this function is called periodically
* to request new PCM data.
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 4890f05..a1c99e5 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -784,12 +784,9 @@
mNotificationFramesAct = frameCount/2;
}
if (frameCount < minFrameCount) {
- if (enforceFrameCount) {
- ALOGE("Invalid buffer size: minFrameCount %d, frameCount %d", minFrameCount, frameCount);
- return BAD_VALUE;
- } else {
- frameCount = minFrameCount;
- }
+ ALOGW_IF(enforceFrameCount, "Minimum buffer size corrected from %d to %d",
+ frameCount, minFrameCount);
+ frameCount = minFrameCount;
}
} else {
// Ensure that buffer alignment matches channelCount
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index bd3e07a..1a85c9c 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -325,7 +325,7 @@
mStreamType, mLeftVolume, mRightVolume);
result.append(buffer);
snprintf(buffer, 255, " msec per frame(%f), latency (%d)\n",
- mMsecsPerFrame, mLatency);
+ mMsecsPerFrame, (mTrack != 0) ? mTrack->latency() : -1);
result.append(buffer);
snprintf(buffer, 255, " aux effect id(%d), send level (%f)\n",
mAuxEffectId, mSendLevel);
@@ -1384,7 +1384,6 @@
mRightVolume = 1.0;
mPlaybackRatePermille = 1000;
mSampleRateHz = 0;
- mLatency = 0;
mMsecsPerFrame = 0;
mAuxEffectId = 0;
mSendLevel = 0.0;
@@ -1443,7 +1442,8 @@
uint32_t MediaPlayerService::AudioOutput::latency () const
{
- return mLatency;
+ if (mTrack == 0) return 0;
+ return mTrack->latency();
}
float MediaPlayerService::AudioOutput::msecsPerFrame() const
@@ -1533,7 +1533,6 @@
mSampleRateHz = sampleRate;
mMsecsPerFrame = mPlaybackRatePermille / (float) sampleRate;
- mLatency = t->latency();
mTrack = t;
status_t res = t->setSampleRate(mPlaybackRatePermille * mSampleRateHz / 1000);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 681ecab..85cec22 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -118,7 +118,6 @@
int32_t mPlaybackRatePermille;
uint32_t mSampleRateHz; // sample rate of the content, as set in open()
float mMsecsPerFrame;
- uint32_t mLatency;
int mSessionId;
float mSendLevel;
int mAuxEffectId;
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 9427ef7..650b6c4 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -427,6 +427,12 @@
break;
}
+ if (mAudioSink != NULL) {
+ mLatencyUs = (int64_t)mAudioSink->latency() * 1000;
+ } else {
+ mLatencyUs = (int64_t)mAudioTrack->latency() * 1000;
+ }
+
CHECK(mInputBuffer->meta_data()->findInt64(
kKeyTime, &mPositionTimeMediaUs));
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index b972548..d8a5d99 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -310,10 +310,10 @@
}
result.append("Global session refs:\n");
- result.append(" session pid cnt\n");
+ result.append(" session pid count\n");
for (size_t i = 0; i < mAudioSessionRefs.size(); i++) {
AudioSessionRef *r = mAudioSessionRefs[i];
- snprintf(buffer, SIZE, " %7d %3d %3d\n", r->sessionid, r->pid, r->cnt);
+ snprintf(buffer, SIZE, " %7d %3d %3d\n", r->mSessionid, r->mPid, r->mCnt);
result.append(buffer);
}
write(fd, result.string(), result.size());
@@ -1036,9 +1036,9 @@
bool removed = false;
for (size_t i = 0; i< num; ) {
AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
- ALOGV(" pid %d @ %d", ref->pid, i);
- if (ref->pid == pid) {
- ALOGV(" removing entry for pid %d session %d", pid, ref->sessionid);
+ ALOGV(" pid %d @ %d", ref->mPid, i);
+ if (ref->mPid == pid) {
+ ALOGV(" removing entry for pid %d session %d", pid, ref->mSessionid);
mAudioSessionRefs.removeAt(i);
delete ref;
removed = true;
@@ -2067,9 +2067,7 @@
maxPeriod = seconds(mFrameCount) / mSampleRate * 15;
}
-if (mType == DUPLICATING) {
- updateWaitTime();
-}
+ updateWaitTime_l();
activeSleepTime = activeSleepTimeUs();
idleSleepTime = idleSleepTimeUs();
@@ -2267,79 +2265,79 @@
// shared by MIXER and DIRECT, overridden by DUPLICATING
void AudioFlinger::PlaybackThread::threadLoop_write()
{
- // FIXME rewrite to reduce number of system calls
- mLastWriteTime = systemTime();
- mInWrite = true;
- mBytesWritten += mixBufferSize;
- int bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
- if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
- mNumWrites++;
- mInWrite = false;
+ // FIXME rewrite to reduce number of system calls
+ mLastWriteTime = systemTime();
+ mInWrite = true;
+ mBytesWritten += mixBufferSize;
+ int bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
+ if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
+ mNumWrites++;
+ mInWrite = false;
}
// shared by MIXER and DIRECT, overridden by DUPLICATING
void AudioFlinger::PlaybackThread::threadLoop_standby()
{
- ALOGV("Audio hardware entering standby, mixer %p, suspend count %u", this, mSuspended);
- mOutput->stream->common.standby(&mOutput->stream->common);
+ ALOGV("Audio hardware entering standby, mixer %p, suspend count %u", this, mSuspended);
+ mOutput->stream->common.standby(&mOutput->stream->common);
}
void AudioFlinger::MixerThread::threadLoop_mix()
{
- // obtain the presentation timestamp of the next output buffer
- int64_t pts;
- status_t status = INVALID_OPERATION;
+ // obtain the presentation timestamp of the next output buffer
+ int64_t pts;
+ status_t status = INVALID_OPERATION;
- if (NULL != mOutput->stream->get_next_write_timestamp) {
- status = mOutput->stream->get_next_write_timestamp(
- mOutput->stream, &pts);
- }
+ if (NULL != mOutput->stream->get_next_write_timestamp) {
+ status = mOutput->stream->get_next_write_timestamp(
+ mOutput->stream, &pts);
+ }
- if (status != NO_ERROR) {
- pts = AudioBufferProvider::kInvalidPTS;
- }
+ if (status != NO_ERROR) {
+ pts = AudioBufferProvider::kInvalidPTS;
+ }
- // mix buffers...
- mAudioMixer->process(pts);
- // increase sleep time progressively when application underrun condition clears.
- // Only increase sleep time if the mixer is ready for two consecutive times to avoid
- // that a steady state of alternating ready/not ready conditions keeps the sleep time
- // such that we would underrun the audio HAL.
- if ((sleepTime == 0) && (sleepTimeShift > 0)) {
- sleepTimeShift--;
- }
- sleepTime = 0;
- standbyTime = systemTime() + mStandbyTimeInNsecs;
- //TODO: delay standby when effects have a tail
+ // mix buffers...
+ mAudioMixer->process(pts);
+ // increase sleep time progressively when application underrun condition clears.
+ // Only increase sleep time if the mixer is ready for two consecutive times to avoid
+ // that a steady state of alternating ready/not ready conditions keeps the sleep time
+ // such that we would underrun the audio HAL.
+ if ((sleepTime == 0) && (sleepTimeShift > 0)) {
+ sleepTimeShift--;
+ }
+ sleepTime = 0;
+ standbyTime = systemTime() + mStandbyTimeInNsecs;
+ //TODO: delay standby when effects have a tail
}
void AudioFlinger::MixerThread::threadLoop_sleepTime()
{
- // If no tracks are ready, sleep once for the duration of an output
- // buffer size, then write 0s to the output
- if (sleepTime == 0) {
- if (mixerStatus == MIXER_TRACKS_ENABLED) {
- sleepTime = activeSleepTime >> sleepTimeShift;
- if (sleepTime < kMinThreadSleepTimeUs) {
- sleepTime = kMinThreadSleepTimeUs;
- }
- // reduce sleep time in case of consecutive application underruns to avoid
- // starving the audio HAL. As activeSleepTimeUs() is larger than a buffer
- // duration we would end up writing less data than needed by the audio HAL if
- // the condition persists.
- if (sleepTimeShift < kMaxThreadSleepTimeShift) {
- sleepTimeShift++;
- }
- } else {
- sleepTime = idleSleepTime;
- }
- } else if (mBytesWritten != 0 ||
- (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) {
- memset (mMixBuffer, 0, mixBufferSize);
- sleepTime = 0;
- ALOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start");
+ // If no tracks are ready, sleep once for the duration of an output
+ // buffer size, then write 0s to the output
+ if (sleepTime == 0) {
+ if (mixerStatus == MIXER_TRACKS_ENABLED) {
+ sleepTime = activeSleepTime >> sleepTimeShift;
+ if (sleepTime < kMinThreadSleepTimeUs) {
+ sleepTime = kMinThreadSleepTimeUs;
}
- // TODO add standby time extension fct of effect tail
+ // reduce sleep time in case of consecutive application underruns to avoid
+ // starving the audio HAL. As activeSleepTimeUs() is larger than a buffer
+ // duration we would end up writing less data than needed by the audio HAL if
+ // the condition persists.
+ if (sleepTimeShift < kMaxThreadSleepTimeShift) {
+ sleepTimeShift++;
+ }
+ } else {
+ sleepTime = idleSleepTime;
+ }
+ } else if (mBytesWritten != 0 ||
+ (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) {
+ memset (mMixBuffer, 0, mixBufferSize);
+ sleepTime = 0;
+ ALOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start");
+ }
+ // TODO add standby time extension fct of effect tail
}
// prepareTracks_l() must be called with ThreadBase::mLock held
@@ -2858,173 +2856,173 @@
sp<Track>& trackToRemove
)
{
-// FIXME Temporarily renamed to avoid confusion with the member "mixerStatus"
-mixer_state mixerStatus_ = MIXER_IDLE;
+ // FIXME Temporarily renamed to avoid confusion with the member "mixerStatus"
+ mixer_state mixerStatus_ = MIXER_IDLE;
- // find out which tracks need to be processed
- if (mActiveTracks.size() != 0) {
- sp<Track> t = mActiveTracks[0].promote();
- // see FIXME in AudioFlinger.h, return MIXER_IDLE might also work
- if (t == 0) return MIXER_CONTINUE;
- //if (t == 0) continue;
+ // find out which tracks need to be processed
+ if (mActiveTracks.size() != 0) {
+ sp<Track> t = mActiveTracks[0].promote();
+ // see FIXME in AudioFlinger.h, return MIXER_IDLE might also work
+ if (t == 0) return MIXER_CONTINUE;
+ //if (t == 0) continue;
- Track* const track = t.get();
- audio_track_cblk_t* cblk = track->cblk();
+ Track* const track = t.get();
+ audio_track_cblk_t* cblk = track->cblk();
- // The first time a track is added we wait
- // for all its buffers to be filled before processing it
- if (cblk->framesReady() && track->isReady() &&
- !track->isPaused() && !track->isTerminated())
- {
- //ALOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
+ // The first time a track is added we wait
+ // for all its buffers to be filled before processing it
+ if (cblk->framesReady() && track->isReady() &&
+ !track->isPaused() && !track->isTerminated())
+ {
+ //ALOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
- if (track->mFillingUpStatus == Track::FS_FILLED) {
- track->mFillingUpStatus = Track::FS_ACTIVE;
- mLeftVolFloat = mRightVolFloat = 0;
- mLeftVolShort = mRightVolShort = 0;
- if (track->mState == TrackBase::RESUMING) {
- track->mState = TrackBase::ACTIVE;
- rampVolume = true;
- }
- } else if (cblk->server != 0) {
- // If the track is stopped before the first frame was mixed,
- // do not apply ramp
- rampVolume = true;
- }
- // compute volume for this track
- float left, right;
- if (track->isMuted() || mMasterMute || track->isPausing() ||
- mStreamTypes[track->streamType()].mute) {
- left = right = 0;
- if (track->isPausing()) {
- track->setPaused();
- }
- } else {
- float typeVolume = mStreamTypes[track->streamType()].volume;
- float v = mMasterVolume * typeVolume;
- uint32_t vlr = cblk->getVolumeLR();
- float v_clamped = v * (vlr & 0xFFFF);
- if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
- left = v_clamped/MAX_GAIN;
- v_clamped = v * (vlr >> 16);
- if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
- right = v_clamped/MAX_GAIN;
- }
+ if (track->mFillingUpStatus == Track::FS_FILLED) {
+ track->mFillingUpStatus = Track::FS_ACTIVE;
+ mLeftVolFloat = mRightVolFloat = 0;
+ mLeftVolShort = mRightVolShort = 0;
+ if (track->mState == TrackBase::RESUMING) {
+ track->mState = TrackBase::ACTIVE;
+ rampVolume = true;
+ }
+ } else if (cblk->server != 0) {
+ // If the track is stopped before the first frame was mixed,
+ // do not apply ramp
+ rampVolume = true;
+ }
+ // compute volume for this track
+ float left, right;
+ if (track->isMuted() || mMasterMute || track->isPausing() ||
+ mStreamTypes[track->streamType()].mute) {
+ left = right = 0;
+ if (track->isPausing()) {
+ track->setPaused();
+ }
+ } else {
+ float typeVolume = mStreamTypes[track->streamType()].volume;
+ float v = mMasterVolume * typeVolume;
+ uint32_t vlr = cblk->getVolumeLR();
+ float v_clamped = v * (vlr & 0xFFFF);
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ left = v_clamped/MAX_GAIN;
+ v_clamped = v * (vlr >> 16);
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ right = v_clamped/MAX_GAIN;
+ }
- if (left != mLeftVolFloat || right != mRightVolFloat) {
- mLeftVolFloat = left;
- mRightVolFloat = right;
+ if (left != mLeftVolFloat || right != mRightVolFloat) {
+ mLeftVolFloat = left;
+ mRightVolFloat = right;
- // If audio HAL implements volume control,
- // force software volume to nominal value
- if (mOutput->stream->set_volume(mOutput->stream, left, right) == NO_ERROR) {
- left = 1.0f;
- right = 1.0f;
- }
+ // If audio HAL implements volume control,
+ // force software volume to nominal value
+ if (mOutput->stream->set_volume(mOutput->stream, left, right) == NO_ERROR) {
+ left = 1.0f;
+ right = 1.0f;
+ }
- // Convert volumes from float to 8.24
- uint32_t vl = (uint32_t)(left * (1 << 24));
- uint32_t vr = (uint32_t)(right * (1 << 24));
+ // Convert volumes from float to 8.24
+ uint32_t vl = (uint32_t)(left * (1 << 24));
+ uint32_t vr = (uint32_t)(right * (1 << 24));
- // Delegate volume control to effect in track effect chain if needed
- // only one effect chain can be present on DirectOutputThread, so if
- // there is one, the track is connected to it
- if (!mEffectChains.isEmpty()) {
- // Do not ramp volume if volume is controlled by effect
- if (mEffectChains[0]->setVolume_l(&vl, &vr)) {
- rampVolume = false;
- }
- }
-
- // Convert volumes from 8.24 to 4.12 format
- uint32_t v_clamped = (vl + (1 << 11)) >> 12;
- if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
- leftVol = (uint16_t)v_clamped;
- v_clamped = (vr + (1 << 11)) >> 12;
- if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
- rightVol = (uint16_t)v_clamped;
- } else {
- leftVol = mLeftVolShort;
- rightVol = mRightVolShort;
+ // Delegate volume control to effect in track effect chain if needed
+ // only one effect chain can be present on DirectOutputThread, so if
+ // there is one, the track is connected to it
+ if (!mEffectChains.isEmpty()) {
+ // Do not ramp volume if volume is controlled by effect
+ if (mEffectChains[0]->setVolume_l(&vl, &vr)) {
rampVolume = false;
}
+ }
- // reset retry count
- track->mRetryCount = kMaxTrackRetriesDirect;
- activeTrack = t;
- mixerStatus_ = MIXER_TRACKS_READY;
+ // Convert volumes from 8.24 to 4.12 format
+ uint32_t v_clamped = (vl + (1 << 11)) >> 12;
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+ leftVol = (uint16_t)v_clamped;
+ v_clamped = (vr + (1 << 11)) >> 12;
+ if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+ rightVol = (uint16_t)v_clamped;
+ } else {
+ leftVol = mLeftVolShort;
+ rightVol = mRightVolShort;
+ rampVolume = false;
+ }
+
+ // reset retry count
+ track->mRetryCount = kMaxTrackRetriesDirect;
+ activeTrack = t;
+ mixerStatus_ = MIXER_TRACKS_READY;
+ } else {
+ //ALOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
+ if (track->isStopped()) {
+ track->reset();
+ }
+ if (track->isTerminated() || track->isStopped() || track->isPaused()) {
+ // We have consumed all the buffers of this track.
+ // Remove it from the list of active tracks.
+ trackToRemove = track;
+ } else {
+ // No buffers for this track. Give it a few chances to
+ // fill a buffer, then remove it from active list.
+ if (--(track->mRetryCount) <= 0) {
+ ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
+ trackToRemove = track;
} else {
- //ALOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
- if (track->isStopped()) {
- track->reset();
- }
- if (track->isTerminated() || track->isStopped() || track->isPaused()) {
- // We have consumed all the buffers of this track.
- // Remove it from the list of active tracks.
- trackToRemove = track;
- } else {
- // No buffers for this track. Give it a few chances to
- // fill a buffer, then remove it from active list.
- if (--(track->mRetryCount) <= 0) {
- ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
- trackToRemove = track;
- } else {
- mixerStatus_ = MIXER_TRACKS_ENABLED;
- }
- }
+ mixerStatus_ = MIXER_TRACKS_ENABLED;
}
}
+ }
+ }
- // remove all the tracks that need to be...
- if (CC_UNLIKELY(trackToRemove != 0)) {
- mActiveTracks.remove(trackToRemove);
- if (!mEffectChains.isEmpty()) {
- ALOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(),
- trackToRemove->sessionId());
- mEffectChains[0]->decActiveTrackCnt();
- }
- if (trackToRemove->isTerminated()) {
- removeTrack_l(trackToRemove);
- }
- }
+ // remove all the tracks that need to be...
+ if (CC_UNLIKELY(trackToRemove != 0)) {
+ mActiveTracks.remove(trackToRemove);
+ if (!mEffectChains.isEmpty()) {
+ ALOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(),
+ trackToRemove->sessionId());
+ mEffectChains[0]->decActiveTrackCnt();
+ }
+ if (trackToRemove->isTerminated()) {
+ removeTrack_l(trackToRemove);
+ }
+ }
-return mixerStatus_;
+ return mixerStatus_;
}
void AudioFlinger::DirectOutputThread::threadLoop_mix()
{
- AudioBufferProvider::Buffer buffer;
- size_t frameCount = mFrameCount;
- int8_t *curBuf = (int8_t *)mMixBuffer;
- // output audio to hardware
- while (frameCount) {
- buffer.frameCount = frameCount;
- activeTrack->getNextBuffer(&buffer);
- if (CC_UNLIKELY(buffer.raw == NULL)) {
- memset(curBuf, 0, frameCount * mFrameSize);
- break;
- }
- memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);
- frameCount -= buffer.frameCount;
- curBuf += buffer.frameCount * mFrameSize;
- activeTrack->releaseBuffer(&buffer);
- }
- sleepTime = 0;
- standbyTime = systemTime() + standbyDelay;
+ AudioBufferProvider::Buffer buffer;
+ size_t frameCount = mFrameCount;
+ int8_t *curBuf = (int8_t *)mMixBuffer;
+ // output audio to hardware
+ while (frameCount) {
+ buffer.frameCount = frameCount;
+ activeTrack->getNextBuffer(&buffer);
+ if (CC_UNLIKELY(buffer.raw == NULL)) {
+ memset(curBuf, 0, frameCount * mFrameSize);
+ break;
+ }
+ memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);
+ frameCount -= buffer.frameCount;
+ curBuf += buffer.frameCount * mFrameSize;
+ activeTrack->releaseBuffer(&buffer);
+ }
+ sleepTime = 0;
+ standbyTime = systemTime() + standbyDelay;
}
void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
{
- if (sleepTime == 0) {
- if (mixerStatus == MIXER_TRACKS_ENABLED) {
- sleepTime = activeSleepTime;
- } else {
- sleepTime = idleSleepTime;
- }
- } else if (mBytesWritten != 0 && audio_is_linear_pcm(mFormat)) {
- memset (mMixBuffer, 0, mFrameCount * mFrameSize);
- sleepTime = 0;
- }
+ if (sleepTime == 0) {
+ if (mixerStatus == MIXER_TRACKS_ENABLED) {
+ sleepTime = activeSleepTime;
+ } else {
+ sleepTime = idleSleepTime;
+ }
+ } else if (mBytesWritten != 0 && audio_is_linear_pcm(mFormat)) {
+ memset (mMixBuffer, 0, mFrameCount * mFrameSize);
+ sleepTime = 0;
+ }
}
// getTrackName_l() must be called with ThreadBase::mLock held
@@ -3139,52 +3137,52 @@
void AudioFlinger::DuplicatingThread::threadLoop_mix()
{
- // mix buffers...
- if (outputsReady(outputTracks)) {
- mAudioMixer->process(AudioBufferProvider::kInvalidPTS);
- } else {
- memset(mMixBuffer, 0, mixBufferSize);
- }
- sleepTime = 0;
- writeFrames = mFrameCount;
+ // mix buffers...
+ if (outputsReady(outputTracks)) {
+ mAudioMixer->process(AudioBufferProvider::kInvalidPTS);
+ } else {
+ memset(mMixBuffer, 0, mixBufferSize);
+ }
+ sleepTime = 0;
+ writeFrames = mFrameCount;
}
void AudioFlinger::DuplicatingThread::threadLoop_sleepTime()
{
- if (sleepTime == 0) {
- if (mixerStatus == MIXER_TRACKS_ENABLED) {
- sleepTime = activeSleepTime;
- } else {
- sleepTime = idleSleepTime;
- }
- } else if (mBytesWritten != 0) {
- // flush remaining overflow buffers in output tracks
- for (size_t i = 0; i < outputTracks.size(); i++) {
- if (outputTracks[i]->isActive()) {
- sleepTime = 0;
- writeFrames = 0;
- memset(mMixBuffer, 0, mixBufferSize);
- break;
- }
- }
+ if (sleepTime == 0) {
+ if (mixerStatus == MIXER_TRACKS_ENABLED) {
+ sleepTime = activeSleepTime;
+ } else {
+ sleepTime = idleSleepTime;
+ }
+ } else if (mBytesWritten != 0) {
+ // flush remaining overflow buffers in output tracks
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ if (outputTracks[i]->isActive()) {
+ sleepTime = 0;
+ writeFrames = 0;
+ memset(mMixBuffer, 0, mixBufferSize);
+ break;
}
+ }
+ }
}
void AudioFlinger::DuplicatingThread::threadLoop_write()
{
- standbyTime = systemTime() + mStandbyTimeInNsecs;
- for (size_t i = 0; i < outputTracks.size(); i++) {
- outputTracks[i]->write(mMixBuffer, writeFrames);
- }
- mBytesWritten += mixBufferSize;
+ standbyTime = systemTime() + mStandbyTimeInNsecs;
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ outputTracks[i]->write(mMixBuffer, writeFrames);
+ }
+ mBytesWritten += mixBufferSize;
}
void AudioFlinger::DuplicatingThread::threadLoop_standby()
{
- // DuplicatingThread implements standby by stopping all tracks
- for (size_t i = 0; i < outputTracks.size(); i++) {
- outputTracks[i]->stop();
- }
+ // DuplicatingThread implements standby by stopping all tracks
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ outputTracks[i]->stop();
+ }
}
void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)
@@ -3202,7 +3200,7 @@
thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f);
mOutputTracks.add(outputTrack);
ALOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread);
- updateWaitTime();
+ updateWaitTime_l();
}
}
@@ -3213,14 +3211,15 @@
if (mOutputTracks[i]->thread() == thread) {
mOutputTracks[i]->destroy();
mOutputTracks.removeAt(i);
- updateWaitTime();
+ updateWaitTime_l();
return;
}
}
ALOGV("removeOutputTrack(): unkonwn thread: %p", thread);
}
-void AudioFlinger::DuplicatingThread::updateWaitTime()
+// caller must hold mLock
+void AudioFlinger::DuplicatingThread::updateWaitTime_l()
{
mWaitTimeMs = UINT_MAX;
for (size_t i = 0; i < mOutputTracks.size(); i++) {
@@ -5700,9 +5699,9 @@
size_t num = mAudioSessionRefs.size();
for (size_t i = 0; i< num; i++) {
AudioSessionRef *ref = mAudioSessionRefs.editItemAt(i);
- if (ref->sessionid == audioSession && ref->pid == caller) {
- ref->cnt++;
- ALOGV(" incremented refcount to %d", ref->cnt);
+ if (ref->mSessionid == audioSession && ref->mPid == caller) {
+ ref->mCnt++;
+ ALOGV(" incremented refcount to %d", ref->mCnt);
return;
}
}
@@ -5718,10 +5717,10 @@
size_t num = mAudioSessionRefs.size();
for (size_t i = 0; i< num; i++) {
AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
- if (ref->sessionid == audioSession && ref->pid == caller) {
- ref->cnt--;
- ALOGV(" decremented refcount to %d", ref->cnt);
- if (ref->cnt == 0) {
+ if (ref->mSessionid == audioSession && ref->mPid == caller) {
+ ref->mCnt--;
+ ALOGV(" decremented refcount to %d", ref->mCnt);
+ if (ref->mCnt == 0) {
mAudioSessionRefs.removeAt(i);
delete ref;
purgeStaleEffects_l();
@@ -5766,9 +5765,9 @@
bool found = false;
for (size_t k = 0; k < numsessionrefs; k++) {
AudioSessionRef *ref = mAudioSessionRefs.itemAt(k);
- if (ref->sessionid == sessionid) {
+ if (ref->mSessionid == sessionid) {
ALOGV(" session %d still exists for %d with %d refs",
- sessionid, ref->pid, ref->cnt);
+ sessionid, ref->mPid, ref->mCnt);
found = true;
break;
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c7ac0a8..336c777 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -812,7 +812,7 @@
virtual void threadLoop_standby();
// Non-trivial for DUPLICATING only
- virtual void updateWaitTime() { }
+ virtual void updateWaitTime_l() { }
// Non-trivial for DIRECT only
virtual void applyVolume() { }
@@ -1046,7 +1046,9 @@
virtual void threadLoop_sleepTime();
virtual void threadLoop_write();
virtual void threadLoop_standby();
- virtual void updateWaitTime();
+
+ // called from threadLoop, addOutputTrack, removeOutputTrack
+ virtual void updateWaitTime_l();
private:
uint32_t mWaitTimeMs;
@@ -1570,12 +1572,11 @@
// for mAudioSessionRefs only
struct AudioSessionRef {
- // FIXME rename parameter names when fields get "m" prefix
- AudioSessionRef(int sessionid_, pid_t pid_) :
- sessionid(sessionid_), pid(pid_), cnt(1) {}
- const int sessionid;
- const pid_t pid;
- int cnt;
+ AudioSessionRef(int sessionid, pid_t pid) :
+ mSessionid(sessionid), mPid(pid), mCnt(1) {}
+ const int mSessionid;
+ const pid_t mPid;
+ int mCnt;
};
friend class RecordThread;
diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java
index d37c9ab..932cba1 100644
--- a/services/java/com/android/server/DropBoxManagerService.java
+++ b/services/java/com/android/server/DropBoxManagerService.java
@@ -28,6 +28,7 @@
import android.os.DropBoxManager;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.Message;
import android.os.StatFs;
import android.os.SystemClock;
import android.provider.Settings;
@@ -64,6 +65,9 @@
private static final int DEFAULT_RESERVE_PERCENT = 10;
private static final int QUOTA_RESCAN_MILLIS = 5000;
+ // mHandler 'what' value.
+ private static final int MSG_SEND_BROADCAST = 1;
+
private static final boolean PROFILE_DUMP = false;
// TODO: This implementation currently uses one file per entry, which is
@@ -88,11 +92,11 @@
private int mCachedQuotaBlocks = 0; // Space we can use: computed from free space, etc.
private long mCachedQuotaUptimeMillis = 0;
- // Ensure that all log entries have a unique timestamp
- private long mLastTimestamp = 0;
-
private volatile boolean mBooted = false;
+ // Provide a way to perform sendBroadcast asynchronously to avoid deadlocks.
+ private final Handler mHandler;
+
/** Receives events that might indicate a need to clean up files. */
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -143,11 +147,21 @@
mContentResolver.registerContentObserver(
Settings.Secure.CONTENT_URI, true,
new ContentObserver(new Handler()) {
+ @Override
public void onChange(boolean selfChange) {
mReceiver.onReceive(context, (Intent) null);
}
});
+ mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_SEND_BROADCAST) {
+ mContext.sendBroadcast((Intent)msg.obj, android.Manifest.permission.READ_LOGS);
+ }
+ }
+ };
+
// The real work gets done lazily in init() -- that way service creation always
// succeeds, and things like disk problems cause individual method failures.
}
@@ -157,6 +171,7 @@
mContext.unregisterReceiver(mReceiver);
}
+ @Override
public void add(DropBoxManager.Entry entry) {
File temp = null;
OutputStream output = null;
@@ -227,14 +242,17 @@
long time = createEntry(temp, tag, flags);
temp = null;
- Intent dropboxIntent = new Intent(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED);
+ final Intent dropboxIntent = new Intent(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED);
dropboxIntent.putExtra(DropBoxManager.EXTRA_TAG, tag);
dropboxIntent.putExtra(DropBoxManager.EXTRA_TIME, time);
if (!mBooted) {
dropboxIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
- mContext.sendBroadcast(dropboxIntent, android.Manifest.permission.READ_LOGS);
-
+ // Call sendBroadcast after returning from this call to avoid deadlock. In particular
+ // the caller may be holding the WindowManagerService lock but sendBroadcast requires a
+ // lock in ActivityManagerService. ActivityManagerService has been caught holding that
+ // very lock while waiting for the WindowManagerService lock.
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SEND_BROADCAST, dropboxIntent));
} catch (IOException e) {
Slog.e(TAG, "Can't write: " + tag, e);
} finally {
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 308661f..6593a0f 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -34,8 +34,8 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.LinkedList;
/**
* Generic connector class for interfacing with a native daemon which uses the
@@ -50,11 +50,15 @@
private OutputStream mOutputStream;
private LocalLog mLocalLog;
- private final BlockingQueue<NativeDaemonEvent> mResponseQueue;
+ private final ResponseQueue mResponseQueue;
private INativeDaemonConnectorCallbacks mCallbacks;
private Handler mCallbackHandler;
+ private AtomicInteger mSequenceNumber;
+
+ private static final int DEFAULT_TIMEOUT = 1 * 60 * 1000; /* 1 minute */
+
/** Lock held whenever communicating with native daemon. */
private final Object mDaemonLock = new Object();
@@ -64,7 +68,8 @@
int responseQueueSize, String logTag, int maxLogSize) {
mCallbacks = callbacks;
mSocket = socket;
- mResponseQueue = new LinkedBlockingQueue<NativeDaemonEvent>(responseQueueSize);
+ mResponseQueue = new ResponseQueue(responseQueueSize);
+ mSequenceNumber = new AtomicInteger(0);
TAG = logTag != null ? logTag : "NativeDaemonConnector";
mLocalLog = new LocalLog(maxLogSize);
}
@@ -79,7 +84,7 @@
try {
listenToSocket();
} catch (Exception e) {
- Slog.e(TAG, "Error in NativeDaemonConnector", e);
+ loge("Error in NativeDaemonConnector: " + e);
SystemClock.sleep(5000);
}
}
@@ -90,12 +95,10 @@
String event = (String) msg.obj;
try {
if (!mCallbacks.onEvent(msg.what, event, event.split(" "))) {
- Slog.w(TAG, String.format(
- "Unhandled event '%s'", event));
+ log(String.format("Unhandled event '%s'", event));
}
} catch (Exception e) {
- Slog.e(TAG, String.format(
- "Error handling '%s'", event), e);
+ loge("Error handling '" + event + "': " + e);
}
return true;
}
@@ -111,7 +114,9 @@
socket.connect(address);
InputStream inputStream = socket.getInputStream();
- mOutputStream = socket.getOutputStream();
+ synchronized (mDaemonLock) {
+ mOutputStream = socket.getOutputStream();
+ }
mCallbacks.onDaemonConnected();
@@ -120,7 +125,10 @@
while (true) {
int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
- if (count < 0) break;
+ if (count < 0) {
+ loge("got " + count + " reading with start = " + start);
+ break;
+ }
// Add our starting point to the count and reset the start.
count += start;
@@ -140,14 +148,10 @@
mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
event.getCode(), event.getRawEvent()));
} else {
- try {
- mResponseQueue.put(event);
- } catch (InterruptedException ex) {
- Slog.e(TAG, "Failed to put response onto queue: " + ex);
- }
+ mResponseQueue.add(event.getCmdNumber(), event);
}
} catch (IllegalArgumentException e) {
- Slog.w(TAG, "Problem parsing message: " + rawEvent, e);
+ log("Problem parsing message: " + rawEvent + " - " + e);
}
start = i + 1;
@@ -169,15 +173,16 @@
}
}
} catch (IOException ex) {
- Slog.e(TAG, "Communications error", ex);
+ loge("Communications error: " + ex);
throw ex;
} finally {
synchronized (mDaemonLock) {
if (mOutputStream != null) {
try {
+ loge("closing stream for " + mSocket);
mOutputStream.close();
} catch (IOException e) {
- Slog.w(TAG, "Failed closing output stream", e);
+ loge("Failed closing output stream: " + e);
}
mOutputStream = null;
}
@@ -188,17 +193,17 @@
socket.close();
}
} catch (IOException ex) {
- Slog.w(TAG, "Failed closing socket", ex);
+ loge("Failed closing socket: " + ex);
}
}
}
/**
- * Send command to daemon, escaping arguments as needed.
+ * Make command for daemon, escaping arguments as needed.
*
- * @return the final command issued.
+ * @return the final command.
*/
- private String sendCommandLocked(String cmd, Object... args)
+ private StringBuilder makeCommand(String cmd, Object... args)
throws NativeDaemonConnectorException {
// TODO: eventually enforce that cmd doesn't contain arguments
if (cmd.indexOf('\0') >= 0) {
@@ -216,22 +221,33 @@
appendEscaped(builder, argString);
}
- final String unterminated = builder.toString();
- log("SND -> {" + unterminated + "}");
+ return builder;
+ }
+
+ private int sendCommand(StringBuilder builder)
+ throws NativeDaemonConnectorException {
+
+ int sequenceNumber = mSequenceNumber.incrementAndGet();
+
+ builder.insert(0, Integer.toString(sequenceNumber) + " ");
+
+ log("SND -> {" + builder.toString() + "}");
builder.append('\0');
- if (mOutputStream == null) {
- throw new NativeDaemonConnectorException("missing output stream");
- } else {
- try {
- mOutputStream.write(builder.toString().getBytes(Charsets.UTF_8));
- } catch (IOException e) {
- throw new NativeDaemonConnectorException("problem sending command", e);
+ synchronized (mDaemonLock) {
+ if (mOutputStream == null) {
+ throw new NativeDaemonConnectorException("missing output stream");
+ } else {
+ try {
+ mOutputStream.write(builder.toString().getBytes(Charsets.UTF_8));
+ } catch (IOException e) {
+ throw new NativeDaemonConnectorException("problem sending command", e);
+ }
}
}
- return unterminated;
+ return sequenceNumber;
}
/**
@@ -292,39 +308,42 @@
*/
public NativeDaemonEvent[] executeForList(String cmd, Object... args)
throws NativeDaemonConnectorException {
- synchronized (mDaemonLock) {
- return executeLocked(cmd, args);
- }
+ return execute(DEFAULT_TIMEOUT, cmd, args);
}
- private NativeDaemonEvent[] executeLocked(String cmd, Object... args)
+ /**
+ * Issue the given command to the native daemon and return any
+ * {@linke NativeDaemonEvent@isClassContinue()} responses, including the
+ * final terminal response. Note that the timeout does not count time in
+ * deep sleep.
+ *
+ * @throws NativeDaemonConnectorException when problem communicating with
+ * native daemon, or if the response matches
+ * {@link NativeDaemonEvent#isClassClientError()} or
+ * {@link NativeDaemonEvent#isClassServerError()}.
+ */
+ public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)
throws NativeDaemonConnectorException {
final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
-
- while (mResponseQueue.size() > 0) {
- try {
- log("ignoring {" + mResponseQueue.take() + "}");
- } catch (Exception e) {}
- }
-
- final String sentCommand = sendCommandLocked(cmd, args);
+ final StringBuilder sentCommand = makeCommand(cmd, args);
+ final int cmdNumber = sendCommand(sentCommand);
NativeDaemonEvent event = null;
+ cmd = sentCommand.toString();
do {
- try {
- event = mResponseQueue.take();
- } catch (InterruptedException e) {
- Slog.w(TAG, "interrupted waiting for event line");
- continue;
+ event = mResponseQueue.remove(cmdNumber, timeout, cmd);
+ if (event == null) {
+ loge("timed-out waiting for response to " + cmdNumber + " " + cmd);
+ throw new NativeDaemonFailureException(cmd, event);
}
events.add(event);
} while (event.isClassContinue());
if (event.isClassClientError()) {
- throw new NativeDaemonArgumentException(sentCommand, event);
+ throw new NativeDaemonArgumentException(cmd, event);
}
if (event.isClassServerError()) {
- throw new NativeDaemonFailureException(sentCommand, event);
+ throw new NativeDaemonFailureException(cmd, event);
}
return events.toArray(new NativeDaemonEvent[events.size()]);
@@ -448,10 +467,120 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mLocalLog.dump(fd, pw, args);
+ pw.println();
+ mResponseQueue.dump(fd, pw, args);
}
private void log(String logstring) {
if (LOGD) Slog.d(TAG, logstring);
mLocalLog.log(logstring);
}
+
+ private void loge(String logstring) {
+ Slog.e(TAG, logstring);
+ mLocalLog.log(logstring);
+ }
+
+ private static class ResponseQueue {
+
+ private static class Response {
+ public int cmdNum;
+ public LinkedList<NativeDaemonEvent> responses = new LinkedList<NativeDaemonEvent>();
+ public String request;
+ public Response(int c, String r) {cmdNum = c; request = r;}
+ }
+
+ private final LinkedList<Response> mResponses;
+ private int mMaxCount;
+
+ ResponseQueue(int maxCount) {
+ mResponses = new LinkedList<Response>();
+ mMaxCount = maxCount;
+ }
+
+ public void add(int cmdNum, NativeDaemonEvent response) {
+ Response found = null;
+ synchronized (mResponses) {
+ for (Response r : mResponses) {
+ if (r.cmdNum == cmdNum) {
+ found = r;
+ break;
+ }
+ }
+ if (found == null) {
+ // didn't find it - make sure our queue isn't too big before adding
+ // another..
+ while (mResponses.size() >= mMaxCount) {
+ Slog.e("NativeDaemonConnector.ResponseQueue",
+ "more buffered than allowed: " + mResponses.size() +
+ " >= " + mMaxCount);
+ // let any waiter timeout waiting for this
+ Response r = mResponses.remove();
+ Slog.e("NativeDaemonConnector.ResponseQueue",
+ "Removing request: " + r.request + " (" + r.cmdNum + ")");
+ }
+ found = new Response(cmdNum, null);
+ mResponses.add(found);
+ }
+ found.responses.add(response);
+ }
+ synchronized (found) {
+ found.notify();
+ }
+ }
+
+ // note that the timeout does not count time in deep sleep. If you don't want
+ // the device to sleep, hold a wakelock
+ public NativeDaemonEvent remove(int cmdNum, int timeoutMs, String origCmd) {
+ long endTime = SystemClock.uptimeMillis() + timeoutMs;
+ long nowTime;
+ Response found = null;
+ while (true) {
+ synchronized (mResponses) {
+ for (Response response : mResponses) {
+ if (response.cmdNum == cmdNum) {
+ found = response;
+ // how many response fragments are left
+ switch (response.responses.size()) {
+ case 0: // haven't got any - must wait
+ break;
+ case 1: // last one - remove this from the master list
+ mResponses.remove(response); // fall through
+ default: // take one and move on
+ response.request = origCmd;
+ return response.responses.remove();
+ }
+ }
+ }
+ nowTime = SystemClock.uptimeMillis();
+ if (endTime <= nowTime) {
+ Slog.e("NativeDaemonConnector.ResponseQueue",
+ "Timeout waiting for response");
+ return null;
+ }
+ /* pre-allocate so we have something unique to wait on */
+ if (found == null) {
+ found = new Response(cmdNum, origCmd);
+ mResponses.add(found);
+ }
+ }
+ try {
+ synchronized (found) {
+ found.wait(endTime - nowTime);
+ }
+ } catch (InterruptedException e) {
+ // loop around to check if we're done or if it's time to stop waiting
+ }
+ }
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("Pending requests:");
+ synchronized (mResponses) {
+ for (Response response : mResponses) {
+ pw.println(" Cmd " + response.cmdNum + " - " + response.request);
+ }
+ }
+ }
+ }
}
diff --git a/services/java/com/android/server/NativeDaemonEvent.java b/services/java/com/android/server/NativeDaemonEvent.java
index 62084c0..d5e9f66 100644
--- a/services/java/com/android/server/NativeDaemonEvent.java
+++ b/services/java/com/android/server/NativeDaemonEvent.java
@@ -28,16 +28,22 @@
// TODO: keep class ranges in sync with ResponseCode.h
// TODO: swap client and server error ranges to roughly mirror HTTP spec
+ private final int mCmdNumber;
private final int mCode;
private final String mMessage;
private final String mRawEvent;
- private NativeDaemonEvent(int code, String message, String rawEvent) {
+ private NativeDaemonEvent(int cmdNumber, int code, String message, String rawEvent) {
+ mCmdNumber = cmdNumber;
mCode = code;
mMessage = message;
mRawEvent = rawEvent;
}
+ public int getCmdNumber() {
+ return mCmdNumber;
+ }
+
public int getCode() {
return mCode;
}
@@ -89,7 +95,11 @@
* Test if event represents an unsolicited event from native daemon.
*/
public boolean isClassUnsolicited() {
- return mCode >= 600 && mCode < 700;
+ return isClassUnsolicited(mCode);
+ }
+
+ private static boolean isClassUnsolicited(int code) {
+ return code >= 600 && code < 700;
}
/**
@@ -110,20 +120,37 @@
* from native side.
*/
public static NativeDaemonEvent parseRawEvent(String rawEvent) {
- final int splitIndex = rawEvent.indexOf(' ');
- if (splitIndex == -1) {
- throw new IllegalArgumentException("unable to find ' ' separator");
+ final String[] parsed = rawEvent.split(" ");
+ if (parsed.length < 2) {
+ throw new IllegalArgumentException("Insufficient arguments");
}
+ int skiplength = 0;
+
final int code;
try {
- code = Integer.parseInt(rawEvent.substring(0, splitIndex));
+ code = Integer.parseInt(parsed[0]);
+ skiplength = parsed[0].length() + 1;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("problem parsing code", e);
}
- final String message = rawEvent.substring(splitIndex + 1);
- return new NativeDaemonEvent(code, message, rawEvent);
+ int cmdNumber = -1;
+ if (isClassUnsolicited(code) == false) {
+ if (parsed.length < 3) {
+ throw new IllegalArgumentException("Insufficient arguemnts");
+ }
+ try {
+ cmdNumber = Integer.parseInt(parsed[1]);
+ skiplength += parsed[1].length() + 1;
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("problem parsing cmdNumber", e);
+ }
+ }
+
+ final String message = rawEvent.substring(skiplength);
+
+ return new NativeDaemonEvent(cmdNumber, code, message, rawEvent);
}
/**
diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java
index 530a8dc..fe80fdf 100644
--- a/telephony/java/com/android/internal/telephony/IccCard.java
+++ b/telephony/java/com/android/internal/telephony/IccCard.java
@@ -35,8 +35,15 @@
import com.android.internal.telephony.PhoneBase;
import com.android.internal.telephony.CommandsInterface.RadioState;
+import com.android.internal.telephony.gsm.SIMFileHandler;
import com.android.internal.telephony.gsm.SIMRecords;
+import com.android.internal.telephony.cdma.CDMALTEPhone;
+import com.android.internal.telephony.cdma.CdmaLteUiccFileHandler;
+import com.android.internal.telephony.cdma.CdmaLteUiccRecords;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
+import com.android.internal.telephony.cdma.RuimFileHandler;
+import com.android.internal.telephony.cdma.RuimRecords;
+
import android.os.SystemProperties;
import com.android.internal.R;
@@ -56,6 +63,8 @@
protected boolean isSubscriptionFromIccCard = true;
protected CdmaSubscriptionSourceManager mCdmaSSM = null;
protected PhoneBase mPhone;
+ private IccRecords mIccRecords;
+ private IccFileHandler mIccFileHandler;
private RegistrantList mAbsentRegistrants = new RegistrantList();
private RegistrantList mPinLockedRegistrants = new RegistrantList();
private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
@@ -167,26 +176,46 @@
}
public IccCard(PhoneBase phone, String logTag, Boolean is3gpp, Boolean dbg) {
+ mLogTag = logTag;
+ mDbg = dbg;
+ if (mDbg) log("[IccCard] Creating card type " + (is3gpp ? "3gpp" : "3gpp2"));
mPhone = phone;
this.is3gpp = is3gpp;
mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(mPhone.getContext(),
mPhone.mCM, mHandler, EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
+ if (phone.mCM.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE
+ && phone instanceof CDMALTEPhone) {
+ mIccRecords = new CdmaLteUiccRecords(phone);
+ mIccFileHandler = new CdmaLteUiccFileHandler((CDMALTEPhone)phone);
+ } else {
+ mIccRecords = is3gpp ? new SIMRecords(phone) : new RuimRecords(phone);
+ mIccFileHandler = is3gpp ? new SIMFileHandler(phone) : new RuimFileHandler(phone);
+ }
mPhone.mCM.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
mPhone.mCM.registerForOn(mHandler, EVENT_RADIO_ON, null);
mPhone.mCM.registerForIccStatusChanged(mHandler, EVENT_ICC_STATUS_CHANGED, null);
- mLogTag = logTag;
- mDbg = dbg;
}
public void dispose() {
+ if (mDbg) log("[IccCard] Disposing card type " + (is3gpp ? "3gpp" : "3gpp2"));
mPhone.mCM.unregisterForIccStatusChanged(mHandler);
mPhone.mCM.unregisterForOffOrNotAvailable(mHandler);
mPhone.mCM.unregisterForOn(mHandler);
mCdmaSSM.dispose(mHandler);
+ mIccRecords.dispose();
+ mIccFileHandler.dispose();
}
protected void finalize() {
- if(mDbg) Log.d(mLogTag, "IccCard finalized");
+ if (mDbg) log("[IccCard] Finalized card type " + (is3gpp ? "3gpp" : "3gpp2"));
+ }
+
+ public IccRecords getIccRecords() {
+ return mIccRecords;
+ }
+
+ public IccFileHandler getIccFileHandler() {
+ return mIccFileHandler;
}
/**
@@ -541,6 +570,10 @@
} else if (isIccCardAdded) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
}
+
+ if (oldState != State.READY && newState == State.READY) {
+ mIccRecords.onReady();
+ }
}
private void onIccSwap(boolean isAdded) {
@@ -932,6 +965,10 @@
public String getAid() {
String aid = "";
+ if (mIccCardStatus == null) {
+ return aid;
+ }
+
int appIndex = getCurrentApplicationIndex();
if (appIndex >= 0 && appIndex < IccCardStatus.CARD_MAX_APPS) {
diff --git a/telephony/java/com/android/internal/telephony/IccRecords.java b/telephony/java/com/android/internal/telephony/IccRecords.java
index fc011c0..6e82903 100644
--- a/telephony/java/com/android/internal/telephony/IccRecords.java
+++ b/telephony/java/com/android/internal/telephony/IccRecords.java
@@ -102,6 +102,7 @@
public abstract void dispose();
protected abstract void onRadioOffOrNotAvailable();
+ public abstract void onReady();
//***** Public Methods
public AdnRecordCache getAdnCache() {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
index 3084c14..14a4b46 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
@@ -36,6 +36,7 @@
import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.gsm.GsmSMSDispatcher;
import com.android.internal.telephony.ims.IsimRecords;
+import com.android.internal.telephony.uicc.UiccController;
public class CDMALTEPhone extends CDMAPhone {
static final String LOG_TAG = "CDMA";
@@ -79,9 +80,9 @@
@Override
protected void initSstIcc() {
- mIccCard = new IccCard(this, LOG_TAG, IccCard.CARD_IS_3GPP, DBG);
- mIccRecords = new CdmaLteUiccRecords(this);
- mIccFileHandler = new CdmaLteUiccFileHandler(this);
+ mIccCard = UiccController.getInstance(this).getIccCard();
+ mIccRecords = mIccCard.getIccRecords();
+ mIccFileHandler = mIccCard.getIccFileHandler();
// CdmaLteServiceStateTracker registers with IccCard to know
// when the card is ready. So create mIccCard before the ServiceStateTracker
mSST = new CdmaLteServiceStateTracker(this);
@@ -164,7 +165,7 @@
// look for our wrapper within the asyncresult, skip the rest if it
// is null.
if (!(ar.userObj instanceof NetworkSelectMessage)) {
- if (DBG) Log.d(LOG_TAG, "unexpected result from user object.");
+ Log.e(LOG_TAG, "unexpected result from user object.");
return;
}
@@ -173,7 +174,7 @@
// found the object, now we send off the message we had originally
// attached to the request.
if (nsm.message != null) {
- if (DBG) Log.d(LOG_TAG, "sending original message to recipient");
+ if (DBG) log("sending original message to recipient");
AsyncResult.forMessage(nsm.message, ar.result, ar.exception);
nsm.message.sendToTarget();
}
@@ -200,14 +201,15 @@
ContentValues map = new ContentValues();
String operatorNumeric = mIccRecords.getOperatorNumeric();
map.put(Telephony.Carriers.NUMERIC, operatorNumeric);
- log("updateCurrentCarrierInProvider from UICC: numeric=" + operatorNumeric);
+ if (DBG) log("updateCurrentCarrierInProvider from UICC: numeric=" +
+ operatorNumeric);
mContext.getContentResolver().insert(uri, map);
return true;
} catch (SQLException e) {
Log.e(LOG_TAG, "[CDMALTEPhone] Can't store current operator ret false", e);
}
} else {
- log("updateCurrentCarrierInProvider mIccRecords == null ret false");
+ if (DBG) log("updateCurrentCarrierInProvider mIccRecords == null ret false");
}
return false;
}
@@ -259,7 +261,6 @@
@Override
protected void log(String s) {
- if (DBG)
Log.d(LOG_TAG, "[CDMALTEPhone] " + s);
}
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index b5dca65..e86e441 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -64,6 +64,7 @@
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.UUSInfo;
import com.android.internal.telephony.cat.CatService;
+import com.android.internal.telephony.uicc.UiccController;
import java.util.ArrayList;
import java.util.List;
@@ -149,9 +150,9 @@
}
protected void initSstIcc() {
- mIccCard = new IccCard(this, LOG_TAG, IccCard.CARD_IS_NOT_3GPP, DBG);
- mIccRecords = new RuimRecords(this);
- mIccFileHandler = new RuimFileHandler(this);
+ mIccCard = UiccController.getInstance(this).getIccCard();
+ mIccRecords = mIccCard.getIccRecords();
+ mIccFileHandler = mIccCard.getIccFileHandler();
// CdmaServiceStateTracker registers with IccCard to know
// when the Ruim card is ready. So create mIccCard before the ServiceStateTracker
mSST = new CdmaServiceStateTracker(this);
@@ -242,7 +243,6 @@
mSMS.dispose();
mIccFileHandler.dispose(); // instance of RuimFileHandler
mIccRecords.dispose();
- mIccCard.dispose();
mRuimPhoneBookInterfaceManager.dispose();
mRuimSmsInterfaceManager.dispose();
mSubInfo.dispose();
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java
index 8375fd0..e195ff2 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java
@@ -27,7 +27,7 @@
public final class CdmaLteUiccFileHandler extends IccFileHandler {
static final String LOG_TAG = "CDMA";
- CdmaLteUiccFileHandler(CDMALTEPhone phone) {
+ public CdmaLteUiccFileHandler(CDMALTEPhone phone) {
super(phone);
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java
index 0a285b9..ca1e96d 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java
@@ -436,6 +436,10 @@
return true;
}
+ if (phone == null || phone.mIccCard == null) {
+ return false;
+ }
+
if (phone.mIccCard.isApplicationOnIcc(AppType.APPTYPE_CSIM) &&
((mMdn == null) || (mMin == null))) {
return false;
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java b/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java
index 375cc07..e854d7f 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java
@@ -25,6 +25,7 @@
import com.android.internal.telephony.IccFileTypeMismatch;
import com.android.internal.telephony.IccIoResult;
import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.PhoneBase;
import com.android.internal.telephony.PhoneProxy;
import java.util.ArrayList;
@@ -38,7 +39,7 @@
//***** Instance Variables
//***** Constructor
- RuimFileHandler(CDMAPhone phone) {
+ public RuimFileHandler(PhoneBase phone) {
super(phone);
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
index e518c4c..265dff7 100755
--- a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
@@ -31,6 +31,7 @@
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.IccRefreshResponse;
import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.PhoneBase;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.MccTable;
@@ -61,7 +62,6 @@
// ***** Event Constants
- private static final int EVENT_RUIM_READY = 1;
private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 2;
private static final int EVENT_GET_IMSI_DONE = 3;
private static final int EVENT_GET_DEVICE_IDENTITY_DONE = 4;
@@ -78,7 +78,7 @@
private static final int EVENT_RUIM_REFRESH = 31;
- RuimRecords(CDMAPhone p) {
+ public RuimRecords(PhoneBase p) {
super(p);
adnCache = new AdnRecordCache(phone);
@@ -88,8 +88,6 @@
// recordsToLoad is set to 0 because no requests are made yet
recordsToLoad = 0;
-
- p.mIccCard.registerForRuimReady(this, EVENT_RUIM_READY, null);
p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
// NOTE the EVENT_SMS_ON_RUIM is not registered
p.mCM.registerForIccRefresh(this, EVENT_RUIM_REFRESH, null);
@@ -102,7 +100,6 @@
@Override
public void dispose() {
//Unregister for all events
- phone.mIccCard.unregisterForRuimReady(this);
phone.mCM.unregisterForOffOrNotAvailable( this);
phone.mCM.unregisterForIccRefresh(this);
}
@@ -206,10 +203,6 @@
}
try { switch (msg.what) {
- case EVENT_RUIM_READY:
- onRuimReady();
- break;
-
case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
onRadioOffOrNotAvailable();
break;
@@ -349,7 +342,8 @@
IccCard.INTENT_VALUE_ICC_LOADED, null);
}
- private void onRuimReady() {
+ @Override
+ public void onReady() {
/* broadcast intent ICC_READY here so that we can make sure
READY is sent before IMSI ready
*/
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 4c846f1..5e9a4f2 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -70,6 +70,7 @@
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.UUSInfo;
import com.android.internal.telephony.test.SimulatedRadioControl;
+import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.IccVmNotSupportedException;
import com.android.internal.telephony.ServiceStateTracker;
@@ -136,12 +137,12 @@
}
mCM.setPhoneType(Phone.PHONE_TYPE_GSM);
- mIccCard = new IccCard(this, LOG_TAG, IccCard.CARD_IS_3GPP, true);
+ mIccCard = UiccController.getInstance(this).getIccCard();
mCT = new GsmCallTracker(this);
mSST = new GsmServiceStateTracker (this);
mSMS = new GsmSMSDispatcher(this, mSmsStorageMonitor, mSmsUsageMonitor);
- mIccFileHandler = new SIMFileHandler(this);
- mIccRecords = new SIMRecords(this);
+ mIccFileHandler = mIccCard.getIccFileHandler();
+ mIccRecords = mIccCard.getIccRecords();
mDataConnectionTracker = new GsmDataConnectionTracker (this);
if (!unitTestMode) {
mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);
@@ -220,7 +221,6 @@
mSST.dispose();
mIccFileHandler.dispose(); // instance of SimFileHandler
mIccRecords.dispose();
- mIccCard.dispose();
mSimPhoneBookIntManager.dispose();
mSimSmsIntManager.dispose();
mSubInfo.dispose();
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java b/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java
index e8d10f9..8c3bc0e 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java
@@ -23,22 +23,20 @@
import com.android.internal.telephony.IccCardApplication;
import com.android.internal.telephony.IccConstants;
import com.android.internal.telephony.IccFileHandler;
-import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneBase;
/**
* {@hide}
*/
public final class SIMFileHandler extends IccFileHandler implements IccConstants {
static final String LOG_TAG = "GSM";
- private Phone mPhone;
//***** Instance Variables
//***** Constructor
- SIMFileHandler(GSMPhone phone) {
+ public SIMFileHandler(PhoneBase phone) {
super(phone);
- mPhone = phone;
}
public void dispose() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index 1fb99e3..68d3b2a 100755
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -123,7 +123,6 @@
// ***** Event Constants
- private static final int EVENT_SIM_READY = 1;
private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 2;
protected static final int EVENT_GET_IMSI_DONE = 3;
protected static final int EVENT_GET_ICCID_DONE = 4;
@@ -188,7 +187,6 @@
// recordsToLoad is set to 0 because no requests are made yet
recordsToLoad = 0;
- p.mIccCard.registerForReady(this, EVENT_SIM_READY, null);
p.mCM.registerForOffOrNotAvailable(
this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
p.mCM.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null);
@@ -202,7 +200,6 @@
@Override
public void dispose() {
//Unregister for all events
- phone.mIccCard.unregisterForReady(this);
phone.mCM.unregisterForOffOrNotAvailable( this);
phone.mCM.unregisterForIccRefresh(this);
}
@@ -526,10 +523,6 @@
}
try { switch (msg.what) {
- case EVENT_SIM_READY:
- onSimReady();
- break;
-
case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
onRadioOffOrNotAvailable();
break;
@@ -1296,7 +1289,8 @@
}
}
- public void onSimReady() {
+ @Override
+ public void onReady() {
/* broadcast intent SIM_READY here so that we can make sure
READY is sent before IMSI ready
*/
diff --git a/telephony/java/com/android/internal/telephony/uicc/UiccController.java b/telephony/java/com/android/internal/telephony/uicc/UiccController.java
new file mode 100644
index 0000000..5961efd
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/uicc/UiccController.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 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 com.android.internal.telephony.uicc;
+
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.cdma.CDMALTEPhone;
+import com.android.internal.telephony.cdma.CDMAPhone;
+import com.android.internal.telephony.gsm.GSMPhone;
+
+import android.util.Log;
+
+/* This class is responsible for keeping all knowledge about
+ * ICCs in the system. It is also used as API to get appropriate
+ * applications to pass them to phone and service trackers.
+ */
+public class UiccController {
+ private static final boolean DBG = true;
+ private static final String LOG_TAG = "RIL_UiccController";
+
+ private static UiccController mInstance;
+
+ private PhoneBase mCurrentPhone;
+ private boolean mIsCurrentCard3gpp;
+ private IccCard mIccCard;
+
+ public static synchronized UiccController getInstance(PhoneBase phone) {
+ if (mInstance == null) {
+ mInstance = new UiccController(phone);
+ } else {
+ mInstance.setNewPhone(phone);
+ }
+ return mInstance;
+ }
+
+ public IccCard getIccCard() {
+ return mIccCard;
+ }
+
+ private UiccController(PhoneBase phone) {
+ if (DBG) log("Creating UiccController");
+ setNewPhone(phone);
+ }
+
+ private void setNewPhone(PhoneBase phone) {
+ mCurrentPhone = phone;
+ if (phone instanceof GSMPhone) {
+ if (DBG) log("New phone is GSMPhone");
+ updateCurrentCard(IccCard.CARD_IS_3GPP);
+ } else if (phone instanceof CDMALTEPhone){
+ if (DBG) log("New phone type is CDMALTEPhone");
+ updateCurrentCard(IccCard.CARD_IS_3GPP);
+ } else if (phone instanceof CDMAPhone){
+ if (DBG) log("New phone type is CDMAPhone");
+ updateCurrentCard(IccCard.CARD_IS_NOT_3GPP);
+ } else {
+ Log.e(LOG_TAG, "Unhandled phone type. Critical error!");
+ }
+ }
+
+ private void updateCurrentCard(boolean isNewCard3gpp) {
+ if (mIsCurrentCard3gpp == isNewCard3gpp && mIccCard != null) {
+ return;
+ }
+
+ if (mIccCard != null) {
+ mIccCard.dispose();
+ mIccCard = null;
+ }
+
+ mIsCurrentCard3gpp = isNewCard3gpp;
+ mIccCard = new IccCard(mCurrentPhone, mCurrentPhone.getPhoneName(),
+ isNewCard3gpp, DBG);
+ }
+
+ private void log(String string) {
+ Log.d(LOG_TAG, string);
+ }
+}
\ No newline at end of file