Merge "Adding enable/disable option for sendCommand()"
diff --git a/api/current.xml b/api/current.xml
index ed51bbf..310944f 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -93977,6 +93977,8 @@
>
<parameter name="uid" type="int">
</parameter>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
</method>
<method name="onDisable"
return="void"
@@ -94106,6 +94108,8 @@
>
<parameter name="uid" type="int">
</parameter>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
</method>
<method name="onRequiresCell"
return="boolean"
@@ -94167,6 +94171,8 @@
>
<parameter name="minTime" type="long">
</parameter>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
</method>
<method name="onSupportsAltitude"
return="boolean"
@@ -111503,6 +111509,19 @@
<parameter name="refCounted" type="boolean">
</parameter>
</method>
+<method name="setWorkSource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
+</method>
</class>
</package>
<package name="android.opengl"
@@ -138355,6 +138374,19 @@
<parameter name="value" type="boolean">
</parameter>
</method>
+<method name="setWorkSource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ws" type="android.os.WorkSource">
+</parameter>
+</method>
</class>
<class name="Process"
extends="java.lang.Object"
@@ -139503,6 +139535,134 @@
</parameter>
</method>
</class>
+<class name="WorkSource"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="WorkSource"
+ type="android.os.WorkSource"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="WorkSource"
+ type="android.os.WorkSource"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="orig" type="android.os.WorkSource">
+</parameter>
+</constructor>
+<method name="add"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.os.WorkSource">
+</parameter>
+</method>
+<method name="clear"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="diff"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.os.WorkSource">
+</parameter>
+</method>
+<method name="remove"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.os.WorkSource">
+</parameter>
+</method>
+<method name="set"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="other" type="android.os.WorkSource">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
</package>
<package name="android.os.storage"
>
@@ -142069,6 +142229,19 @@
<parameter name="args" type="android.os.Bundle">
</parameter>
</method>
+<method name="switchToHeader"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="header" type="android.preference.PreferenceActivity.Header">
+</parameter>
+</method>
<field name="EXTRA_NO_HEADERS"
type="java.lang.String"
transient="false"
@@ -142102,15 +142275,28 @@
visibility="public"
>
</field>
+<field name="HEADER_ID_UNDEFINED"
+ type="long"
+ transient="false"
+ volatile="false"
+ value="-1L"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="PreferenceActivity.Header"
extends="java.lang.Object"
abstract="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
+<implements name="android.os.Parcelable">
+</implements>
<constructor name="PreferenceActivity.Header"
type="android.preference.PreferenceActivity.Header"
static="false"
@@ -142119,6 +142305,65 @@
visibility="public"
>
</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="readFromParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="in" type="android.os.Parcel">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="extras"
+ type="android.os.Bundle"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="fragment"
type="java.lang.String"
transient="false"
@@ -142139,8 +142384,8 @@
visibility="public"
>
</field>
-<field name="icon"
- type="android.graphics.drawable.Drawable"
+<field name="iconRes"
+ type="int"
transient="false"
volatile="false"
static="false"
@@ -142149,8 +142394,8 @@
visibility="public"
>
</field>
-<field name="iconRes"
- type="int"
+<field name="id"
+ type="long"
transient="false"
volatile="false"
static="false"
@@ -233790,6 +234035,17 @@
visibility="public"
>
</method>
+<method name="isIconified"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isSubmitButtonEnabled"
return="boolean"
abstract="false"
@@ -233801,6 +234057,19 @@
visibility="public"
>
</method>
+<method name="setIconified"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="iconify" type="boolean">
+</parameter>
+</method>
<method name="setIconifiedByDefault"
return="void"
abstract="false"
@@ -233814,6 +234083,19 @@
<parameter name="iconified" type="boolean">
</parameter>
</method>
+<method name="setOnCloseListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.widget.SearchView.OnCloseListener">
+</parameter>
+</method>
<method name="setOnQueryChangeListener"
return="void"
abstract="false"
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index d3ec3d9..e1d6619 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -64,7 +64,7 @@
= IPowerManager.Stub.asInterface(ServiceManager.getService(Context.POWER_SERVICE));
try {
IBinder lock = new Binder();
- pm.acquireWakeLock(PowerManager.FULL_WAKE_LOCK, lock, "svc power");
+ pm.acquireWakeLock(PowerManager.FULL_WAKE_LOCK, lock, "svc power", null);
pm.setStayOnSetting(val);
pm.releaseWakeLock(lock, 0);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 97aa23e..2e8d682 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4204,6 +4204,7 @@
}
final void performStart() {
+ mFragments.mStateSaved = false;
mCalled = false;
mFragments.execPendingActions();
mInstrumentation.callActivityOnStart(this);
@@ -4221,6 +4222,8 @@
}
final void performRestart() {
+ mFragments.mStateSaved = false;
+
synchronized (mManagedCursors) {
final int N = mManagedCursors.size();
for (int i=0; i<N; i++) {
@@ -4359,6 +4362,7 @@
if (Config.LOGV) Log.v(
TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
+ ", resCode=" + resultCode + ", data=" + data);
+ mFragments.mStateSaved = false;
if (who == null) {
onActivityResult(requestCode, resultCode, data);
} else {
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 7f749bb..950d339 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -47,6 +47,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.WorkSource;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.text.format.Time;
@@ -129,8 +130,8 @@
private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000;
- private static final String SYNC_WAKE_LOCK_PREFIX = "SyncWakeLock";
- private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock";
+ private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*";
+ private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
private Context mContext;
@@ -1735,6 +1736,7 @@
PowerManager.WakeLock oldWakeLock = mSyncWakeLock;
try {
mSyncWakeLock = getSyncWakeLock(op.account.type, op.authority);
+ mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterInfo.uid));
mSyncWakeLock.acquire();
} finally {
if (oldWakeLock != null && oldWakeLock != mSyncWakeLock) {
diff --git a/core/java/android/database/sqlite/DatabaseConnectionPool.java b/core/java/android/database/sqlite/DatabaseConnectionPool.java
index aede438..54b0605 100644
--- a/core/java/android/database/sqlite/DatabaseConnectionPool.java
+++ b/core/java/android/database/sqlite/DatabaseConnectionPool.java
@@ -99,7 +99,7 @@
poolObj = mPool.get(0);
} else {
for (int i = 0; i < mMaxPoolSize; i++) {
- if (mPool.get(i).mDb.mCache.isSqlInStatementCache(sql)) {
+ if (mPool.get(i).mDb.isSqlInStatementCache(sql)) {
poolObj = mPool.get(i);
break;
}
@@ -125,8 +125,7 @@
// there are free connections available. pick one
// preferably a connection caching the pre-compiled statement of the given SQL
for (int i = 0; i < poolSize; i++) {
- if (mPool.get(i).isFree() &&
- mPool.get(i).mDb.mCache.isSqlInStatementCache(sql)) {
+ if (mPool.get(i).isFree() && mPool.get(i).mDb.isSqlInStatementCache(sql)) {
poolObj = mPool.get(i);
break;
}
diff --git a/core/java/android/database/sqlite/SQLiteCache.java b/core/java/android/database/sqlite/SQLiteCache.java
deleted file mode 100644
index 049fb62..0000000
--- a/core/java/android/database/sqlite/SQLiteCache.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.database.sqlite;
-
-import android.util.Log;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * For each instance of {@link SQLiteDatabase}, this class maintains a LRU cache to store
- * the compiled query statement ids returned by sqlite database.
- *<p>
- *<ul>
- * <li>key = SQL statement with "?" for bind args</li>
- * <li>value = {@link SQLiteCompiledSql}</li>
- *</ul>
- * If an application opens the database and keeps it open during its entire life, then
- * there will not be an overhead of compilation of SQL statements by sqlite.
- *<p>
- * Why is this cache NOT static? because sqlite attaches compiled-sql statements to the
- * database connections.
- *<p>
- * This cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
- * (@link #setMaxSqlCacheSize(int)}).
- */
-/* package */ class SQLiteCache {
- private static final String TAG = "SQLiteCache";
-
- /** The {@link SQLiteDatabase} instance this cache is attached to */
- private final SQLiteDatabase mDatabase;
-
- /** Default statement-cache size per database connection ( = instance of this class) */
- private int mMaxSqlCacheSize = 25;
-
- /** The LRU cache */
- private final Map<String, SQLiteCompiledSql> mCompiledQueries =
- new LinkedHashMap<String, SQLiteCompiledSql>(mMaxSqlCacheSize + 1, 0.75f, true) {
- @Override
- public boolean removeEldestEntry(Map.Entry<String, SQLiteCompiledSql> eldest) {
- // eldest = least-recently used entry
- // if it needs to be removed to accommodate a new entry,
- // close {@link SQLiteCompiledSql} represented by this entry, if not in use
- // and then let it be removed from the Map.
- mDatabase.verifyLockOwner();
- if (this.size() <= mMaxSqlCacheSize) {
- // cache is not full. nothing needs to be removed
- return false;
- }
- // cache is full. eldest will be removed.
- eldest.getValue().releaseIfNotInUse();
- // return true, so that this entry is removed automatically by the caller.
- return true;
- }
- };
-
- /** Maintains whether or not cacheFullWarning has been logged */
- private boolean mCacheFullWarning;
-
- /** The following 2 members maintain stats about cache hits and misses */
- private int mNumCacheHits;
- private int mNumCacheMisses;
-
- /**
- * Constructor used by {@link SQLiteDatabase}.
- * @param db
- */
- /* package */ SQLiteCache(SQLiteDatabase db) {
- mDatabase = db;
- }
-
- /**
- * Adds the given SQL and its compiled-statement to the cache, if the given SQL statement
- * doesn't already exist in cache.
- *
- * @return true if added to cache. false otherwise.
- */
- /* package */ synchronized boolean addToCompiledQueries(String sql,
- SQLiteCompiledSql compiledStatement) {
- if (mCompiledQueries.containsKey(sql)) {
- // already exists.
- return false;
- }
-
- /* add the given SQLiteCompiledSql compiledStatement to cache.
- * no need to worry about the cache size - because {@link #mCompiledQueries}
- * self-limits its size to {@link #mMaxSqlCacheSize}.
- */
- mCompiledQueries.put(sql, compiledStatement);
-
- // need to log a warning to say that the cache is full?
- if (!isCacheFullWarningLogged() && mCompiledQueries.size() == mMaxSqlCacheSize) {
- /*
- * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
- * log a warning.
- * chances are it is NOT using ? for bindargs - or cachesize is too small.
- */
- Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
- mDatabase.getPath() +
- ". Use setMaxSqlCacheSize() in SQLiteDatabase to increase cachesize. ");
- setCacheFullWarningLogged();
- }
- return true;
- }
-
- /**
- * Returns the compiled-statement for the given SQL statement, if the entry exists in cache
- * and is free to use. Returns null otherwise.
- * <p>
- * If a compiled-sql statement is returned for the caller, it is reserved for the caller.
- * So, don't use this method unless the caller needs to acquire the object.
- */
- /* package */ synchronized SQLiteCompiledSql getCompiledStatementForSql(String sql) {
- SQLiteCompiledSql compiledStatement = mCompiledQueries.get(sql);
- if (compiledStatement == null) {
- mNumCacheMisses++;
- return null;
- }
- mNumCacheHits++;
- // reserve it for the caller, if it is not already in use
- if (!compiledStatement.acquire()) {
- // couldn't acquire it since it is already in use. bug in app?
- if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
- Log.w(TAG, "Possible bug: Either using the same SQL in 2 threads at " +
- " the same time, or previous instance of this SQL statement was " +
- "never close()d. " + compiledStatement.toString());
- }
- return null;
- }
- return compiledStatement;
- }
-
- /**
- * If the given statement is in cache, it is released back to cache and it is made available for
- * others to use.
- * <p>
- * return true if the statement is put back in cache, false otherwise (false = the statement
- * is NOT in cache)
- */
- /* package */ synchronized boolean releaseBackToCache(SQLiteCompiledSql stmt) {
- if (!mCompiledQueries.containsValue(stmt)) {
- return false;
- }
- // it is in cache. release it from the caller, make it available for others to use
- stmt.free();
- return true;
- }
-
- /**
- * releases all compiled-sql statements in the cache.
- */
- /* package */ synchronized void dealloc() {
- for (SQLiteCompiledSql stmt : mCompiledQueries.values()) {
- stmt.setState(SQLiteCompiledSql.NSTATE_CACHE_DEALLOC);
- stmt.releaseFromDatabase();
- }
- mCompiledQueries.clear();
- }
-
- /**
- * see documentation on {@link SQLiteDatabase#setMaxSqlCacheSize(int)}.
- */
- /* package */ synchronized void setMaxSqlCacheSize(int cacheSize) {
- if (cacheSize > SQLiteDatabase.MAX_SQL_CACHE_SIZE || cacheSize < 0) {
- throw new IllegalStateException("expected value between 0 and " +
- SQLiteDatabase.MAX_SQL_CACHE_SIZE);
- } else if (cacheSize < mMaxSqlCacheSize) {
- throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
- "set with previous setMaxSqlCacheSize() call.");
- }
- mMaxSqlCacheSize = cacheSize;
- }
-
- /* package */ synchronized boolean isSqlInStatementCache(String sql) {
- return mCompiledQueries.containsKey(sql);
- }
-
- private synchronized boolean isCacheFullWarningLogged() {
- return mCacheFullWarning;
- }
-
- private synchronized void setCacheFullWarningLogged() {
- mCacheFullWarning = true;
- }
- /* package */ synchronized int getCacheHitNum() {
- return mNumCacheHits;
- }
- /* package */ synchronized int getCacheMissNum() {
- return mNumCacheMisses;
- }
- /* package */ synchronized int getCachesize() {
- return mCompiledQueries.size();
- }
-
- // only for testing
- /* package */ synchronized Set<String> getKeys() {
- return mCompiledQueries.keySet();
- }
-}
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index 120d090..0b5a4df 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -16,7 +16,6 @@
package android.database.sqlite;
-import android.database.DatabaseUtils;
import android.util.Log;
/**
@@ -25,19 +24,19 @@
* and it is released in one of the 2 following ways
* 1. when {@link SQLiteDatabase} object is closed.
* 2. if this is not cached in {@link SQLiteDatabase}, {@link android.database.Cursor#close()}
- * releases this obj.
+ * releaases this obj.
*/
/* package */ class SQLiteCompiledSql {
private static final String TAG = "SQLiteCompiledSql";
/** The database this program is compiled against. */
- /* package */ final SQLiteDatabase mDatabase;
+ /* package */ SQLiteDatabase mDatabase;
/**
* Native linkage, do not modify. This comes from the database.
*/
- /* package */ final int nHandle;
+ /* package */ int nHandle = 0;
/**
* Native linkage, do not modify. When non-0 this holds a reference to a valid
@@ -47,53 +46,52 @@
*/
/* package */ int nStatement = 0;
- /** the following 3 members are for debugging purposes */
- private final String mSqlStmt;
- private final Throwable mStackTrace;
- private int nState = 0;
- /** values the above member can take */
- private static final int NSTATE_CACHEABLE = 64;
- private static final int NSTATE_IN_CACHE = 32;
- private static final int NSTATE_INUSE = 16;
- private static final int NSTATE_INUSE_RESETMASK = 0x0f;
- /* package */ static final int NSTATE_CLOSE_NOOP = 1;
- private static final int NSTATE_EVICTED_FROM_CACHE = 2;
- /* package */ static final int NSTATE_CACHE_DEALLOC = 4;
- private static final int NSTATE_IN_FINALIZER_Q = 8;
+ /** the following are for debugging purposes */
+ private String mSqlStmt = null;
+ private Throwable mStackTrace = null;
- private SQLiteCompiledSql(SQLiteDatabase db, String sql) {
- db.verifyDbIsOpen();
- db.verifyLockOwner();
+ /** when in cache and is in use, this member is set */
+ private boolean mInUse = false;
+
+ /* package */ SQLiteCompiledSql(SQLiteDatabase db, String sql) {
+ if (!db.isOpen()) {
+ throw new IllegalStateException("database " + db.getPath() + " already closed");
+ }
mDatabase = db;
mSqlStmt = sql;
mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
- nHandle = db.mNativeHandle;
- native_compile(sql);
+ this.nHandle = db.mNativeHandle;
+ compile(sql, true);
}
- /* package */ static SQLiteCompiledSql get(SQLiteDatabase db, String sql, int type) {
- // only CRUD statements are cached.
- if (type != DatabaseUtils.STATEMENT_SELECT && type != DatabaseUtils.STATEMENT_UPDATE) {
- return new SQLiteCompiledSql(db, sql);
+ /**
+ * Compiles the given SQL into a SQLite byte code program using sqlite3_prepare_v2(). If
+ * this method has been called previously without a call to close and forCompilation is set
+ * to false the previous compilation will be used. Setting forceCompilation to true will
+ * always re-compile the program and should be done if you pass differing SQL strings to this
+ * method.
+ *
+ * <P>Note: this method acquires the database lock.</P>
+ *
+ * @param sql the SQL string to compile
+ * @param forceCompilation forces the SQL to be recompiled in the event that there is an
+ * existing compiled SQL program already around
+ */
+ private void compile(String sql, boolean forceCompilation) {
+ mDatabase.verifyLockOwner();
+ // Only compile if we don't have a valid statement already or the caller has
+ // explicitly requested a recompile.
+ if (forceCompilation) {
+ // Note that the native_compile() takes care of destroying any previously
+ // existing programs before it compiles.
+ native_compile(sql);
}
- // the given SQL statement is cacheable.
- SQLiteCompiledSql stmt = db.mCache.getCompiledStatementForSql(sql);
- if (stmt != null) {
- return stmt;
- }
- // either the entry doesn't exist in cache or the one in cache is currently in use.
- // try to add it to cache and let cache worry about what copy to keep
- stmt = new SQLiteCompiledSql(db, sql);
- stmt.nState |= NSTATE_CACHEABLE |
- ((db.mCache.addToCompiledQueries(sql, stmt)) ? NSTATE_IN_CACHE : 0);
- return stmt;
}
- /* package */ synchronized void releaseFromDatabase() {
+ /* package */ void releaseSqlStatement() {
// Note that native_finalize() checks to make sure that nStatement is
// non-null before destroying it.
if (nStatement != 0) {
- nState |= NSTATE_IN_FINALIZER_Q;
mDatabase.finalizeStatementLater(nStatement);
nStatement = 0;
}
@@ -103,47 +101,20 @@
* returns true if acquire() succeeds. false otherwise.
*/
/* package */ synchronized boolean acquire() {
- if ((nState & NSTATE_INUSE) > 0 ) {
- // this object is already in use
+ if (mInUse) {
+ // someone already has acquired it.
return false;
}
- nState |= NSTATE_INUSE;
+ mInUse = true;
return true;
}
- /* package */ synchronized void free() {
- nState &= NSTATE_INUSE_RESETMASK;
+ /* package */ synchronized void release() {
+ mInUse = false;
}
- /* package */ void release(int type) {
- if (type != DatabaseUtils.STATEMENT_SELECT && type != DatabaseUtils.STATEMENT_UPDATE) {
- // it is not cached. release its memory from the database.
- releaseFromDatabase();
- return;
- }
- // if in cache, reset its in-use flag
- if (!mDatabase.mCache.releaseBackToCache(this)) {
- // not in cache. release its memory from the database.
- releaseFromDatabase();
- }
- }
-
- /* package */ synchronized void releaseIfNotInUse() {
- nState |= NSTATE_EVICTED_FROM_CACHE;
- // if it is not in use, release its memory from the database
- if ((nState & NSTATE_INUSE) == 0) {
- releaseFromDatabase();
- }
- }
-
- // only for testing purposes
/* package */ synchronized boolean isInUse() {
- return (nState & NSTATE_INUSE) > 0;
- }
-
- /* package */ synchronized SQLiteCompiledSql setState(int val) {
- nState = nState & val;
- return this; // for chaining
+ return mInUse;
}
/**
@@ -154,18 +125,11 @@
try {
if (nStatement == 0) return;
// finalizer should NEVER get called
- // but if the database itself is not closed and is GC'ed, then
- // all sub-objects attached to the database could end up getting GC'ed too.
- // in that case, don't print any warning.
- if ((nState & NSTATE_INUSE) == 0) {
- // no need to print warning
- } else {
- int len = mSqlStmt.length();
- Log.w(TAG, "Releasing SQL statement in finalizer. " +
- "Could be due to close() not being called on the cursor or on the database. " +
- toString(), mStackTrace);
- }
- releaseFromDatabase();
+ int len = mSqlStmt.length();
+ Log.w(TAG, "Releasing statement in a finalizer. Please ensure " +
+ "that you explicitly call close() on your cursor: " +
+ mSqlStmt.substring(0, (len > 100) ? 100 : len), mStackTrace);
+ releaseSqlStatement();
} finally {
super.finalize();
}
@@ -176,27 +140,6 @@
StringBuilder buff = new StringBuilder();
buff.append(" nStatement=");
buff.append(nStatement);
- if ((nState & NSTATE_CACHEABLE) > 0) {
- buff.append(",cacheable");
- }
- if ((nState & NSTATE_IN_CACHE) > 0) {
- buff.append(",cached");
- }
- if ((nState & NSTATE_INUSE) > 0) {
- buff.append(",in_use");
- }
- if ((nState & NSTATE_CLOSE_NOOP) > 0) {
- buff.append(",no_op_close");
- }
- if ((nState & NSTATE_EVICTED_FROM_CACHE) > 0) {
- buff.append(",evicted_from_cache");
- }
- if ((nState & NSTATE_CACHE_DEALLOC) > 0) {
- buff.append(",dealloc_cache");
- }
- if ((nState & NSTATE_IN_FINALIZER_Q) > 0) {
- buff.append(",in dbFInalizerQ");
- }
buff.append(", db=");
buff.append(mDatabase.getPath());
buff.append(", db_connectionNum=");
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 70bd3d8..4200701 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -41,6 +41,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
@@ -259,11 +260,60 @@
private final WeakHashMap<SQLiteClosable, Object> mPrograms;
/**
- * Absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}.
- * Size of each prepared-statement is between 1K - 6K, depending on the complexity of the
+ * for each instance of this class, a LRU cache is maintained to store
+ * the compiled query statement ids returned by sqlite database.
+ * key = SQL statement with "?" for bind args
+ * value = {@link SQLiteCompiledSql}
+ * If an application opens the database and keeps it open during its entire life, then
+ * there will not be an overhead of compilation of SQL statements by sqlite.
+ *
+ * why is this cache NOT static? because sqlite attaches compiledsql statements to the
+ * struct created when {@link SQLiteDatabase#openDatabase(String, CursorFactory, int)} is
+ * invoked.
+ *
+ * this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
+ * (@link #setMaxSqlCacheSize(int)}).
+ */
+ // default statement-cache size per database connection ( = instance of this class)
+ private int mMaxSqlCacheSize = 25;
+ /* package */ final Map<String, SQLiteCompiledSql> mCompiledQueries =
+ new LinkedHashMap<String, SQLiteCompiledSql>(mMaxSqlCacheSize + 1, 0.75f, true) {
+ @Override
+ public boolean removeEldestEntry(Map.Entry<String, SQLiteCompiledSql> eldest) {
+ // eldest = least-recently used entry
+ // if it needs to be removed to accommodate a new entry,
+ // close {@link SQLiteCompiledSql} represented by this entry, if not in use
+ // and then let it be removed from the Map.
+ // when this is called, the caller must be trying to add a just-compiled stmt
+ // to cache; i.e., caller should already have acquired database lock AND
+ // the lock on mCompiledQueries. do as assert of these two 2 facts.
+ verifyLockOwner();
+ if (this.size() <= mMaxSqlCacheSize) {
+ // cache is not full. nothing needs to be removed
+ return false;
+ }
+ // cache is full. eldest will be removed.
+ SQLiteCompiledSql entry = eldest.getValue();
+ if (!entry.isInUse()) {
+ // this {@link SQLiteCompiledSql} is not in use. release it.
+ entry.releaseSqlStatement();
+ }
+ // return true, so that this entry is removed automatically by the caller.
+ return true;
+ }
+ };
+ /**
+ * absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}
+ * size of each prepared-statement is between 1K - 6K, depending on the complexity of the
* SQL statement & schema.
*/
public static final int MAX_SQL_CACHE_SIZE = 100;
+ private int mCacheFullWarnings;
+ private static final int MAX_WARNINGS_ON_CACHESIZE_CONDITION = 1;
+
+ /** maintain stats about number of cache hits and misses */
+ private int mNumCacheHits;
+ private int mNumCacheMisses;
/** Used to find out where this object was created in case it never got closed. */
private final Throwable mStackTrace;
@@ -272,9 +322,6 @@
private static final String LOG_SLOW_QUERIES_PROPERTY = "db.log.slow_query_threshold";
private final int mSlowQueryThreshold;
- /** the LRU cache */
- /* package */ final SQLiteCache mCache = new SQLiteCache(this);
-
/** stores the list of statement ids that need to be finalized by sqlite */
private final ArrayList<Integer> mClosedStatementIds = new ArrayList<Integer>();
@@ -1040,7 +1087,7 @@
* to be closed. sqlite doesn't let a database close if there are
* any unfinalized statements - such as the compiled-sql objects in mCompiledQueries.
*/
- mCache.dealloc();
+ deallocCachedSqlStatements();
Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
while (iter.hasNext()) {
@@ -1848,11 +1895,12 @@
executeSql(sql, bindArgs);
}
- private void executeSql(String sql, Object[] bindArgs) throws SQLException {
+ private int executeSql(String sql, Object[] bindArgs) throws SQLException {
long timeStart = SystemClock.uptimeMillis();
+ int n;
SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
try {
- statement.executeUpdateDelete();
+ n = statement.executeUpdateDelete();
} catch (SQLiteDatabaseCorruptException e) {
onCorruption();
throw e;
@@ -1860,6 +1908,7 @@
statement.close();
}
logTimeStat(sql, timeStart);
+ return n;
}
@Override
@@ -2043,20 +2092,115 @@
}
}
+ /*
+ * ============================================================================
+ *
+ * The following methods deal with compiled-sql cache
+ * ============================================================================
+ */
+ /**
+ * Adds the given SQL and its compiled-statement-id-returned-by-sqlite to the
+ * cache of compiledQueries attached to 'this'.
+ * <p>
+ * If there is already a {@link SQLiteCompiledSql} in compiledQueries for the given SQL,
+ * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
+ * mapping is NOT replaced with the new mapping).
+ */
+ /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
+ synchronized(mCompiledQueries) {
+ // don't insert the new mapping if a mapping already exists
+ if (mCompiledQueries.containsKey(sql)) {
+ return;
+ }
+
+ if (mCompiledQueries.size() == mMaxSqlCacheSize) {
+ /*
+ * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
+ * log a warning.
+ * chances are it is NOT using ? for bindargs - or cachesize is too small.
+ */
+ if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION) {
+ Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
+ getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. ");
+ }
+ }
+ /* add the given SQLiteCompiledSql compiledStatement to cache.
+ * no need to worry about the cache size - because {@link #mCompiledQueries}
+ * self-limits its size to {@link #mMaxSqlCacheSize}.
+ */
+ mCompiledQueries.put(sql, compiledStatement);
+ if (SQLiteDebug.DEBUG_SQL_CACHE) {
+ Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" +
+ mCompiledQueries.size() + "|" + sql);
+ }
+ }
+ }
+
+ /** package-level access for testing purposes */
+ /* package */ void deallocCachedSqlStatements() {
+ synchronized (mCompiledQueries) {
+ for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
+ compiledSql.releaseSqlStatement();
+ }
+ mCompiledQueries.clear();
+ }
+ }
+
+ /**
+ * From the compiledQueries cache, returns the compiled-statement-id for the given SQL.
+ * Returns null, if not found in the cache.
+ */
+ /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
+ SQLiteCompiledSql compiledStatement = null;
+ boolean cacheHit;
+ synchronized(mCompiledQueries) {
+ cacheHit = (compiledStatement = mCompiledQueries.get(sql)) != null;
+ }
+ if (cacheHit) {
+ mNumCacheHits++;
+ } else {
+ mNumCacheMisses++;
+ }
+
+ if (SQLiteDebug.DEBUG_SQL_CACHE) {
+ Log.v(TAG, "|cache_stats|" +
+ getPath() + "|" + mCompiledQueries.size() +
+ "|" + mNumCacheHits + "|" + mNumCacheMisses +
+ "|" + cacheHit + "|" + sql);
+ }
+ return compiledStatement;
+ }
+
/**
* Sets the maximum size of the prepared-statement cache for this database.
* (size of the cache = number of compiled-sql-statements stored in the cache).
*<p>
- * Maximum cache size can ONLY be increased from its current size (default = 25).
+ * Maximum cache size can ONLY be increased from its current size (default = 10).
* If this method is called with smaller size than the current maximum value,
* then IllegalStateException is thrown.
+ *<p>
+ * This method is thread-safe.
*
* @param cacheSize the size of the cache. can be (0 to {@link #MAX_SQL_CACHE_SIZE})
* @throws IllegalStateException if input cacheSize > {@link #MAX_SQL_CACHE_SIZE} or
* the value set with previous setMaxSqlCacheSize() call.
*/
public void setMaxSqlCacheSize(int cacheSize) {
- mCache.setMaxSqlCacheSize(cacheSize);
+ synchronized(this) {
+ if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
+ throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
+ } else if (cacheSize < mMaxSqlCacheSize) {
+ throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
+ "set with previous setMaxSqlCacheSize() call.");
+ }
+ mMaxSqlCacheSize = cacheSize;
+ }
+ }
+
+ /* package */ boolean isSqlInStatementCache(String sql) {
+ synchronized (mCompiledQueries) {
+ return mCompiledQueries.containsKey(sql);
+ }
}
/* package */ void finalizeStatementLater(int id) {
@@ -2340,16 +2484,16 @@
}
if (pageCount > 0) {
dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
- lookasideUsed, db.mCache.getCacheHitNum(),
- db.mCache.getCacheMissNum(), db.mCache.getCachesize()));
+ lookasideUsed, db.mNumCacheHits, db.mNumCacheMisses,
+ db.mCompiledQueries.size()));
}
}
// if there are pooled connections, return the cache stats for them also.
if (db.mConnectionPool != null) {
for (SQLiteDatabase pDb : db.mConnectionPool.getConnectionList()) {
dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
- + lastnode, 0, 0, 0, pDb.mCache.getCacheHitNum(),
- pDb.mCache.getCacheMissNum(), pDb.mCache.getCachesize()));
+ + lastnode, 0, 0, 0, pDb.mNumCacheHits, pDb.mNumCacheMisses,
+ pDb.mCompiledQueries.size()));
}
}
} catch (SQLiteException e) {
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index b390369..c7c0c79 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -110,6 +110,42 @@
}
}
+ private void compileSql() {
+ // only cache CRUD statements
+ if (mStatementType != DatabaseUtils.STATEMENT_SELECT &&
+ mStatementType != DatabaseUtils.STATEMENT_UPDATE) {
+ mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
+ nStatement = mCompiledSql.nStatement;
+ // since it is not in the cache, no need to acquire() it.
+ return;
+ }
+
+ mCompiledSql = mDatabase.getCompiledStatementForSql(mSql);
+ if (mCompiledSql == null) {
+ // create a new compiled-sql obj
+ mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
+
+ // add it to the cache of compiled-sqls
+ // but before adding it and thus making it available for anyone else to use it,
+ // make sure it is acquired by me.
+ mCompiledSql.acquire();
+ mDatabase.addToCompiledQueries(mSql, mCompiledSql);
+ } else {
+ // it is already in compiled-sql cache.
+ // try to acquire the object.
+ if (!mCompiledSql.acquire()) {
+ int last = mCompiledSql.nStatement;
+ // the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object.
+ // we can't have two different SQLiteProgam objects can't share the same
+ // CompiledSql object. create a new one.
+ // finalize it when I am done with it in "this" object.
+ mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
+ // since it is not in the cache, no need to acquire() it.
+ }
+ }
+ nStatement = mCompiledSql.nStatement;
+ }
+
@Override
protected void onAllReferencesReleased() {
release();
@@ -127,7 +163,16 @@
if (mCompiledSql == null) {
return;
}
- mCompiledSql.release(mStatementType);
+ synchronized(mDatabase.mCompiledQueries) {
+ if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) {
+ // it is NOT in compiled-sql cache. i.e., responsibility of
+ // releasing this statement is on me.
+ mCompiledSql.releaseSqlStatement();
+ } else {
+ // it is in compiled-sql cache. reset its CompiledSql#mInUse flag
+ mCompiledSql.release();
+ }
+ }
mCompiledSql = null;
nStatement = 0;
}
@@ -288,9 +333,6 @@
synchronized (this) {
mBindArgs = null;
if (nHandle == 0 || !mDatabase.isOpen()) {
- if (mCompiledSql != null) {
- mCompiledSql.setState(SQLiteCompiledSql.NSTATE_CLOSE_NOOP);
- }
return;
}
releaseReference();
@@ -307,8 +349,7 @@
/* package */ synchronized void compileAndbindAllArgs() {
if (nStatement == 0) {
// SQL statement is not compiled yet. compile it now.
- mCompiledSql = SQLiteCompiledSql.get(mDatabase, mSql, mStatementType);
- nStatement = mCompiledSql.nStatement;
+ compileSql();
}
if (mBindArgs == null) {
return;
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index a5a8a92..658fc58 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -37,7 +37,6 @@
@SuppressWarnings("deprecation")
public class SQLiteStatement extends SQLiteProgram
{
-
private static final String TAG = "SQLiteStatement";
private static final boolean READ = true;
@@ -45,9 +44,9 @@
private SQLiteDatabase mOrigDb;
private int mState;
- /** possible value for {@link #mState}. indicates that a transaction is started.} */
+ /** possible value for {@link #mState}. indicates that a transaction is started. */
private static final int TRANS_STARTED = 1;
- /** possible value for {@link #mState}. indicates that a lock is acquired.} */
+ /** possible value for {@link #mState}. indicates that a lock is acquired. */
private static final int LOCK_ACQUIRED = 2;
/**
@@ -81,8 +80,8 @@
*/
public int executeUpdateDelete() {
synchronized(this) {
- long timeStart = acquireAndLock(WRITE);
try {
+ long timeStart = acquireAndLock(WRITE);
int numChanges = native_execute();
mDatabase.logTimeStat(mSql, timeStart);
return numChanges;
@@ -103,8 +102,8 @@
*/
public long executeInsert() {
synchronized(this) {
- long timeStart = acquireAndLock(WRITE);
try {
+ long timeStart = acquireAndLock(WRITE);
long lastInsertedRowId = native_executeInsert();
mDatabase.logTimeStat(mSql, timeStart);
return lastInsertedRowId;
@@ -124,8 +123,8 @@
*/
public long simpleQueryForLong() {
synchronized(this) {
- long timeStart = acquireAndLock(READ);
try {
+ long timeStart = acquireAndLock(READ);
long retValue = native_1x1_long();
mDatabase.logTimeStat(mSql, timeStart);
return retValue;
@@ -145,8 +144,8 @@
*/
public String simpleQueryForString() {
synchronized(this) {
- long timeStart = acquireAndLock(READ);
try {
+ long timeStart = acquireAndLock(READ);
String retValue = native_1x1_string();
mDatabase.logTimeStat(mSql, timeStart);
return retValue;
@@ -166,8 +165,8 @@
*/
public ParcelFileDescriptor simpleQueryForBlobFileDescriptor() {
synchronized(this) {
- long timeStart = acquireAndLock(READ);
try {
+ long timeStart = acquireAndLock(READ);
ParcelFileDescriptor retValue = native_1x1_blob_ashmem();
mDatabase.logTimeStat(mSql, timeStart);
return retValue;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index f5b1e57..f182a7a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -404,6 +404,7 @@
public static final byte CMD_UPDATE = 0;
public static final byte CMD_START = 1;
+ public static final byte CMD_OVERFLOW = 2;
public byte cmd;
@@ -1703,6 +1704,8 @@
pw.print(" ");
if (rec.cmd == HistoryItem.CMD_START) {
pw.println(" START");
+ } else if (rec.cmd == HistoryItem.CMD_OVERFLOW) {
+ pw.println(" *OVERFLOW*");
} else {
if (rec.batteryLevel < 10) pw.print("00");
else if (rec.batteryLevel < 100) pw.print("0");
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 01cc408..0067e940 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -17,10 +17,13 @@
package android.os;
+import android.os.WorkSource;
+
/** @hide */
interface IPowerManager
{
- void acquireWakeLock(int flags, IBinder lock, String tag);
+ void acquireWakeLock(int flags, IBinder lock, String tag, in WorkSource ws);
+ void updateWakeLockWorkSource(IBinder lock, in WorkSource ws);
void goToSleep(long time);
void goToSleepWithReason(long time, int reason);
void releaseWakeLock(IBinder lock, int flags);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index f4ca8bc..3876a3e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -209,6 +209,7 @@
int mCount = 0;
boolean mRefCounted = true;
boolean mHeld = false;
+ WorkSource mWorkSource;
WakeLock(int flags, String tag)
{
@@ -247,7 +248,7 @@
synchronized (mToken) {
if (!mRefCounted || mCount++ == 0) {
try {
- mService.acquireWakeLock(mFlags, mToken, mTag);
+ mService.acquireWakeLock(mFlags, mToken, mTag, mWorkSource);
} catch (RemoteException e) {
}
mHeld = true;
@@ -313,6 +314,32 @@
}
}
+ public void setWorkSource(WorkSource ws) {
+ synchronized (mToken) {
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+ boolean changed = true;
+ if (ws == null) {
+ mWorkSource = null;
+ } else if (mWorkSource == null) {
+ changed = mWorkSource != null;
+ mWorkSource = new WorkSource(ws);
+ } else {
+ changed = mWorkSource.diff(ws);
+ if (changed) {
+ mWorkSource.set(ws);
+ }
+ }
+ if (changed && mHeld) {
+ try {
+ mService.updateWakeLockWorkSource(mToken, mWorkSource);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
public String toString() {
synchronized (mToken) {
return "WakeLock{"
diff --git a/core/java/android/os/WorkSource.aidl b/core/java/android/os/WorkSource.aidl
new file mode 100644
index 0000000..1e7fabc
--- /dev/null
+++ b/core/java/android/os/WorkSource.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+parcelable WorkSource;
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
new file mode 100644
index 0000000..bba1984
--- /dev/null
+++ b/core/java/android/os/WorkSource.java
@@ -0,0 +1,311 @@
+package android.os;
+
+/**
+ * Describes the source of some work that may be done by someone else.
+ * Currently the public representation of what a work source is is not
+ * defined; this is an opaque container.
+ */
+public class WorkSource implements Parcelable {
+ int mNum;
+ int[] mUids;
+
+ /**
+ * Internal statics to avoid object allocations in some operations.
+ * The WorkSource object itself is not thread safe, but we need to
+ * hold sTmpWorkSource lock while working with these statics.
+ */
+ static final WorkSource sTmpWorkSource = new WorkSource(0);
+ /**
+ * For returning newbie work from a modification operation.
+ */
+ static WorkSource sNewbWork;
+ /**
+ * For returning gone work form a modification operation.
+ */
+ static WorkSource sGoneWork;
+
+ /**
+ * Create an empty work source.
+ */
+ public WorkSource() {
+ mNum = 0;
+ }
+
+ /**
+ * Create a new WorkSource that is a copy of an existing one.
+ * If <var>orig</var> is null, an empty WorkSource is created.
+ */
+ public WorkSource(WorkSource orig) {
+ if (orig == null) {
+ mNum = 0;
+ return;
+ }
+ mNum = orig.mNum;
+ if (orig.mUids != null) {
+ mUids = orig.mUids.clone();
+ } else {
+ mUids = null;
+ }
+ }
+
+ /** @hide */
+ public WorkSource(int uid) {
+ mNum = 1;
+ mUids = new int[] { uid, 0 };
+ }
+
+ WorkSource(Parcel in) {
+ mNum = in.readInt();
+ mUids = in.createIntArray();
+ }
+
+ /** @hide */
+ public int size() {
+ return mNum;
+ }
+
+ /** @hide */
+ public int get(int index) {
+ return mUids[index];
+ }
+
+ /**
+ * Clear this WorkSource to be empty.
+ */
+ public void clear() {
+ mNum = 0;
+ }
+
+ /**
+ * Compare this WorkSource with another.
+ * @param other The WorkSource to compare against.
+ * @return If there is a difference, true is returned.
+ */
+ public boolean diff(WorkSource other) {
+ int N = mNum;
+ if (N != other.mNum) {
+ return true;
+ }
+ final int[] uids1 = mUids;
+ final int[] uids2 = other.mUids;
+ for (int i=0; i<N; i++) {
+ if (uids1[i] != uids2[i]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Replace the current contents of this work source with the given
+ * work source. If <var>other</var> is null, the current work source
+ * will be made empty.
+ */
+ public void set(WorkSource other) {
+ if (other == null) {
+ mNum = 0;
+ return;
+ }
+ mNum = other.mNum;
+ if (other.mUids != null) {
+ if (mUids != null && mUids.length >= mNum) {
+ System.arraycopy(other.mUids, 0, mUids, 0, mNum);
+ } else {
+ mUids = other.mUids.clone();
+ }
+ } else {
+ mUids = null;
+ }
+ }
+
+ /** @hide */
+ public void set(int uid) {
+ mNum = 1;
+ if (mUids == null) mUids = new int[2];
+ mUids[0] = uid;
+ }
+
+ /** @hide */
+ public WorkSource[] setReturningDiffs(WorkSource other) {
+ synchronized (sTmpWorkSource) {
+ sNewbWork = null;
+ sGoneWork = null;
+ updateLocked(other, true, true);
+ if (sNewbWork != null || sGoneWork != null) {
+ WorkSource[] diffs = new WorkSource[2];
+ diffs[0] = sNewbWork;
+ diffs[1] = sGoneWork;
+ return diffs;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Merge the contents of <var>other</var> WorkSource in to this one.
+ *
+ * @param other The other WorkSource whose contents are to be merged.
+ * @return Returns true if any new sources were added.
+ */
+ public boolean add(WorkSource other) {
+ synchronized (sTmpWorkSource) {
+ return updateLocked(other, false, false);
+ }
+ }
+
+ /** @hide */
+ public WorkSource addReturningNewbs(WorkSource other) {
+ synchronized (sTmpWorkSource) {
+ sNewbWork = null;
+ updateLocked(other, false, true);
+ return sNewbWork;
+ }
+ }
+
+ /** @hide */
+ public boolean add(int uid) {
+ synchronized (sTmpWorkSource) {
+ sTmpWorkSource.mUids[0] = uid;
+ return updateLocked(sTmpWorkSource, false, false);
+ }
+ }
+
+ /** @hide */
+ public WorkSource addReturningNewbs(int uid) {
+ synchronized (sTmpWorkSource) {
+ sNewbWork = null;
+ sTmpWorkSource.mUids[0] = uid;
+ updateLocked(sTmpWorkSource, false, true);
+ return sNewbWork;
+ }
+ }
+
+ public boolean remove(WorkSource other) {
+ int N1 = mNum;
+ final int[] uids1 = mUids;
+ final int N2 = other.mNum;
+ final int[] uids2 = other.mUids;
+ boolean changed = false;
+ int i1 = 0;
+ for (int i2=0; i2<N2 && i1<N1; i2++) {
+ if (uids2[i2] == uids1[i1]) {
+ N1--;
+ if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1-1, N1-i1);
+ }
+ while (i1 < N1 && uids2[i2] > uids1[i1]) {
+ i1++;
+ }
+ }
+
+ mNum = N1;
+
+ return changed;
+ }
+
+ private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) {
+ int N1 = mNum;
+ int[] uids1 = mUids;
+ final int N2 = other.mNum;
+ final int[] uids2 = other.mUids;
+ boolean changed = false;
+ int i1 = 0;
+ for (int i2=0; i2<N2; i2++) {
+ if (i1 >= N1 || uids2[i2] < uids1[i1]) {
+ // Need to insert a new uid.
+ changed = true;
+ if (uids1 == null) {
+ uids1 = new int[4];
+ uids1[0] = uids2[i2];
+ } else if (i1 >= uids1.length) {
+ int[] newuids = new int[(uids1.length*3)/2];
+ if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1);
+ if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1);
+ uids1 = newuids;
+ uids1[i1] = uids2[i2];
+ } else {
+ if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1+1, N1-i1);
+ uids1[i1] = uids2[i2];
+ }
+ if (returnNewbs) {
+ if (sNewbWork == null) {
+ sNewbWork = new WorkSource(uids2[i2]);
+ } else {
+ sNewbWork.addLocked(uids2[i2]);
+ }
+ }
+ N1++;
+ i1++;
+ } else {
+ if (!set) {
+ // Skip uids that already exist or are not in 'other'.
+ do {
+ i1++;
+ } while (i1 < N1 && uids2[i2] >= uids1[i1]);
+ } else {
+ // Remove any uids that don't exist in 'other'.
+ int start = i1;
+ while (i1 < N1 && uids2[i2] > uids1[i1]) {
+ if (sGoneWork == null) {
+ sGoneWork = new WorkSource(uids1[i1]);
+ } else {
+ sGoneWork.addLocked(uids1[i1]);
+ }
+ i1++;
+ }
+ if (start < i1) {
+ System.arraycopy(uids1, i1, uids1, start, i1-start);
+ N1 -= i1-start;
+ i1 = start;
+ }
+ // If there is a matching uid, skip it.
+ if (i1 < N1 && uids2[i1] == uids1[i1]) {
+ i1++;
+ }
+ }
+ }
+ }
+
+ mNum = N1;
+ mUids = uids1;
+
+ return changed;
+ }
+
+ private void addLocked(int uid) {
+ if (mUids == null) {
+ mUids = new int[4];
+ mUids[0] = uid;
+ mNum = 1;
+ return;
+ }
+ if (mNum >= mUids.length) {
+ int[] newuids = new int[(mNum*3)/2];
+ System.arraycopy(mUids, 0, newuids, 0, mNum);
+ mUids = newuids;
+ }
+
+ mUids[mNum] = uid;
+ mNum++;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mNum);
+ dest.writeIntArray(mUids);
+ }
+
+ public static final Parcelable.Creator<WorkSource> CREATOR
+ = new Parcelable.Creator<WorkSource>() {
+ public WorkSource createFromParcel(Parcel in) {
+ return new WorkSource(in);
+ }
+ public WorkSource[] newArray(int size) {
+ return new WorkSource[size];
+ }
+ };
+}
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 2efb09d..60d810e 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -29,10 +29,11 @@
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Xml;
@@ -40,6 +41,7 @@
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
+import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.FrameLayout;
@@ -112,7 +114,11 @@
PreferenceFragment.OnPreferenceStartFragmentCallback {
private static final String TAG = "PreferenceActivity";
- private static final String PREFERENCES_TAG = "android:preferences";
+ // Constants for state save/restore
+ private static final String HEADERS_TAG = ":android:headers";
+ private static final String CUR_HEADER_TAG = ":android:cur_header";
+ private static final String SINGLE_PANE_TAG = ":android:single_pane";
+ private static final String PREFERENCES_TAG = ":android:preferences";
/**
* When starting this activity, the invoking Intent can contain this extra
@@ -165,6 +171,8 @@
private boolean mSinglePane;
+ private Header mCurHeader;
+
// --- State for old mode when showing a single preference list
private PreferenceManager mPreferenceManager;
@@ -186,17 +194,29 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_BIND_PREFERENCES:
+ case MSG_BIND_PREFERENCES: {
bindPreferences();
- break;
- case MSG_BUILD_HEADERS:
+ } break;
+ case MSG_BUILD_HEADERS: {
+ ArrayList<Header> oldHeaders = new ArrayList<Header>(mHeaders);
+ mHeaders.clear();
onBuildHeaders(mHeaders);
mAdapter.notifyDataSetChanged();
Header header = onGetNewHeader();
if (header != null && header.fragment != null) {
- switchToHeader(header.fragment, header.fragmentArguments);
+ Header mappedHeader = findBestMatchingHeader(header, oldHeaders);
+ if (mappedHeader == null || mCurHeader != mappedHeader) {
+ switchToHeader(header);
+ }
+ } else if (mCurHeader != null) {
+ Header mappedHeader = findBestMatchingHeader(mCurHeader, mHeaders);
+ if (mappedHeader != null) {
+ setSelectedHeader(mappedHeader);
+ } else {
+ switchToHeader(null);
+ }
}
- break;
+ } break;
}
}
};
@@ -235,13 +255,7 @@
// All view fields must be updated every time, because the view may be recycled
Header header = getItem(position);
- if (header.icon == null) {
- holder.icon.setImageDrawable(null);
- holder.icon.setImageResource(header.iconRes);
- } else {
- holder.icon.setImageResource(0);
- holder.icon.setImageDrawable(header.icon);
- }
+ holder.icon.setImageResource(header.iconRes);
holder.title.setText(header.title);
if (TextUtils.isEmpty(header.summary)) {
holder.summary.setVisibility(View.GONE);
@@ -255,9 +269,24 @@
}
/**
+ * Default value for {@link Header#id Header.id} indicating that no
+ * identifier value is set. All other values (including those below -1)
+ * are valid.
+ */
+ public static final long HEADER_ID_UNDEFINED = -1;
+
+ /**
* Description of a single Header item that the user can select.
*/
- public static class Header {
+ public static final class Header implements Parcelable {
+ /**
+ * Identifier for this header, to correlate with a new list when
+ * it is updated. The default value is
+ * {@link PreferenceActivity#HEADER_ID_UNDEFINED}, meaning no id.
+ * @attr ref android.R.styleable#PreferenceHeader_id
+ */
+ public long id = HEADER_ID_UNDEFINED;
+
/**
* Title of the header that is shown to the user.
* @attr ref android.R.styleable#PreferenceHeader_title
@@ -277,12 +306,6 @@
public int iconRes;
/**
- * Optional icon drawable to show for this header. (If this is non-null,
- * the iconRes will be ignored.)
- */
- public Drawable icon;
-
- /**
* Full class name of the fragment to display when this header is
* selected.
* @attr ref android.R.styleable#PreferenceHeader_fragment
@@ -299,6 +322,62 @@
* Intent to launch when the preference is selected.
*/
public Intent intent;
+
+ /**
+ * Optional additional data for use by subclasses of PreferenceActivity.
+ */
+ public Bundle extras;
+
+ public Header() {
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(id);
+ TextUtils.writeToParcel(title, dest, flags);
+ TextUtils.writeToParcel(summary, dest, flags);
+ dest.writeInt(iconRes);
+ dest.writeString(fragment);
+ dest.writeBundle(fragmentArguments);
+ if (intent != null) {
+ dest.writeInt(1);
+ intent.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeBundle(extras);
+ }
+
+ public void readFromParcel(Parcel in) {
+ id = in.readLong();
+ title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ iconRes = in.readInt();
+ fragment = in.readString();
+ fragmentArguments = in.readBundle();
+ if (in.readInt() != 0) {
+ intent = Intent.CREATOR.createFromParcel(in);
+ }
+ extras = in.readBundle();
+ }
+
+ Header(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public static final Creator<Header> CREATOR = new Creator<Header>() {
+ public Header createFromParcel(Parcel source) {
+ return new Header(source);
+ }
+ public Header[] newArray(int size) {
+ return new Header[size];
+ }
+ };
}
@Override
@@ -314,40 +393,66 @@
String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
- if (initialFragment != null && mSinglePane) {
- // If we are just showing a fragment, we want to run in
- // new fragment mode, but don't need to compute and show
- // the headers.
- getListView().setVisibility(View.GONE);
- mPrefsContainer.setVisibility(View.VISIBLE);
- switchToHeader(initialFragment, initialArguments);
+ if (savedInstanceState != null) {
+ // We are restarting from a previous saved state; used that to
+ // initialize, instead of starting fresh.
+ ArrayList<Header> headers = savedInstanceState.getParcelableArrayList(HEADERS_TAG);
+ if (headers != null) {
+ mHeaders.addAll(headers);
+ int curHeader = savedInstanceState.getInt(CUR_HEADER_TAG,
+ (int)HEADER_ID_UNDEFINED);
+ if (curHeader >= 0 && curHeader < mHeaders.size()) {
+ setSelectedHeader(mHeaders.get(curHeader));
+ }
+ }
+ mSinglePane = savedInstanceState.getBoolean(SINGLE_PANE_TAG);
} else {
- // We need to try to build the headers.
- onBuildHeaders(mHeaders);
+ if (initialFragment != null && mSinglePane) {
+ // If we are just showing a fragment, we want to run in
+ // new fragment mode, but don't need to compute and show
+ // the headers.
+ switchToHeader(initialFragment, initialArguments);
- // If there are headers, then at this point we need to show
- // them and, depending on the screen, we may also show in-line
- // the currently selected preference fragment.
- if (mHeaders.size() > 0) {
- mAdapter = new HeaderAdapter(this, mHeaders);
- setListAdapter(mAdapter);
- if (!mSinglePane) {
- mPrefsContainer.setVisibility(View.VISIBLE);
- if (initialFragment == null) {
- Header h = onGetInitialHeader();
- initialFragment = h.fragment;
- initialArguments = h.fragmentArguments;
+ } else {
+ // We need to try to build the headers.
+ onBuildHeaders(mHeaders);
+
+ // If there are headers, then at this point we need to show
+ // them and, depending on the screen, we may also show in-line
+ // the currently selected preference fragment.
+ if (mHeaders.size() > 0) {
+ if (!mSinglePane) {
+ if (initialFragment == null) {
+ Header h = onGetInitialHeader();
+ switchToHeader(h);
+ } else {
+ switchToHeader(initialFragment, initialArguments);
+ }
}
- switchToHeader(initialFragment, initialArguments);
}
+ }
+ }
+ // The default configuration is to only show the list view. Adjust
+ // visibility for other configurations.
+ if (initialFragment != null && mSinglePane) {
+ // Single pane, showing just a prefs fragment.
+ getListView().setVisibility(View.GONE);
+ mPrefsContainer.setVisibility(View.VISIBLE);
+ } else if (mHeaders.size() > 0) {
+ mAdapter = new HeaderAdapter(this, mHeaders);
+ setListAdapter(mAdapter);
+ if (!mSinglePane) {
+ // Multi-pane.
+ getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
+ mPrefsContainer.setVisibility(View.VISIBLE);
+ }
+ } else {
// If there are no headers, we are in the old "just show a screen
// of preferences" mode.
- } else {
- mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
- mPreferenceManager.setOnPreferenceTreeClickListener(this);
- }
+ mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
+ mPreferenceManager.setOnPreferenceTreeClickListener(this);
}
getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
@@ -534,6 +639,9 @@
TypedArray sa = getResources().obtainAttributes(attrs,
com.android.internal.R.styleable.PreferenceHeader);
+ header.id = sa.getInt(
+ com.android.internal.R.styleable.PreferenceHeader_id,
+ (int)HEADER_ID_UNDEFINED);
header.title = sa.getText(
com.android.internal.R.styleable.PreferenceHeader_title);
header.summary = sa.getText(
@@ -621,6 +729,17 @@
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
+ if (mHeaders.size() > 0) {
+ outState.putParcelableArrayList(HEADERS_TAG, mHeaders);
+ if (mCurHeader != null) {
+ int index = mHeaders.indexOf(mCurHeader);
+ if (index >= 0) {
+ outState.putInt(CUR_HEADER_TAG, index);
+ }
+ }
+ }
+ outState.putBoolean(SINGLE_PANE_TAG, mSinglePane);
+
if (mPreferenceManager != null) {
final PreferenceScreen preferenceScreen = getPreferenceScreen();
if (preferenceScreen != null) {
@@ -680,7 +799,7 @@
/**
* Called when the user selects an item in the header list. The default
* implementation will call either {@link #startWithFragment(String, Bundle)}
- * or {@link #switchToHeader(String, Bundle)} as appropriate.
+ * or {@link #switchToHeader(Header)} as appropriate.
*
* @param header The header that was selected.
* @param position The header's position in the list.
@@ -690,7 +809,7 @@
if (mSinglePane) {
startWithFragment(header.fragment, header.fragmentArguments);
} else {
- switchToHeader(header.fragment, header.fragmentArguments);
+ switchToHeader(header);
}
} else if (header.intent != null) {
startActivity(header.intent);
@@ -715,6 +834,16 @@
startActivity(intent);
}
+ void setSelectedHeader(Header header) {
+ mCurHeader = header;
+ int index = mHeaders.indexOf(header);
+ if (index >= 0) {
+ getListView().setItemChecked(index, true);
+ } else {
+ getListView().clearChoices();
+ }
+ }
+
/**
* When in two-pane mode, switch the fragment pane to show the given
* preference fragment.
@@ -723,6 +852,8 @@
* @param args Optional arguments to supply to the fragment.
*/
public void switchToHeader(String fragmentName, Bundle args) {
+ setSelectedHeader(null);
+
getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
Fragment f = Fragment.instantiate(this, fragmentName, args);
@@ -731,6 +862,63 @@
}
/**
+ * When in two-pane mode, switch to the fragment pane to show the given
+ * preference fragment.
+ *
+ * @param header The new header to display.
+ */
+ public void switchToHeader(Header header) {
+ switchToHeader(header.fragment, header.fragmentArguments);
+ mCurHeader = header;
+ setSelectedHeader(header);
+ }
+
+ Header findBestMatchingHeader(Header cur, ArrayList<Header> from) {
+ ArrayList<Header> matches = new ArrayList<Header>();
+ for (int j=0; j<from.size(); j++) {
+ Header oh = from.get(j);
+ if (cur == oh || (cur.id != HEADER_ID_UNDEFINED && cur.id == oh.id)) {
+ // Must be this one.
+ matches.clear();
+ matches.add(oh);
+ break;
+ }
+ if (cur.fragment != null) {
+ if (cur.fragment.equals(oh.fragment)) {
+ matches.add(oh);
+ }
+ } else if (cur.intent != null) {
+ if (cur.intent.equals(oh.intent)) {
+ matches.add(oh);
+ }
+ } else if (cur.title != null) {
+ if (cur.title.equals(oh.title)) {
+ matches.add(oh);
+ }
+ }
+ }
+ final int NM = matches.size();
+ if (NM == 1) {
+ return matches.get(0);
+ } else if (NM > 1) {
+ for (int j=0; j<NM; j++) {
+ Header oh = matches.get(j);
+ if (cur.fragmentArguments != null &&
+ cur.fragmentArguments.equals(oh.fragmentArguments)) {
+ return oh;
+ }
+ if (cur.extras != null && cur.extras.equals(oh.extras)) {
+ return oh;
+ }
+ if (cur.title != null && cur.title.equals(oh.title)) {
+ return oh;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
* Start a new fragment.
*
* @param fragment The fragment to start
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 3db05b6..012f3cc 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -877,7 +877,7 @@
mSplitMotionTargets = new SplitMotionTargets();
}
return dispatchSplitTouchEvent(ev);
- }
+ }
final int action = ev.getAction();
final float xf = ev.getX();
@@ -1222,13 +1222,7 @@
uniqueTargetCount--;
}
- final boolean childHandled = target.dispatchTouchEvent(targetEvent);
- handled |= childHandled;
- if (!childHandled) {
- // Child doesn't want these events anymore, but we're still dispatching
- // other split events to children.
- targets.removeView(target);
- }
+ handled |= target.dispatchTouchEvent(targetEvent);
} finally {
targetEvent.recycle();
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index d4acff8..9c59db3 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3788,7 +3788,7 @@
InputMethodManager imm = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
- // bring it back to the reading level scale so that user can enter text
+ // bring it back to the default level scale so that user can enter text
boolean zoom = mZoomManager.getScale() < mZoomManager.getDefaultScale();
if (zoom) {
mZoomManager.setZoomCenter(mLastTouchX, mLastTouchY);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index d87f55f..f3dfb77 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -16,11 +16,12 @@
package android.widget;
+import com.android.internal.R;
+
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
@@ -56,8 +57,6 @@
import android.view.inputmethod.InputConnectionWrapper;
import android.view.inputmethod.InputMethodManager;
-import com.android.internal.R;
-
import java.util.ArrayList;
import java.util.List;
@@ -3074,8 +3073,10 @@
// Estimate how many screens we should travel
final float screenTravelCount = (float) viewTravelCount / childCount;
- mScrollDuration = (int) (SCROLL_DURATION / screenTravelCount);
+ mScrollDuration = screenTravelCount < 1 ? (int) (screenTravelCount * SCROLL_DURATION) :
+ (int) (SCROLL_DURATION / screenTravelCount);
mLastSeenPos = INVALID_POSITION;
+
post(this);
}
@@ -3213,9 +3214,15 @@
}
case MOVE_OFFSET: {
- final int childCount = getChildCount();
+ if (mLastSeenPos == firstPos) {
+ // No new views, let things keep going.
+ post(this);
+ return;
+ }
mLastSeenPos = firstPos;
+
+ final int childCount = getChildCount();
final int position = mTargetPos;
final int lastPos = firstPos + childCount - 1;
@@ -3228,7 +3235,9 @@
} else {
// On-screen, just scroll.
final int targetTop = getChildAt(position - firstPos).getTop();
- smoothScrollBy(targetTop - mOffsetFromTop, mScrollDuration);
+ final int distance = targetTop - mOffsetFromTop;
+ smoothScrollBy(distance,
+ (int) (mScrollDuration * ((float) distance / getHeight())));
}
break;
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index fe1c9da..57dc725 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -57,6 +57,7 @@
private OnCloseListener mOnCloseListener;
private boolean mIconifiedByDefault;
+ private boolean mIconified;
private CursorAdapter mSuggestionsAdapter;
private View mSearchButton;
private View mSubmitButton;
@@ -139,8 +140,6 @@
mQueryTextView.setOnItemClickListener(mOnItemClickListener);
mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener);
- mSubmitButtonEnabled = false;
-
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SearchView, 0, 0);
setIconifiedByDefault(a.getBoolean(R.styleable.SearchView_iconifiedByDefault, true));
a.recycle();
@@ -175,6 +174,15 @@
}
/**
+ * Sets a listener to inform when the user closes the SearchView.
+ *
+ * @param listener the listener to call when the user closes the SearchView.
+ */
+ public void setOnCloseListener(OnCloseListener listener) {
+ mOnCloseListener = listener;
+ }
+
+ /**
* Sets a query string in the text field and optionally submits the query as well.
*
* @param query the query string. This replaces any query text already present in the
@@ -205,20 +213,54 @@
* Sets the default or resting state of the search field. If true, a single search icon is
* shown by default and expands to show the text field and other buttons when pressed. Also,
* if the default state is iconified, then it collapses to that state when the close button
- * is pressed.
+ * is pressed. Changes to this property will take effect immediately.
*
- * @param iconified
+ * <p>The default value is false.</p>
+ *
+ * @param iconified whether the search field should be iconified by default
*/
public void setIconifiedByDefault(boolean iconified) {
mIconifiedByDefault = iconified;
updateViewsVisibility(iconified);
}
+ /**
+ * Returns the default iconified state of the search field.
+ * @return
+ */
public boolean isIconfiedByDefault() {
return mIconifiedByDefault;
}
/**
+ * Iconifies or expands the SearchView. Any query text is cleared when iconified. This is
+ * a temporary state and does not override the default iconified state set by
+ * {@link #setIconifiedByDefault(boolean)}. If the default state is iconified, then
+ * a false here will only be valid until the user closes the field. And if the default
+ * state is expanded, then a true here will only clear the text field and not close it.
+ *
+ * @param iconify a true value will collapse the SearchView to an icon, while a false will
+ * expand it.
+ */
+ public void setIconified(boolean iconify) {
+ if (iconify) {
+ onCloseClicked();
+ } else {
+ onSearchClicked();
+ }
+ }
+
+ /**
+ * Returns the current iconified state of the SearchView.
+ *
+ * @return true if the SearchView is currently iconified, false if the search field is
+ * fully visible.
+ */
+ public boolean isIconified() {
+ return mIconified;
+ }
+
+ /**
* Enables showing a submit button when the query is non-empty. In cases where the SearchView
* is being used to filter the contents of the current activity and doesn't launch a separate
* results activity, then the submit button should be disabled.
@@ -263,11 +305,12 @@
return mSuggestionsAdapter;
}
- private void updateViewsVisibility(boolean collapsed) {
+ private void updateViewsVisibility(final boolean collapsed) {
+ mIconified = collapsed;
// Visibility of views that are visible when collapsed
- int visCollapsed = collapsed? VISIBLE : GONE;
+ final int visCollapsed = collapsed ? VISIBLE : GONE;
// Visibility of views that are visible when expanded
- int visExpanded = collapsed? GONE : VISIBLE;
+ final int visExpanded = collapsed ? GONE : VISIBLE;
mSearchButton.setVisibility(visCollapsed);
mSubmitButton.setVisibility(mSubmitButtonEnabled ? visExpanded : GONE);
@@ -322,7 +365,8 @@
// entered query with the action key
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
- launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView.getText().toString());
+ launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView.getText()
+ .toString());
return true;
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 1620778..bd87a0d 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -18,6 +18,7 @@
import com.android.internal.os.BatteryStatsImpl;
+import android.os.WorkSource;
import android.telephony.SignalStrength;
interface IBatteryStats {
@@ -33,6 +34,9 @@
SensorService.cpp */
void noteStopSensor(int uid, int sensor);
+ void noteStartWakelockFromSource(in WorkSource ws, int pid, String name, int type);
+ void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, int type);
+
void noteStartGps(int uid);
void noteStopGps(int uid);
void noteScreenOn();
@@ -57,6 +61,12 @@
void noteScanWifiLockReleased(int uid);
void noteWifiMulticastEnabled(int uid);
void noteWifiMulticastDisabled(int uid);
+ void noteFullWifiLockAcquiredFromSource(in WorkSource ws);
+ void noteFullWifiLockReleasedFromSource(in WorkSource ws);
+ void noteScanWifiLockAcquiredFromSource(in WorkSource ws);
+ void noteScanWifiLockReleasedFromSource(in WorkSource ws);
+ void noteWifiMulticastEnabledFromSource(in WorkSource ws);
+ void noteWifiMulticastDisabledFromSource(in WorkSource ws);
void setBatteryState(int status, int health, int plugType, int level, int temp, int volt);
long getAwakeTimeBattery();
long getAwakeTimePlugged();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b9f0c61..4943531 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -29,6 +29,7 @@
import android.os.Parcelable;
import android.os.Process;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
@@ -68,12 +69,12 @@
private static final int VERSION = 50;
// Maximum number of items we will record in the history.
- private static final int MAX_HISTORY_ITEMS = 1000;
+ private static final int MAX_HISTORY_ITEMS = 2000;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
// in to one common name.
- private static final int MAX_WAKELOCKS_PER_UID = 20;
+ private static final int MAX_WAKELOCKS_PER_UID = 30;
private static final String BATCHED_WAKELOCK_NAME = "*overflow*";
@@ -1173,7 +1174,7 @@
// If the current time is basically the same as the last time,
// just collapse into one record.
if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
- && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+100)) {
+ && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500)) {
// If the current is the same as the one before, then we no
// longer need the entry.
if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
@@ -1189,6 +1190,10 @@
return;
}
+ if (mNumHistoryItems == MAX_HISTORY_ITEMS) {
+ addHistoryRecordLocked(curTime, HistoryItem.CMD_OVERFLOW);
+ }
+
if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
// Once we've reached the maximum number of items, we only
// record changes to the battery level.
@@ -1329,6 +1334,20 @@
}
}
+ public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteStartWakeLocked(ws.get(i), pid, name, type);
+ }
+ }
+
+ public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteStopWakeLocked(ws.get(i), pid, name, type);
+ }
+ }
+
public int startAddingCpuLocked() {
mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
@@ -1949,6 +1968,48 @@
getUidStatsLocked(uid).noteWifiMulticastDisabledLocked();
}
+ public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteFullWifiLockAcquiredLocked(ws.get(i));
+ }
+ }
+
+ public void noteFullWifiLockReleasedFromSourceLocked(WorkSource ws) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteFullWifiLockReleasedLocked(ws.get(i));
+ }
+ }
+
+ public void noteScanWifiLockAcquiredFromSourceLocked(WorkSource ws) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteScanWifiLockAcquiredLocked(ws.get(i));
+ }
+ }
+
+ public void noteScanWifiLockReleasedFromSourceLocked(WorkSource ws) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteScanWifiLockReleasedLocked(ws.get(i));
+ }
+ }
+
+ public void noteWifiMulticastEnabledFromSourceLocked(WorkSource ws) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteWifiMulticastEnabledLocked(ws.get(i));
+ }
+ }
+
+ public void noteWifiMulticastDisabledFromSourceLocked(WorkSource ws) {
+ int N = ws.size();
+ for (int i=0; i<N; i++) {
+ noteWifiMulticastDisabledLocked(ws.get(i));
+ }
+ }
+
@Override public long getScreenOnTime(long batteryRealtime, int which) {
return mScreenOnTimer.getTotalTimeLocked(batteryRealtime, which);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 89298ea..4d02b60 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -939,7 +939,7 @@
<permission android:name="android.permission.UPDATE_DEVICE_STATS"
android:label="@string/permlab_batteryStats"
android:description="@string/permdesc_batteryStats"
- android:protectionLevel="signature" />
+ android:protectionLevel="signatureOrSystem" />
<!-- Allows an application to open windows that are for use by parts
of the system user interface. Not for use by third party apps. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9363db5..cda997a 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3530,6 +3530,8 @@
<!-- Attribute for a header describing the item shown in the top-level list
from which the selects the set of preference to dig in to. -->
<declare-styleable name="PreferenceHeader">
+ <!-- Identifier value for the header. -->
+ <attr name="id" />
<!-- The title of the item that is shown to the user. -->
<attr name="title" />
<!-- The summary for the item. -->
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCacheTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCacheTest.java
deleted file mode 100644
index 2459815..0000000
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteCacheTest.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.database.sqlite;
-
-
-import android.content.Context;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteDatabaseTest.ClassToTestSqlCompilationAndCaching;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.io.File;
-import java.util.ArrayList;
-
-public class SQLiteCacheTest extends AndroidTestCase {
- private SQLiteDatabase mDatabase;
- private File mDatabaseFile;
- private static final String TABLE_NAME = "testCache";
- private SQLiteCache mCache;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
- mDatabaseFile = new File(dbDir, "test.db");
- if (mDatabaseFile.exists()) {
- mDatabaseFile.delete();
- }
- mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
- assertNotNull(mDatabase);
- mCache = mDatabase.mCache;
- assertNotNull(mCache);
-
- // create a test table
- mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
- }
-
- @Override
- protected void tearDown() throws Exception {
- mDatabase.lock();
- // flush the above statement from cache and close all the pending statements to be released
- mCache.dealloc();
- mDatabase.closePendingStatements();
- mDatabase.unlock();
- assertEquals(0, mDatabase.getQueuedUpStmtList().size());
- mDatabase.close();
- mDatabaseFile.delete();
- super.tearDown();
- }
-
- /**
- * do N+1 queries - and when the 0th entry is removed from LRU cache due to the
- * insertion of (N+1)th entry, make sure 0th entry is closed
- */
- @SmallTest
- public void testLruCaching() {
- mDatabase.disableWriteAheadLogging();
- // set cache size
- int N = 25;
- mDatabase.setMaxSqlCacheSize(N);
-
- ArrayList<Integer> stmtObjs = new ArrayList<Integer>();
- ArrayList<String> sqlStrings = new ArrayList<String>();
- int stmt0 = 0;
- for (int i = 0; i < N+1; i++) {
- String s = "insert into " + TABLE_NAME + " values(" + i + ",?);";
- sqlStrings.add(s);
- ClassToTestSqlCompilationAndCaching c =
- ClassToTestSqlCompilationAndCaching.create(mDatabase, s);
- int n = c.getSqlStatementId();
- stmtObjs.add(i, n);
- if (i == 0) {
- // save the statementId of this obj. we want to make sure it is thrown out of
- // the cache at the end of this test.
- stmt0 = n;
- }
- c.close();
- }
- assertEquals(N, mCache.getCachesize());
- // is 0'th entry out of the cache? it should be in the list of statementIds
- // corresponding to the pre-compiled sql statements to be finalized.
- assertTrue(mDatabase.getQueuedUpStmtList().contains(stmt0));
- for (int i = 1; i < N+1; i++) {
- SQLiteCompiledSql compSql =
- mDatabase.mCache.getCompiledStatementForSql(sqlStrings.get(i));
- assertNotNull(compSql);
- assertTrue(stmtObjs.contains(compSql.nStatement));
- }
- assertEquals(N, mCache.getCachesize());
-
- }
-
- /**
- * Cache should only have Select / Insert / Update / Delete / Replace.
- */
- @SmallTest
- public void testCachingOfCRUDstatementsOnly() {
- ClassToTestSqlCompilationAndCaching c;
- // do some CRUD sql
- int crudSqlNum = 7 * 4;
- mDatabase.setMaxSqlCacheSize(crudSqlNum);
- for (int i = 0; i < crudSqlNum / 4; i++) {
- c= ClassToTestSqlCompilationAndCaching.create(mDatabase,
- "insert into " + TABLE_NAME + " values(" + i + ",?);");
- c.close();
- c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
- "update " + TABLE_NAME + " set i = " + i);
- c.close();
- c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
- "select * from " + TABLE_NAME + " where i = " + i);
- c.close();
- c= ClassToTestSqlCompilationAndCaching.create(mDatabase,
- "delete from " + TABLE_NAME + " where i = " + i);
- c.close();
- }
- // do some non-CRUD sql
- c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
- "create table j (i int);");
- c.close();
- c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
- "pragma database_list;");
- c.close();
- c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
- "begin transaction;");
- c.close();
- c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
- "commit;");
- c.close();
- c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
- "attach database \"blah\" as blah_db;");
- c.close();
- // cache size should be crudSqlNum
- assertEquals(crudSqlNum, mCache.getCachesize());
- // everything in the cache should be a CRUD sql statement
- for (String s : mCache.getKeys()) {
- int type = DatabaseUtils.getSqlStatementType(s);
- assertTrue(type == DatabaseUtils.STATEMENT_SELECT ||
- type == DatabaseUtils.STATEMENT_UPDATE);
- }
- }
-
- /**
- * calling SQLiteCache.getCompiledStatementForSql() should reserve the cached-entry
- * for the caller, if the entry exists
- */
- @SmallTest
- public void testGetShouldReserveEntry() {
- String sql = "insert into " + TABLE_NAME + " values(1,?);";
- ClassToTestSqlCompilationAndCaching c =
- ClassToTestSqlCompilationAndCaching.create(mDatabase, sql);
- c.close();
- SQLiteCompiledSql compiledSql = mCache.getCompiledStatementForSql(sql);
- assertNotNull(compiledSql);
- assertTrue(compiledSql.isInUse());
- // get entry for the same sql again. should get null and a warning in log
- assertNull(mCache.getCompiledStatementForSql(sql));
- compiledSql.free();
- assertFalse(compiledSql.isInUse());
- }
-}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCompiledSqlTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCompiledSqlTest.java
deleted file mode 100644
index 1606cf6..0000000
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteCompiledSqlTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.database.sqlite;
-
-
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteDatabaseTest.ClassToTestSqlCompilationAndCaching;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.io.File;
-
-public class SQLiteCompiledSqlTest extends AndroidTestCase {
- private SQLiteDatabase mDatabase;
- private File mDatabaseFile;
- private static final String TABLE_NAME = "testCache";
- private SQLiteCache mCache;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
- mDatabaseFile = new File(dbDir, "sqlitecursor_test.db");
- if (mDatabaseFile.exists()) {
- mDatabaseFile.delete();
- }
- mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
- assertNotNull(mDatabase);
- mCache = mDatabase.mCache;
- assertNotNull(mCache);
-
- // create a test table
- mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
- }
-
- @Override
- protected void tearDown() throws Exception {
- mDatabase.close();
- mDatabaseFile.delete();
- super.tearDown();
- }
-
- /**
- * releaseIfNotInUse() should release only if it is not in use
- */
- @SmallTest
- public void testReleaseIfNotInUse() {
- ClassToTestSqlCompilationAndCaching c;
- // do some CRUD sql
- int crudSqlNum = 20 * 4;
- mDatabase.setMaxSqlCacheSize(crudSqlNum);
- for (int i = 0; i < crudSqlNum / 4; i++) {
- c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
- "insert into " + TABLE_NAME + " values(" + i + ",?);");
- c.close();
- c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
- "update " + TABLE_NAME + " set i = " + i);
- c.close();
- c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
- "select * from " + TABLE_NAME + " where i = " + i);
- c.close();
- c = ClassToTestSqlCompilationAndCaching.create(mDatabase,
- "delete from " + TABLE_NAME + " where i = " + i);
- c.close();
- }
- assertEquals(crudSqlNum, mCache.getCachesize());
- String sql = "insert into " + TABLE_NAME + " values(1,?);";
- SQLiteCompiledSql compiledSql = mCache.getCompiledStatementForSql(sql);
- assertNotNull(compiledSql);
- assertTrue(compiledSql.isInUse());
- // the object is in use. try to release it
- compiledSql.releaseIfNotInUse();
- // compiledSql should not be released yet
- int stmtId = compiledSql.nStatement;
- assertTrue(stmtId > 0);
- // free the object and call releaseIfNotInUse() again - and it should work this time
- compiledSql.free();
- assertFalse(compiledSql.isInUse());
- compiledSql.releaseIfNotInUse();
- assertEquals(0, compiledSql.nStatement);
- assertTrue(mDatabase.getQueuedUpStmtList().contains(stmtId));
- }
-}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index 3249cb6..86eda71 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -447,6 +447,42 @@
}
}
+ @SmallTest
+ public void testLruCachingOfSqliteCompiledSqlObjs() {
+ createTableAndClearCache();
+ // set cache size
+ int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
+ mDatabase.setMaxSqlCacheSize(N);
+
+ // do N+1 queries - and when the 0th entry is removed from LRU cache due to the
+ // insertion of (N+1)th entry, make sure 0th entry is closed
+ ArrayList<Integer> stmtObjs = new ArrayList<Integer>();
+ ArrayList<String> sqlStrings = new ArrayList<String>();
+ int stmt0 = 0;
+ for (int i = 0; i < N+1; i++) {
+ String s = "insert into test values(" + i + ",?);";
+ sqlStrings.add(s);
+ ClassToTestSqlCompilationAndCaching c =
+ ClassToTestSqlCompilationAndCaching.create(mDatabase, s);
+ int n = c.getSqlStatementId();
+ stmtObjs.add(i, n);
+ if (i == 0) {
+ // save the statementId of this obj. we want to make sure it is thrown out of
+ // the cache at the end of this test.
+ stmt0 = n;
+ }
+ c.close();
+ }
+ // is 0'th entry out of the cache? it should be in the list of statementIds
+ // corresponding to the pre-compiled sql statements to be finalized.
+ assertTrue(mDatabase.getQueuedUpStmtList().contains(stmt0));
+ for (int i = 1; i < N+1; i++) {
+ SQLiteCompiledSql compSql = mDatabase.getCompiledStatementForSql(sqlStrings.get(i));
+ assertNotNull(compSql);
+ assertTrue(stmtObjs.contains(compSql.nStatement));
+ }
+ }
+
@MediumTest
public void testDbCloseReleasingAllCachedSql() {
mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, text1 TEXT, text2 TEXT, " +
@@ -467,7 +503,7 @@
mDatabase.enableWriteAheadLogging();
mDatabase.lock();
// flush the above statement from cache and close all the pending statements to be released
- mDatabase.mCache.dealloc();
+ mDatabase.deallocCachedSqlStatements();
mDatabase.closePendingStatements();
mDatabase.unlock();
assertEquals(0, mDatabase.getQueuedUpStmtList().size());
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java
index bdd6d5c..217545f 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java
@@ -53,12 +53,6 @@
* Start 2 threads to repeatedly execute the above SQL statement.
* Even though 2 threads are executing the same SQL, they each should get their own copy of
* prepared SQL statement id and there SHOULD NOT be an error from sqlite or android.
- *<p>
- * This method will produce a lot of the following warnings:
- * Possible bug: Either using the same SQL in 2 threads at the same time, or
- * previous instance of this SQL statement was never close()d.
- * That is expected behavior.
- *
* @throws InterruptedException thrown if the test threads started by this test are interrupted
*/
@LargeTest
diff --git a/graphics/java/android/renderscript/ProgramFragment.java b/graphics/java/android/renderscript/ProgramFragment.java
index 04091a3..00c5cf1 100644
--- a/graphics/java/android/renderscript/ProgramFragment.java
+++ b/graphics/java/android/renderscript/ProgramFragment.java
@@ -62,9 +62,9 @@
}
}
- public static class Builder {
+ public static class Builder extends ShaderBuilder {
public static final int MAX_TEXTURE = 2;
- RenderScript mRS;
+ int mNumTextures;
boolean mPointSpriteEnable;
boolean mVaryingColorEnable;
@@ -101,7 +101,72 @@
}
Slot[] mSlots;
+ private void buildShaderString() {
+ mShader = "//rs_shader_internal\n";
+ mShader += "varying lowp vec4 varColor;\n";
+ mShader += "varying vec4 varTex0;\n";
+
+ mShader += "void main() {\n";
+ if (mVaryingColorEnable) {
+ mShader += " lowp vec4 col = varColor;\n";
+ } else {
+ mShader += " lowp vec4 col = UNI_Color;\n";
+ }
+
+ if (mNumTextures != 0) {
+ if (mPointSpriteEnable) {
+ mShader += " vec2 t0 = gl_PointCoord;\n";
+ } else {
+ mShader += " vec2 t0 = varTex0.xy;\n";
+ }
+ }
+
+ for(int i = 0; i < mNumTextures; i ++) {
+ switch(mSlots[i].env) {
+ case REPLACE:
+ switch (mSlots[i].format) {
+ case ALPHA:
+ mShader += " col.a = texture2D(UNI_Tex0, t0).a;\n";
+ break;
+ case LUMINANCE_ALPHA:
+ mShader += " col.rgba = texture2D(UNI_Tex0, t0).rgba;\n";
+ break;
+ case RGB:
+ mShader += " col.rgb = texture2D(UNI_Tex0, t0).rgb;\n";
+ break;
+ case RGBA:
+ mShader += " col.rgba = texture2D(UNI_Tex0, t0).rgba;\n";
+ break;
+ }
+ break;
+ case MODULATE:
+ switch (mSlots[i].format) {
+ case ALPHA:
+ mShader += " col.a *= texture2D(UNI_Tex0, t0).a;\n";
+ break;
+ case LUMINANCE_ALPHA:
+ mShader += " col.rgba *= texture2D(UNI_Tex0, t0).rgba;\n";
+ break;
+ case RGB:
+ mShader += " col.rgb *= texture2D(UNI_Tex0, t0).rgb;\n";
+ break;
+ case RGBA:
+ mShader += " col.rgba *= texture2D(UNI_Tex0, t0).rgba;\n";
+ break;
+ }
+ break;
+ case DECAL:
+ mShader += " col = texture2D(UNI_Tex0, t0);\n";
+ break;
+ }
+ }
+
+ mShader += " gl_FragColor = col;\n";
+ mShader += "}\n";
+ }
+
public Builder(RenderScript rs) {
+ super(rs);
mRS = rs;
mSlots = new Slot[MAX_TEXTURE];
mPointSpriteEnable = false;
@@ -126,22 +191,35 @@
return this;
}
+ @Override
public ProgramFragment create() {
- mRS.validate();
- int[] tmp = new int[MAX_TEXTURE * 2 + 2];
- if (mSlots[0] != null) {
- tmp[0] = mSlots[0].env.mID;
- tmp[1] = mSlots[0].format.mID;
+ mNumTextures = 0;
+ for(int i = 0; i < MAX_TEXTURE; i ++) {
+ if(mSlots[i] != null) {
+ mNumTextures ++;
+ }
}
- if (mSlots[1] != null) {
- tmp[2] = mSlots[1].env.mID;
- tmp[3] = mSlots[1].format.mID;
+ buildShaderString();
+ Type constType = null;
+ if (!mVaryingColorEnable) {
+ Element.Builder b = new Element.Builder(mRS);
+ b.add(Element.F32_4(mRS), "Color");
+ Type.Builder typeBuilder = new Type.Builder(mRS, b.create());
+ typeBuilder.add(Dimension.X, 1);
+ constType = typeBuilder.create();
+ addConstant(constType);
}
- tmp[4] = mPointSpriteEnable ? 1 : 0;
- tmp[5] = mVaryingColorEnable ? 1 : 0;
- int id = mRS.nProgramFragmentCreate(tmp);
- ProgramFragment pf = new ProgramFragment(id, mRS);
+ setTextureCount(mNumTextures);
+
+ ProgramFragment pf = super.create();
pf.mTextureCount = MAX_TEXTURE;
+ if (!mVaryingColorEnable) {
+ Allocation constantData = Allocation.createTyped(mRS,constType);
+ float[] data = new float[4];
+ data[0] = data[1] = data[2] = data[3] = 1.0f;
+ constantData.data(data);
+ pf.bindConstants(constantData, 0);
+ }
return pf;
}
}
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
index b072433..119db69 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -17,6 +17,7 @@
package android.renderscript;
+import android.graphics.Matrix;
import android.util.Config;
import android.util.Log;
@@ -38,29 +39,6 @@
bindConstants(va.mAlloc, 0);
}
-
- public static class Builder {
- RenderScript mRS;
- boolean mTextureMatrixEnable;
-
- public Builder(RenderScript rs, Element in, Element out) {
- mRS = rs;
- }
- public Builder(RenderScript rs) {
- mRS = rs;
- }
-
- public Builder setTextureMatrixEnable(boolean enable) {
- mTextureMatrixEnable = enable;
- return this;
- }
-
- public ProgramVertex create() {
- int id = mRS.nProgramVertexCreate(mTextureMatrixEnable);
- return new ProgramVertex(id, mRS);
- }
- }
-
public static class ShaderBuilder extends BaseProgramBuilder {
public ShaderBuilder(RenderScript rs) {
super(rs);
@@ -93,6 +71,68 @@
}
}
+ public static class Builder extends ShaderBuilder {
+ boolean mTextureMatrixEnable;
+
+ public Builder(RenderScript rs, Element in, Element out) {
+ super(rs);
+ }
+ public Builder(RenderScript rs) {
+ super(rs);
+ }
+
+ public Builder setTextureMatrixEnable(boolean enable) {
+ mTextureMatrixEnable = enable;
+ return this;
+ }
+ static Type getConstantInputType(RenderScript rs) {
+ Element.Builder b = new Element.Builder(rs);
+ b.add(Element.MATRIX4X4(rs), "MV");
+ b.add(Element.MATRIX4X4(rs), "P");
+ b.add(Element.MATRIX4X4(rs), "TexMatrix");
+ b.add(Element.MATRIX4X4(rs), "MVP");
+
+ Type.Builder typeBuilder = new Type.Builder(rs, b.create());
+ typeBuilder.add(Dimension.X, 1);
+ return typeBuilder.create();
+ }
+
+ private void buildShaderString() {
+
+ mShader = "//rs_shader_internal\n";
+ mShader += "varying vec4 varColor;\n";
+ mShader += "varying vec4 varTex0;\n";
+
+ mShader += "void main() {\n";
+ mShader += " gl_Position = UNI_MVP * ATTRIB_position;\n";
+ mShader += " gl_PointSize = 1.0;\n";
+
+ mShader += " varColor = ATTRIB_color;\n";
+ if (mTextureMatrixEnable) {
+ mShader += " varTex0 = UNI_TexMatrix * ATTRIB_texture0;\n";
+ } else {
+ mShader += " varTex0 = ATTRIB_texture0;\n";
+ }
+ mShader += "}\n";
+ }
+
+ @Override
+ public ProgramVertex create() {
+ buildShaderString();
+
+ addConstant(getConstantInputType(mRS));
+
+ Element.Builder b = new Element.Builder(mRS);
+ b.add(Element.F32_4(mRS), "position");
+ b.add(Element.F32_4(mRS), "color");
+ b.add(Element.F32_3(mRS), "normal");
+ b.add(Element.F32_4(mRS), "texture0");
+ addInput(b.create());
+
+ return super.create();
+ }
+ }
+
public static class MatrixAllocation {
@@ -105,9 +145,14 @@
Matrix4f mTexture;
public Allocation mAlloc;
+ private FieldPacker mIOBuffer;
public MatrixAllocation(RenderScript rs) {
- mAlloc = Allocation.createSized(rs, Element.createUser(rs, Element.DataType.FLOAT_32), 48);
+ Type constInputType = ProgramVertex.Builder.getConstantInputType(rs);
+ mAlloc = Allocation.createTyped(rs, constInputType);
+ int bufferSize = constInputType.getElement().getSizeBytes()*
+ constInputType.getElementCount();
+ mIOBuffer = new FieldPacker(bufferSize);
loadModelview(new Matrix4f());
loadProjection(new Matrix4f());
loadTexture(new Matrix4f());
@@ -118,24 +163,32 @@
mAlloc = null;
}
+ private void addToBuffer(int offset, Matrix4f m) {
+ mIOBuffer.reset(offset);
+ for(int i = 0; i < 16; i ++) {
+ mIOBuffer.addF32(m.mMat[i]);
+ }
+ mAlloc.data(mIOBuffer.getData());
+ }
+
public void loadModelview(Matrix4f m) {
mModel = m;
- mAlloc.subData1D(MODELVIEW_OFFSET, 16, m.mMat);
+ addToBuffer(MODELVIEW_OFFSET*4, m);
}
public void loadProjection(Matrix4f m) {
mProjection = m;
- mAlloc.subData1D(PROJECTION_OFFSET, 16, m.mMat);
+ addToBuffer(PROJECTION_OFFSET*4, m);
}
public void loadTexture(Matrix4f m) {
mTexture = m;
- mAlloc.subData1D(TEXTURE_OFFSET, 16, m.mMat);
+ addToBuffer(TEXTURE_OFFSET*4, m);
}
public void setupOrthoWindow(int w, int h) {
mProjection.loadOrtho(0,w, h,0, -1,1);
- mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+ addToBuffer(PROJECTION_OFFSET*4, mProjection);
}
public void setupOrthoNormalized(int w, int h) {
@@ -147,7 +200,7 @@
float aspect = ((float)h) / w;
mProjection.loadOrtho(-1,1, -aspect,aspect, -1,1);
}
- mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+ addToBuffer(PROJECTION_OFFSET*4, mProjection);
}
public void setupProjectionNormalized(int w, int h) {
@@ -173,7 +226,7 @@
m1.loadMultiply(m1, m2);
mProjection = m1;
- mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+ addToBuffer(PROJECTION_OFFSET*4, mProjection);
}
}
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7cc4880..b1f5f6b 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -80,6 +80,9 @@
{ SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }
};
+// This array contains the swapped version of each SkXfermode. For instance
+// this array's SrcOver blending mode is actually DstOver. You can refer to
+// createLayer() for more information on the purpose of this array.
static const Blender gBlendsSwap[] = {
{ SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO },
{ SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE },
@@ -280,6 +283,54 @@
}
}
+/**
+ * Layers are viewed by Skia are slightly different than layers in image editing
+ * programs (for instance.) When a layer is created, previously created layers
+ * and the frame buffer still receive every drawing command. For instance, if a
+ * layer is created and a shape intersecting the bounds of the layers and the
+ * framebuffer is draw, the shape will be drawn on both (unless the layer was
+ * created with the SkCanvas::kClipToLayer_SaveFlag flag.)
+ *
+ * A way to implement layers is to create an FBO for each layer, backed by an RGBA
+ * texture. Unfortunately, this is inefficient as it requires every primitive to
+ * be drawn n + 1 times, where n is the number of active layers. In practice this
+ * means, for every primitive:
+ * - Switch active frame buffer
+ * - Change viewport, clip and projection matrix
+ * - Issue the drawing
+ *
+ * Switching rendering target n + 1 times per drawn primitive is extremely costly.
+ * To avoid this, layers are implemented in a different way here.
+ *
+ * This implementation relies on the frame buffer being at least RGBA 8888. When
+ * a layer is created, only a texture is created, not an FBO. The content of the
+ * frame buffer contained within the layer's bounds is copied into this texture
+ * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame
+ * buffer and drawing continues as normal. This technique therefore treats the
+ * frame buffer as a scratch buffer for the layers.
+ *
+ * To compose the layers back onto the frame buffer, each layer texture
+ * (containing the original frame buffer data) is drawn as a simple quad over
+ * the frame buffer. The trick is that the quad is set as the composition
+ * destination in the blending equation, and the frame buffer becomes the source
+ * of the composition.
+ *
+ * Drawing layers with an alpha value requires an extra step before composition.
+ * An empty quad is drawn over the layer's region in the frame buffer. This quad
+ * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the
+ * quad is used to multiply the colors in the frame buffer. This is achieved by
+ * changing the GL blend functions for the GL_FUNC_ADD blend equation to
+ * GL_ZERO, GL_SRC_ALPHA.
+ *
+ * Because glCopyTexImage2D() can be slow, an alternative implementation might
+ * be use to draw a single clipped layer. The implementation described above
+ * is correct in every case.
+ *
+ * (1) The frame buffer is actually not cleared right away. To allow the GPU
+ * to potentially optimize series of calls to glCopyTexImage2D, the frame
+ * buffer is left untouched until the first drawing operation. Only when
+ * something actually gets drawn are the layers regions cleared.
+ */
bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) {
LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top);
@@ -289,8 +340,11 @@
Rect bounds(left, top, right, bottom);
mSnapshot->transform->mapRect(bounds);
- LayerSize size(bounds.getWidth(), bounds.getHeight());
+ // Layers only make sense if they are in the framebuffer's bounds
+ bounds.intersect(*mSnapshot->clipRect);
+ if (bounds.isEmpty()) return false;
+ LayerSize size(bounds.getWidth(), bounds.getHeight());
Layer* layer = mCaches.layerCache.get(size);
if (!layer) {
return false;
@@ -319,6 +373,9 @@
return true;
}
+/**
+ * Read the documentation of createLayer() before doing anything in this method.
+ */
void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
if (!current->layer) {
LOGE("Attempting to compose a layer that does not exist");
@@ -333,16 +390,8 @@
const Rect& rect = layer->layer;
if (layer->alpha < 255) {
- glEnable(GL_BLEND);
- glBlendFuncSeparate(GL_ZERO, GL_SRC_ALPHA, GL_DST_ALPHA, GL_ZERO);
-
drawColorRect(rect.left, rect.top, rect.right, rect.bottom,
- layer->alpha << 24, SkXfermode::kSrcOver_Mode, true, true);
-
- glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode);
- if (!mCaches.blend) {
- glDisable(GL_BLEND);
- }
+ layer->alpha << 24, SkXfermode::kDstIn_Mode, true);
}
// Layers are already drawn with a top-left origin, don't flip the texture
@@ -525,6 +574,7 @@
Patch* mesh = mCaches.patchCache.get(patch);
mesh->updateVertices(bitmap, left, top, right, bottom,
&patch->xDivs[0], &patch->yDivs[0], patch->numXDivs, patch->numYDivs);
+ mesh->dump();
// Specify right and bottom as +1.0f from left/top to prevent scaling since the
// patch mesh already defines the final size
@@ -841,7 +891,7 @@
}
void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
- int color, SkXfermode::Mode mode, bool ignoreTransform, bool ignoreBlending) {
+ int color, SkXfermode::Mode mode, bool ignoreTransform) {
clearLayerRegions();
// If a shader is set, preserve only the alpha
@@ -867,10 +917,8 @@
mColorFilter->describe(description, mExtensions);
}
- if (!ignoreBlending) {
- // Setup the blending mode
- chooseBlending(alpha < 255 || (mShader && mShader->blend()), mode, description);
- }
+ // Setup the blending mode
+ chooseBlending(alpha < 255 || (mShader && mShader->blend()), mode, description);
// Build and use the appropriate shader
useProgram(mCaches.programCache.get(description));
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 12ec276..3126754 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -176,8 +176,7 @@
* @paran ignoreBlending True if the blending is set by the caller
*/
void drawColorRect(float left, float top, float right, float bottom,
- int color, SkXfermode::Mode mode, bool ignoreTransform = false,
- bool ignoreBlending = false);
+ int color, SkXfermode::Mode mode, bool ignoreTransform = false);
/**
* Draws a textured rectangle with the specified texture. The specified coordinates
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index ebedf53..062c986 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -116,6 +116,11 @@
bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op) {
bool clipped = false;
+ // NOTE: The unimplemented operations require support for regions
+ // Supporting regions would require using a stencil buffer instead
+ // of the scissor. The stencil buffer itself is not too expensive
+ // (memory cost excluded) but on fillrate limited devices, managing
+ // the stencil might have a negative impact on the framerate.
switch (op) {
case SkRegion::kDifference_Op:
break;
diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h
index 9225904..c83ece4 100644
--- a/libs/rs/RenderScriptEnv.h
+++ b/libs/rs/RenderScriptEnv.h
@@ -28,4 +28,4 @@
#define RS_PROGRAM_VERTEX_MODELVIEW_OFFSET 0
#define RS_PROGRAM_VERTEX_PROJECTION_OFFSET 16
#define RS_PROGRAM_VERTEX_TEXTURE_OFFSET 32
-
+#define RS_PROGRAM_VERTEX_MVP_OFFSET 48
diff --git a/libs/rs/java/Samples/res/raw/shaderf.glsl b/libs/rs/java/Samples/res/raw/shaderf.glsl
index fdcf481..fcbe7ee 100644
--- a/libs/rs/java/Samples/res/raw/shaderf.glsl
+++ b/libs/rs/java/Samples/res/raw/shaderf.glsl
@@ -3,10 +3,11 @@
varying lowp float light0_Specular;
varying lowp float light1_Diffuse;
varying lowp float light1_Specular;
+varying vec2 varTex0;
void main() {
vec2 t0 = varTex0.xy;
- lowp vec4 col = texture2D(uni_Tex0, t0).rgba;
+ lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
col.xyz = col.xyz * (light0_Diffuse * UNI_light0_DiffuseColor + light1_Diffuse * UNI_light1_DiffuseColor);
col.xyz += light0_Specular * UNI_light0_SpecularColor;
col.xyz += light1_Specular * UNI_light1_SpecularColor;
diff --git a/libs/rs/java/Samples/res/raw/shaderv.glsl b/libs/rs/java/Samples/res/raw/shaderv.glsl
index 7f61197..867589cf 100644
--- a/libs/rs/java/Samples/res/raw/shaderv.glsl
+++ b/libs/rs/java/Samples/res/raw/shaderv.glsl
@@ -2,24 +2,12 @@
varying float light0_Specular;
varying float light1_Diffuse;
varying float light1_Specular;
-
-/*
-rs_matrix4x4 model;
- float3 light0_Posision;
- float light0_Diffuse;
- float light0_Specular;
- float light0_CosinePower;
-
- float3 light1_Posision;
- float light1_Diffuse;
- float light1_Specular;
- float light1_CosinePower;
-*/
+varying vec2 varTex0;
// This is where actual shader code begins
void main() {
vec4 worldPos = UNI_model * ATTRIB_position;
- gl_Position = UNI_MVP * worldPos;
+ gl_Position = UNI_proj * worldPos;
mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
vec3 worldNorm = model3 * ATTRIB_normal;
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
index e76e740..d920527 100644
--- a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
+++ b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
@@ -67,7 +67,6 @@
// Custom shaders
private ProgramVertex mProgVertexCustom;
- private ProgramVertex.MatrixAllocation mPVACustom;
private ProgramFragment mProgFragmentCustom;
private ScriptField_VertexShaderConstants_s mVSConst;
private ScriptField_FragentShaderConstants_s mFSConst;
@@ -202,9 +201,7 @@
pvbCustom.addConstant(mVSConst.getAllocation().getType());
mProgVertexCustom = pvbCustom.create();
// Bind the source of constant data
- mProgVertexCustom.bindConstants(mVSConst.getAllocation(), 1);
- mPVACustom = new ProgramVertex.MatrixAllocation(mRS);
- mProgVertexCustom.bindAllocation(mPVACustom);
+ mProgVertexCustom.bindConstants(mVSConst.getAllocation(), 0);
ProgramFragment.ShaderBuilder pfbCustom = new ProgramFragment.ShaderBuilder(mRS);
// Specify the resource that contains the shader string
@@ -215,7 +212,7 @@
pfbCustom.addConstant(mFSConst.getAllocation().getType());
mProgFragmentCustom = pfbCustom.create();
// Bind the source of constant data
- mProgFragmentCustom.bindConstants(mFSConst.getAllocation(), 1);
+ mProgFragmentCustom.bindConstants(mFSConst.getAllocation(), 0);
mScript.set_gProgVertexCustom(mProgVertexCustom);
mScript.set_gProgFragmentCustom(mProgFragmentCustom);
diff --git a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
index 4bcf7f5..c7bea93 100644
--- a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
+++ b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
@@ -395,14 +395,12 @@
rsMatrixLoadTranslate(&gVSConstants->model, 0.0f, 0.0f, -10.0f);
rsMatrixRotate(&gVSConstants->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
rsMatrixRotate(&gVSConstants->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
+ // Setup the projectioni matrix
+ float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+ rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f);
setupCustomShaderLights();
rsgBindProgramVertex(gProgVertexCustom);
- // Setup the projectioni matrix with 60 degree field of view
- rs_matrix4x4 proj;
- float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
- rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
- rsgProgramVertexLoadProjectionMatrix(&proj);
// Fragment shader with texture
rsgBindProgramStore(gProgStoreBlendNoneDepth);
@@ -416,7 +414,7 @@
rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
rsgBindFont(gFontMono);
- rsgDrawText("Custom shader sample", 10, rsgGetHeight() - 10);
+ //rsgDrawText("Custom shader sample", 10, rsgGetHeight() - 10);
}
int root(int launchID) {
diff --git a/libs/rs/java/Samples/src/com/android/samples/shader_def.rsh b/libs/rs/java/Samples/src/com/android/samples/shader_def.rsh
index 1b697ca..e3f6206 100644
--- a/libs/rs/java/Samples/src/com/android/samples/shader_def.rsh
+++ b/libs/rs/java/Samples/src/com/android/samples/shader_def.rsh
@@ -18,6 +18,7 @@
typedef struct VertexShaderConstants_s {
rs_matrix4x4 model;
+ rs_matrix4x4 proj;
float3 light0_Posision;
float light0_Diffuse;
float light0_Specular;
@@ -27,7 +28,6 @@
float light1_Diffuse;
float light1_Specular;
float light1_CosinePower;
-
} VertexShaderConstants;
typedef struct FragentShaderConstants_s {
@@ -42,6 +42,6 @@
typedef struct VertexShaderInputs_s {
float4 position;
float3 normal;
- float4 texture0;
+ float2 texture0;
} VertexShaderInputs;
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index 0b7bb27..d0909c8 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -314,6 +314,11 @@
ElementState::ElementState()
{
+ const uint32_t initialCapacity = 32;
+ mBuilderElements.setCapacity(initialCapacity);
+ mBuilderNameStrings.setCapacity(initialCapacity);
+ mBuilderNameLengths.setCapacity(initialCapacity);
+ mBuilderArrays.setCapacity(initialCapacity);
}
ElementState::~ElementState()
@@ -321,6 +326,29 @@
rsAssert(!mElements.size());
}
+void ElementState::elementBuilderBegin() {
+ mBuilderElements.clear();
+ mBuilderNameStrings.clear();
+ mBuilderNameLengths.clear();
+ mBuilderArrays.clear();
+}
+
+void ElementState::elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize) {
+ mBuilderElements.push(e);
+ mBuilderNameStrings.push(nameStr);
+ mBuilderNameLengths.push(strlen(nameStr));
+ mBuilderArrays.push(arraySize);
+
+}
+
+const Element *ElementState::elementBuilderCreate(Context *rsc) {
+ return Element::create(rsc, mBuilderElements.size(),
+ &(mBuilderElements.editArray()[0]),
+ &(mBuilderNameStrings.editArray()[0]),
+ mBuilderNameLengths.editArray(),
+ mBuilderArrays.editArray());
+}
+
/////////////////////////////////////////
//
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
index 42eef4a..ae6a6cc 100644
--- a/libs/rs/rsElement.h
+++ b/libs/rs/rsElement.h
@@ -99,8 +99,17 @@
ElementState();
~ElementState();
+ void elementBuilderBegin();
+ void elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize);
+ const Element *elementBuilderCreate(Context *rsc);
+
// Cache of all existing elements.
Vector<Element *> mElements;
+private:
+ Vector<const Element *> mBuilderElements;
+ Vector<const char*> mBuilderNameStrings;
+ Vector<size_t> mBuilderNameLengths;
+ Vector<uint32_t> mBuilderArrays;
};
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index 5889bfb..0f815a2 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -387,14 +387,34 @@
void FontState::initRenderState()
{
- uint32_t tmp[] = {
- RS_TEX_ENV_MODE_REPLACE, 1,
- RS_TEX_ENV_MODE_NONE, 0,
- 0, 0
- };
- ProgramFragment *pf = new ProgramFragment(mRSC, tmp, 6);
+ String8 shaderString("varying vec4 varTex0;\n");
+ shaderString.append("void main() {\n");
+ shaderString.append(" lowp vec4 col = UNI_Color;\n");
+ shaderString.append(" col.a = texture2D(UNI_Tex0, varTex0.xy).a;\n");
+ shaderString.append(" gl_FragColor = col;\n");
+ shaderString.append("}\n");
+
+ const Element *colorElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
+ mRSC->mStateElement.elementBuilderBegin();
+ mRSC->mStateElement.elementBuilderAdd(colorElem, "Color", 1);
+ const Element *constInput = mRSC->mStateElement.elementBuilderCreate(mRSC);
+
+ Type *inputType = new Type(mRSC);
+ inputType->setElement(constInput);
+ inputType->setDimX(1);
+ inputType->compute();
+
+ uint32_t tmp[4];
+ tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
+ tmp[1] = (uint32_t)inputType;
+ tmp[2] = RS_PROGRAM_PARAM_TEXTURE_COUNT;
+ tmp[3] = 1;
+
+ mFontShaderFConstant.set(new Allocation(mRSC, inputType));
+ ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(),
+ shaderString.length(), tmp, 4);
mFontShaderF.set(pf);
- mFontShaderF->init(mRSC);
+ mFontShaderF->bindAllocation(mFontShaderFConstant.get(), 0);
Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST,
RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP);
@@ -539,7 +559,7 @@
mRSC->setFragmentStore(mFontProgramStore.get());
if(mFontColorDirty) {
- mFontShaderF->setConstantColor(mFontColor[0], mFontColor[1], mFontColor[2], mFontColor[3]);
+ mFontShaderFConstant->data(mRSC, &mFontColor, 4*sizeof(float));
mFontColorDirty = false;
}
diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h
index defe38b..027ed1d 100644
--- a/libs/rs/rsFont.h
+++ b/libs/rs/rsFont.h
@@ -171,6 +171,7 @@
Vector<Font*> mActiveFonts;
// Render state for the font
+ ObjectBaseRef<Allocation> mFontShaderFConstant;
ObjectBaseRef<ProgramFragment> mFontShaderF;
ObjectBaseRef<Sampler> mFontSampler;
ObjectBaseRef<ProgramStore> mFontProgramStore;
diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp
index 6041db8..2441491 100644
--- a/libs/rs/rsProgram.cpp
+++ b/libs/rs/rsProgram.cpp
@@ -46,6 +46,7 @@
mOutputCount = 0;
mConstantCount = 0;
mIsValid = false;
+ mIsInternal = false;
}
Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength,
@@ -97,6 +98,14 @@
mConstantTypes[constant++].set(reinterpret_cast<Type *>(params[ct+1]));
}
}
+ mIsInternal = false;
+ uint32_t internalTokenLen = strlen(RS_SHADER_INTERNAL);
+ if(shaderLength > internalTokenLen &&
+ strncmp(RS_SHADER_INTERNAL, shaderText, internalTokenLen) == 0) {
+ mIsInternal = true;
+ shaderText += internalTokenLen;
+ shaderLength -= internalTokenLen;
+ }
mUserShader.setTo(shaderText, shaderLength);
}
@@ -281,9 +290,9 @@
}
void Program::setupUserConstants(ShaderCache *sc, bool isFragment) {
- uint32_t uidx = 1;
+ uint32_t uidx = 0;
for (uint32_t ct=0; ct < mConstantCount; ct++) {
- Allocation *alloc = mConstants[ct+1].get();
+ Allocation *alloc = mConstants[ct].get();
if (!alloc) {
continue;
}
@@ -313,6 +322,9 @@
if (slot >= 0) {
if(f->getType() == RS_TYPE_MATRIX_4X4) {
glUniformMatrix4fv(slot, 1, GL_FALSE, fd);
+ /*for(int i = 0; i < 4; i++) {
+ LOGE("Mat = %f %f %f %f", fd[i*4 + 0], fd[i*4 + 1], fd[i*4 + 2], fd[i*4 + 3]);
+ }*/
}
else if(f->getType() == RS_TYPE_MATRIX_3X3) {
glUniformMatrix3fv(slot, 1, GL_FALSE, fd);
diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h
index ddc5e8a..e7329c2 100644
--- a/libs/rs/rsProgram.h
+++ b/libs/rs/rsProgram.h
@@ -23,10 +23,10 @@
// ---------------------------------------------------------------------------
namespace android {
namespace renderscript {
-
-
class ShaderCache;
+#define RS_SHADER_INTERNAL "//rs_shader_internal\n"
+
class Program : public ObjectBase
{
public:
@@ -42,7 +42,7 @@
void bindAllocation(Allocation *, uint32_t slot);
virtual void createShader();
- bool isUserProgram() const {return mUserShader.size() > 0;}
+ bool isUserProgram() const {return !mIsInternal;}
void bindTexture(uint32_t slot, Allocation *);
void bindSampler(uint32_t slot, Sampler *);
@@ -71,6 +71,7 @@
uint32_t mOutputCount;
uint32_t mConstantCount;
bool mIsValid;
+ bool mIsInternal;
// Applies to vertex and fragment shaders only
void appendUserConstants();
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index 83321d3..3174e82 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -92,10 +92,14 @@
void ProgramFragment::setConstantColor(float r, float g, float b, float a)
{
+ if(isUserProgram()) {
+ return;
+ }
mConstantColor[0] = r;
mConstantColor[1] = g;
mConstantColor[2] = b;
mConstantColor[3] = a;
+ memcpy(mConstants[0]->getPtr(), mConstantColor, 4*sizeof(float));
mDirty = true;
}
@@ -114,13 +118,6 @@
rsc->checkError("ProgramFragment::setupGL2 start");
- if (!mVaryingColor &&
- (sc->fragUniformSlot(mConstantColorUniformIndex) >= 0)) {
- //LOGE("mConstantColorUniformIndex %i %i", mConstantColorUniformIndex, sc->fragUniformSlot(mConstantColorUniformIndex));
- glUniform4fv(sc->fragUniformSlot(mConstantColorUniformIndex), 1, mConstantColor);
- rsc->checkError("ProgramFragment::color setup");
- }
-
rsc->checkError("ProgramFragment::setupGL2 begin uniforms");
setupUserConstants(sc, true);
@@ -158,124 +155,32 @@
void ProgramFragment::createShader()
{
- mShader.setTo("precision mediump float;\n");
- mShader.append("varying lowp vec4 varColor;\n");
- mShader.append("varying vec4 varTex0;\n");
- mShader.append("uniform vec4 uni_Color;\n");
-
if (mUserShader.length() > 1) {
+ mShader.append("precision mediump float;\n");
appendUserConstants();
for (uint32_t ct=0; ct < mTextureCount; ct++) {
char buf[256];
- sprintf(buf, "uniform sampler2D uni_Tex%i;\n", ct);
+ sprintf(buf, "uniform sampler2D UNI_Tex%i;\n", ct);
mShader.append(buf);
}
mShader.append(mUserShader);
} else {
- uint32_t mask = mTextureEnableMask;
- uint32_t texNum = 0;
- while (mask) {
- if (mask & 1) {
- char buf[64];
- mShader.append("uniform sampler2D uni_Tex");
- sprintf(buf, "%i", texNum);
- mShader.append(buf);
- mShader.append(";\n");
- }
- mask >>= 1;
- texNum++;
- }
-
-
- mShader.append("void main() {\n");
- if (mVaryingColor) {
- mShader.append(" lowp vec4 col = varColor;\n");
- } else {
- mShader.append(" lowp vec4 col = uni_Color;\n");
- }
-
- if (mTextureEnableMask) {
- if (mPointSpriteEnable) {
- mShader.append(" vec2 t0 = gl_PointCoord;\n");
- } else {
- mShader.append(" vec2 t0 = varTex0.xy;\n");
- }
- }
-
- mask = mTextureEnableMask;
- texNum = 0;
- while (mask) {
- if (mask & 1) {
- switch(mEnvModes[texNum]) {
- case RS_TEX_ENV_MODE_NONE:
- rsAssert(0);
- break;
- case RS_TEX_ENV_MODE_REPLACE:
- switch(mTextureFormats[texNum]) {
- case 1:
- mShader.append(" col.a = texture2D(uni_Tex0, t0).a;\n");
- break;
- case 2:
- mShader.append(" col.rgba = texture2D(uni_Tex0, t0).rgba;\n");
- break;
- case 3:
- mShader.append(" col.rgb = texture2D(uni_Tex0, t0).rgb;\n");
- break;
- case 4:
- mShader.append(" col.rgba = texture2D(uni_Tex0, t0).rgba;\n");
- break;
- }
- break;
- case RS_TEX_ENV_MODE_MODULATE:
- switch(mTextureFormats[texNum]) {
- case 1:
- mShader.append(" col.a *= texture2D(uni_Tex0, t0).a;\n");
- break;
- case 2:
- mShader.append(" col.rgba *= texture2D(uni_Tex0, t0).rgba;\n");
- break;
- case 3:
- mShader.append(" col.rgb *= texture2D(uni_Tex0, t0).rgb;\n");
- break;
- case 4:
- mShader.append(" col.rgba *= texture2D(uni_Tex0, t0).rgba;\n");
- break;
- }
- break;
- case RS_TEX_ENV_MODE_DECAL:
- mShader.append(" col = texture2D(uni_Tex0, t0);\n");
- break;
- }
-
- }
- mask >>= 1;
- texNum++;
- }
-
- //mShader.append(" col.a = 1.0;\n");
- //mShader.append(" col.r = 0.5;\n");
-
- mShader.append(" gl_FragColor = col;\n");
- mShader.append("}\n");
+ LOGE("ProgramFragment::createShader cannot create program, shader code not defined");
+ rsAssert(0);
}
}
void ProgramFragment::init(Context *rsc)
{
mUniformCount = 0;
- //if (!mVaryingColor) {
- mConstantColorUniformIndex = mUniformCount;
- mUniformNames[mUniformCount++].setTo("uni_Color");
- //}
-
if (mUserShader.size() > 0) {
for (uint32_t ct=0; ct < mConstantCount; ct++) {
initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, &mUniformCount, "UNI_");
}
}
mTextureUniformIndexStart = mUniformCount;
- mUniformNames[mUniformCount++].setTo("uni_Tex0");
- mUniformNames[mUniformCount++].setTo("uni_Tex1");
+ mUniformNames[mUniformCount++].setTo("UNI_Tex0");
+ mUniformNames[mUniformCount++].setTo("UNI_Tex1");
createShader();
}
@@ -303,12 +208,36 @@
void ProgramFragmentState::init(Context *rsc)
{
- uint32_t tmp[] = {
- RS_TEX_ENV_MODE_NONE, 0,
- RS_TEX_ENV_MODE_NONE, 0,
- 0, 0
- };
- ProgramFragment *pf = new ProgramFragment(rsc, tmp, 6);
+ String8 shaderString(RS_SHADER_INTERNAL);
+ shaderString.append("varying lowp vec4 varColor;\n");
+ shaderString.append("varying vec4 varTex0;\n");
+ shaderString.append("void main() {\n");
+ shaderString.append(" lowp vec4 col = UNI_Color;\n");
+ shaderString.append(" gl_FragColor = col;\n");
+ shaderString.append("}\n");
+
+ const Element *colorElem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
+ rsc->mStateElement.elementBuilderBegin();
+ rsc->mStateElement.elementBuilderAdd(colorElem, "Color", 1);
+ const Element *constInput = rsc->mStateElement.elementBuilderCreate(rsc);
+
+ Type *inputType = new Type(rsc);
+ inputType->setElement(constInput);
+ inputType->setDimX(1);
+ inputType->compute();
+
+ uint32_t tmp[4];
+ tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
+ tmp[1] = (uint32_t)inputType;
+ tmp[2] = RS_PROGRAM_PARAM_TEXTURE_COUNT;
+ tmp[3] = 0;
+
+ Allocation *constAlloc = new Allocation(rsc, inputType);
+ ProgramFragment *pf = new ProgramFragment(rsc, shaderString.string(),
+ shaderString.length(), tmp, 4);
+ pf->bindAllocation(constAlloc, 0);
+ pf->setConstantColor(1.0f, 1.0f, 1.0f, 1.0f);
+
mDefault.set(pf);
}
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 68e3705..28084d7 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -79,15 +79,7 @@
void ProgramVertex::createShader()
{
- mShader.setTo("");
-
- mShader.append("varying vec4 varColor;\n");
- mShader.append("varying vec4 varTex0;\n");
-
if (mUserShader.length() > 1) {
- mShader.append("uniform mat4 ");
- mShader.append(mUniformNames[0]);
- mShader.append(";\n");
appendUserConstants();
@@ -118,28 +110,8 @@
}
mShader.append(mUserShader);
} else {
- mShader.append("attribute vec4 ATTRIB_position;\n");
- mShader.append("attribute vec4 ATTRIB_color;\n");
- mShader.append("attribute vec3 ATTRIB_normal;\n");
- mShader.append("attribute vec4 ATTRIB_texture0;\n");
-
- for (uint32_t ct=0; ct < mUniformCount; ct++) {
- mShader.append("uniform mat4 ");
- mShader.append(mUniformNames[ct]);
- mShader.append(";\n");
- }
-
- mShader.append("void main() {\n");
- mShader.append(" gl_Position = UNI_MVP * ATTRIB_position;\n");
- mShader.append(" gl_PointSize = 1.0;\n");
-
- mShader.append(" varColor = ATTRIB_color;\n");
- if (mTextureMatrixEnable) {
- mShader.append(" varTex0 = UNI_TexMatrix * ATTRIB_texture0;\n");
- } else {
- mShader.append(" varTex0 = ATTRIB_texture0;\n");
- }
- mShader.append("}\n");
+ LOGE("ProgramFragment::createShader cannot create program, shader code not defined");
+ rsAssert(0);
}
}
@@ -152,18 +124,16 @@
rsc->checkError("ProgramVertex::setupGL2 start");
- const float *f = static_cast<const float *>(mConstants[0]->getPtr());
-
- Matrix mvp;
- mvp.load(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
- Matrix t;
- t.load(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]);
- mvp.multiply(&t);
-
- glUniformMatrix4fv(sc->vtxUniformSlot(0), 1, GL_FALSE, mvp.m);
- if (mTextureMatrixEnable) {
- glUniformMatrix4fv(sc->vtxUniformSlot(1), 1, GL_FALSE,
- &f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET]);
+ if(!isUserProgram()) {
+ float *f = static_cast<float *>(mConstants[0]->getPtr());
+ Matrix mvp;
+ mvp.load(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
+ Matrix t;
+ t.load(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]);
+ mvp.multiply(&t);
+ for(uint32_t i = 0; i < 16; i ++) {
+ f[RS_PROGRAM_VERTEX_MVP_OFFSET + i] = mvp.m[i];
+ }
}
rsc->checkError("ProgramVertex::setupGL2 begin uniforms");
@@ -183,6 +153,9 @@
void ProgramVertex::setProjectionMatrix(const rsc_Matrix *m) const
{
+ if(isUserProgram()) {
+ return;
+ }
float *f = static_cast<float *>(mConstants[0]->getPtr());
memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m, sizeof(rsc_Matrix));
mDirty = true;
@@ -190,6 +163,9 @@
void ProgramVertex::setModelviewMatrix(const rsc_Matrix *m) const
{
+ if(isUserProgram()) {
+ return;
+ }
float *f = static_cast<float *>(mConstants[0]->getPtr());
memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m, sizeof(rsc_Matrix));
mDirty = true;
@@ -197,6 +173,9 @@
void ProgramVertex::setTextureMatrix(const rsc_Matrix *m) const
{
+ if(isUserProgram()) {
+ return;
+ }
float *f = static_cast<float *>(mConstants[0]->getPtr());
memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m, sizeof(rsc_Matrix));
mDirty = true;
@@ -204,12 +183,18 @@
void ProgramVertex::getProjectionMatrix(rsc_Matrix *m) const
{
+ if(isUserProgram()) {
+ return;
+ }
float *f = static_cast<float *>(mConstants[0]->getPtr());
memcpy(m, &f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], sizeof(rsc_Matrix));
}
void ProgramVertex::transformToScreen(const Context *rsc, float *v4out, const float *v3in) const
{
+ if(isUserProgram()) {
+ return;
+ }
float *f = static_cast<float *>(mConstants[0]->getPtr());
Matrix mvp;
mvp.loadMultiply((Matrix *)&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET],
@@ -225,17 +210,11 @@
initAddUserElement(mInputElements[ct].get(), mAttribNames, &mAttribCount, "ATTRIB_");
}
- mUniformCount = 1;
- mUniformNames[0].setTo("UNI_MVP");
+ mUniformCount = 0;
for (uint32_t ct=0; ct < mConstantCount; ct++) {
initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, &mUniformCount, "UNI_");
}
- } else {
- mUniformCount = 2;
- mUniformNames[0].setTo("UNI_MVP");
- mUniformNames[1].setTo("UNI_TexMatrix");
}
-
createShader();
}
@@ -262,41 +241,78 @@
void ProgramVertexState::init(Context *rsc)
{
-#ifndef ANDROID_RS_BUILD_FOR_HOST
- RsElement e = (RsElement) Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1);
+ const Element *matrixElem = Element::create(rsc, RS_TYPE_MATRIX_4X4, RS_KIND_USER, false, 1);
+ const Element *f3Elem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
+ const Element *f4Elem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
- rsi_TypeBegin(rsc, e);
- rsi_TypeAdd(rsc, RS_DIMENSION_X, 48);
- mAllocType.set((Type *)rsi_TypeCreate(rsc));
+ rsc->mStateElement.elementBuilderBegin();
+ rsc->mStateElement.elementBuilderAdd(matrixElem, "MV", 1);
+ rsc->mStateElement.elementBuilderAdd(matrixElem, "P", 1);
+ rsc->mStateElement.elementBuilderAdd(matrixElem, "TexMatrix", 1);
+ rsc->mStateElement.elementBuilderAdd(matrixElem, "MVP", 1);
+ const Element *constInput = rsc->mStateElement.elementBuilderCreate(rsc);
- ProgramVertex *pv = new ProgramVertex(rsc, false);
- Allocation *alloc = (Allocation *)rsi_AllocationCreateTyped(rsc, mAllocType.get());
+ rsc->mStateElement.elementBuilderBegin();
+ rsc->mStateElement.elementBuilderAdd(f4Elem, "position", 1);
+ rsc->mStateElement.elementBuilderAdd(f4Elem, "color", 1);
+ rsc->mStateElement.elementBuilderAdd(f3Elem, "normal", 1);
+ rsc->mStateElement.elementBuilderAdd(f4Elem, "texture0", 1);
+ const Element *attrElem = rsc->mStateElement.elementBuilderCreate(rsc);
+
+ Type *inputType = new Type(rsc);
+ inputType->setElement(constInput);
+ inputType->setDimX(1);
+ inputType->compute();
+
+ String8 shaderString(RS_SHADER_INTERNAL);
+ shaderString.append("varying vec4 varColor;\n");
+ shaderString.append("varying vec4 varTex0;\n");
+ shaderString.append("void main() {\n");
+ shaderString.append(" gl_Position = UNI_MVP * ATTRIB_position;\n");
+ shaderString.append(" gl_PointSize = 1.0;\n");
+ shaderString.append(" varColor = ATTRIB_color;\n");
+ shaderString.append(" varTex0 = ATTRIB_texture0;\n");
+ shaderString.append("}\n");
+
+ uint32_t tmp[6];
+ tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
+ tmp[1] = (uint32_t)inputType;
+ tmp[2] = RS_PROGRAM_PARAM_INPUT;
+ tmp[3] = (uint32_t)attrElem;
+ tmp[4] = RS_PROGRAM_PARAM_TEXTURE_COUNT;
+ tmp[5] = 0;
+
+ ProgramVertex *pv = new ProgramVertex(rsc, shaderString.string(),
+ shaderString.length(), tmp, 6);
+ Allocation *alloc = new Allocation(rsc, inputType);
+ pv->bindAllocation(alloc, 0);
mDefaultAlloc.set(alloc);
mDefault.set(pv);
- pv->init(rsc);
pv->bindAllocation(alloc, 0);
updateSize(rsc);
-#endif //ANDROID_RS_BUILD_FOR_HOST
}
void ProgramVertexState::updateSize(Context *rsc)
{
+ float *f = static_cast<float *>(mDefaultAlloc->getPtr());
+
Matrix m;
m.loadOrtho(0,rsc->getWidth(), rsc->getHeight(),0, -1,1);
- mDefaultAlloc->subData(rsc, RS_PROGRAM_VERTEX_PROJECTION_OFFSET, 16, &m.m[0], 16*4);
+ memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m.m, sizeof(m));
+ memcpy(&f[RS_PROGRAM_VERTEX_MVP_OFFSET], m.m, sizeof(m));
m.loadIdentity();
- mDefaultAlloc->subData(rsc, RS_PROGRAM_VERTEX_MODELVIEW_OFFSET, 16, &m.m[0], 16*4);
+ memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m.m, sizeof(m));
+ memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m.m, sizeof(m));
}
void ProgramVertexState::deinit(Context *rsc)
{
mDefaultAlloc.clear();
mDefault.clear();
- mAllocType.clear();
mLast.clear();
}
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index 59fd3195..d6b3f5a 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -79,8 +79,6 @@
ObjectBaseRef<ProgramVertex> mDefault;
ObjectBaseRef<ProgramVertex> mLast;
ObjectBaseRef<Allocation> mDefaultAlloc;
-
- ObjectBaseRef<Type> mAllocType;
};
diff --git a/location/java/android/location/ILocationProvider.aidl b/location/java/android/location/ILocationProvider.aidl
index 2b9782a..ecf6789 100644
--- a/location/java/android/location/ILocationProvider.aidl
+++ b/location/java/android/location/ILocationProvider.aidl
@@ -20,6 +20,7 @@
import android.location.Location;
import android.net.NetworkInfo;
import android.os.Bundle;
+import android.os.WorkSource;
/**
* Binder interface for services that implement location providers.
@@ -43,7 +44,7 @@
long getStatusUpdateTime();
String getInternalState();
void enableLocationTracking(boolean enable);
- void setMinTime(long minTime);
+ void setMinTime(long minTime, in WorkSource ws);
void updateNetworkState(int state, in NetworkInfo info);
void updateLocation(in Location location);
boolean sendExtraCommand(String command, inout Bundle extras);
diff --git a/location/java/android/location/provider/LocationProvider.java b/location/java/android/location/provider/LocationProvider.java
index cf939de..95b4425 100644
--- a/location/java/android/location/provider/LocationProvider.java
+++ b/location/java/android/location/provider/LocationProvider.java
@@ -26,6 +26,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.WorkSource;
import android.util.Log;
/**
@@ -106,8 +107,8 @@
LocationProvider.this.onEnableLocationTracking(enable);
}
- public void setMinTime(long minTime) {
- LocationProvider.this.onSetMinTime(minTime);
+ public void setMinTime(long minTime, WorkSource ws) {
+ LocationProvider.this.onSetMinTime(minTime, ws);
}
public void updateNetworkState(int state, NetworkInfo info) {
@@ -123,11 +124,11 @@
}
public void addListener(int uid) {
- LocationProvider.this.onAddListener(uid);
+ LocationProvider.this.onAddListener(uid, new WorkSource(uid));
}
public void removeListener(int uid) {
- LocationProvider.this.onRemoveListener(uid);
+ LocationProvider.this.onRemoveListener(uid, new WorkSource(uid));
}
};
@@ -303,8 +304,9 @@
* the frequency of updates to match the requested frequency.
*
* @param minTime the smallest minTime value over all listeners for this provider.
+ * @param ws the source this work is coming from.
*/
- public abstract void onSetMinTime(long minTime);
+ public abstract void onSetMinTime(long minTime, WorkSource ws);
/**
* Updates the network state for the given provider. This function must
@@ -340,14 +342,16 @@
* Notifies the location provider when a new client is listening for locations.
*
* @param uid user ID of the new client.
+ * @param ws a WorkSource representation of the client.
*/
- public abstract void onAddListener(int uid);
+ public abstract void onAddListener(int uid, WorkSource ws);
/**
* Notifies the location provider when a client is no longer listening for locations.
*
* @param uid user ID of the client no longer listening.
+ * @param ws a WorkSource representation of the client.
*/
- public abstract void onRemoveListener(int uid);
+ public abstract void onRemoveListener(int uid, WorkSource ws);
}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 9f2a49c..b2a0a46 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -110,15 +110,28 @@
private final static String TAG = "MediaScanner";
- private static final String[] PRESCAN_PROJECTION = new String[] {
+ private static final String[] FILES_PRESCAN_PROJECTION = new String[] {
Files.FileColumns._ID, // 0
Files.FileColumns.DATA, // 1
- Files.FileColumns.DATE_MODIFIED, // 2
+ Files.FileColumns.FORMAT, // 2
+ Files.FileColumns.DATE_MODIFIED, // 3
};
- private static final int PRESCAN_ID_COLUMN_INDEX = 0;
- private static final int PRESCAN_PATH_COLUMN_INDEX = 1;
- private static final int PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 2;
+ private static final int FILES_PRESCAN_ID_COLUMN_INDEX = 0;
+ private static final int FILES_PRESCAN_PATH_COLUMN_INDEX = 1;
+ private static final int FILES_PRESCAN_FORMAT_COLUMN_INDEX = 2;
+ private static final int FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 3;
+
+ private static final String[] MEDIA_PRESCAN_PROJECTION = new String[] {
+ MediaStore.MediaColumns._ID, // 0
+ MediaStore.MediaColumns.DATA, // 1
+ MediaStore.MediaColumns.DATE_MODIFIED, // 2
+ };
+
+ private static final int MEDIA_PRESCAN_ID_COLUMN_INDEX = 0;
+ private static final int MEDIA_PRESCAN_PATH_COLUMN_INDEX = 1;
+ private static final int MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 2;
+
private static final String[] PLAYLIST_MEMBERS_PROJECTION = new String[] {
Audio.Playlists.Members.PLAYLIST_ID, // 0
@@ -316,14 +329,16 @@
long mRowId;
String mPath;
long mLastModified;
+ int mFormat;
boolean mSeenInFileSystem;
boolean mLastModifiedChanged;
- FileCacheEntry(Uri tableUri, long rowId, String path, long lastModified) {
+ FileCacheEntry(Uri tableUri, long rowId, String path, long lastModified, int format) {
mTableUri = tableUri;
mRowId = rowId;
mPath = path;
mLastModified = lastModified;
+ mFormat = format;
mSeenInFileSystem = false;
mLastModifiedChanged = false;
}
@@ -444,7 +459,7 @@
} else {
tableUri = mFilesUri;
}
- entry = new FileCacheEntry(tableUri, 0, path, 0);
+ entry = new FileCacheEntry(tableUri, 0, path, 0, 0);
mFileCache.put(key, entry);
}
entry.mSeenInFileSystem = true;
@@ -884,13 +899,15 @@
if (prescanFiles) {
// First read existing files from the files table
- c = mMediaProvider.query(mFilesUri, PRESCAN_PROJECTION, where, selectionArgs, null);
+ c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
+ where, selectionArgs, null);
if (c != null) {
while (c.moveToNext()) {
- long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
- String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
- long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+ long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
+ String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
+ int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
+ long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
// Only consider entries with absolute path names.
// This allows storing URIs in the database without the
@@ -902,7 +919,7 @@
}
FileCacheEntry entry = new FileCacheEntry(mFilesUri, rowId, path,
- lastModified);
+ lastModified, format);
mFileCache.put(key, entry);
}
}
@@ -912,12 +929,13 @@
}
// Read existing files from the audio table and update FileCacheEntry
- c = mMediaProvider.query(mAudioUri, PRESCAN_PROJECTION, where, selectionArgs, null);
+ c = mMediaProvider.query(mAudioUri, MEDIA_PRESCAN_PROJECTION,
+ where, selectionArgs, null);
if (c != null) {
while (c.moveToNext()) {
- long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
- String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
- long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+ long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
+ String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
+ long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
// Only consider entries with absolute path names.
// This allows storing URIs in the database without the
@@ -930,7 +948,7 @@
FileCacheEntry entry = mFileCache.get(path);
if (entry == null) {
mFileCache.put(key, new FileCacheEntry(mAudioUri, rowId, path,
- lastModified));
+ lastModified, 0));
} else {
// update the entry
entry.mTableUri = mAudioUri;
@@ -943,12 +961,13 @@
}
// Read existing files from the video table and update FileCacheEntry
- c = mMediaProvider.query(mVideoUri, PRESCAN_PROJECTION, where, selectionArgs, null);
+ c = mMediaProvider.query(mVideoUri, MEDIA_PRESCAN_PROJECTION,
+ where, selectionArgs, null);
if (c != null) {
while (c.moveToNext()) {
- long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
- String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
- long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+ long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
+ String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
+ long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
// Only consider entries with absolute path names.
// This allows storing URIs in the database without the
@@ -961,7 +980,7 @@
FileCacheEntry entry = mFileCache.get(path);
if (entry == null) {
mFileCache.put(key, new FileCacheEntry(mVideoUri, rowId, path,
- lastModified));
+ lastModified, 0));
} else {
// update the entry
entry.mTableUri = mVideoUri;
@@ -974,12 +993,13 @@
}
// Read existing files from the video table and update FileCacheEntry
- c = mMediaProvider.query(mImagesUri, PRESCAN_PROJECTION, where, selectionArgs, null);
+ c = mMediaProvider.query(mImagesUri, MEDIA_PRESCAN_PROJECTION,
+ where, selectionArgs, null);
if (c != null) {
while (c.moveToNext()) {
- long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
- String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
- long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+ long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
+ String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
+ long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
// Only consider entries with absolute path names.
// This allows storing URIs in the database without the
@@ -992,7 +1012,7 @@
FileCacheEntry entry = mFileCache.get(path);
if (entry == null) {
mFileCache.put(key, new FileCacheEntry(mImagesUri, rowId, path,
- lastModified));
+ lastModified, 0));
} else {
// update the entry
entry.mTableUri = mImagesUri;
@@ -1006,13 +1026,13 @@
if (mProcessPlaylists) {
// Read existing files from the playlists table and update FileCacheEntry
- c = mMediaProvider.query(mPlaylistsUri, PRESCAN_PROJECTION, where,
- selectionArgs, null);
+ c = mMediaProvider.query(mPlaylistsUri, MEDIA_PRESCAN_PROJECTION,
+ where, selectionArgs, null);
if (c != null) {
while (c.moveToNext()) {
- long rowId = c.getLong(PRESCAN_ID_COLUMN_INDEX);
- String path = c.getString(PRESCAN_PATH_COLUMN_INDEX);
- long lastModified = c.getLong(PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+ long rowId = c.getLong(MEDIA_PRESCAN_ID_COLUMN_INDEX);
+ String path = c.getString(MEDIA_PRESCAN_PATH_COLUMN_INDEX);
+ long lastModified = c.getLong(MEDIA_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
// Only consider entries with absolute path names.
// This allows storing URIs in the database without the
@@ -1025,7 +1045,7 @@
FileCacheEntry entry = mFileCache.get(path);
if (entry == null) {
mFileCache.put(key, new FileCacheEntry(mPlaylistsUri, rowId, path,
- lastModified));
+ lastModified, 0));
} else {
// update the entry
entry.mTableUri = mPlaylistsUri;
@@ -1109,12 +1129,14 @@
// remove database entries for files that no longer exist.
boolean fileMissing = false;
- if (!entry.mSeenInFileSystem) {
- if (inScanDirectory(path, directories)) {
+ if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) {
+ if (entry.mFormat != MtpConstants.FORMAT_ASSOCIATION &&
+ inScanDirectory(path, directories)) {
// we didn't see this file in the scan directory.
fileMissing = true;
} else {
- // the file is outside of our scan directory,
+ // the file actually a directory or other abstract object
+ // or is outside of our scan directory,
// so we need to check for file existence here.
File testFile = new File(path);
if (!testFile.exists()) {
@@ -1134,9 +1156,11 @@
ContentValues values = new ContentValues();
values.put(MediaStore.Audio.Playlists.DATA, "");
values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, 0);
- mMediaProvider.update(ContentUris.withAppendedId(mPlaylistsUri, entry.mRowId), values, null, null);
+ mMediaProvider.update(ContentUris.withAppendedId(mPlaylistsUri, entry.mRowId),
+ values, null, null);
} else {
- mMediaProvider.delete(ContentUris.withAppendedId(mFilesUri, entry.mRowId), null, null);
+ mMediaProvider.delete(ContentUris.withAppendedId(mFilesUri, entry.mRowId),
+ null, null);
iterator.remove();
}
}
diff --git a/media/java/android/media/MtpConstants.java b/media/java/android/media/MtpConstants.java
index 153f64f..a7d33ce 100644
--- a/media/java/android/media/MtpConstants.java
+++ b/media/java/android/media/MtpConstants.java
@@ -138,6 +138,28 @@
public static final int FORMAT_ABSTRACT_CONTACT = 0xBB81;
public static final int FORMAT_VCARD_2 = 0xBB82;
+ public static boolean isAbstractObject(int format) {
+ switch (format) {
+ case FORMAT_ABSTRACT_MULTIMEDIA_ALBUM:
+ case FORMAT_ABSTRACT_IMAGE_ALBUM:
+ case FORMAT_ABSTRACT_AUDIO_ALBUM:
+ case FORMAT_ABSTRACT_VIDEO_ALBUM:
+ case FORMAT_ABSTRACT_AV_PLAYLIST:
+ case FORMAT_ABSTRACT_CONTACT_GROUP:
+ case FORMAT_ABSTRACT_MESSAGE_FOLDER:
+ case FORMAT_ABSTRACT_CHAPTERED_PRODUCTION:
+ case FORMAT_ABSTRACT_AUDIO_PLAYLIST:
+ case FORMAT_ABSTRACT_VIDEO_PLAYLIST:
+ case FORMAT_ABSTRACT_MEDIACAST:
+ case FORMAT_ABSTRACT_DOCUMENT:
+ case FORMAT_ABSTRACT_MESSSAGE:
+ case FORMAT_ABSTRACT_CONTACT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
// MTP object properties
public static final int PROPERTY_STORAGE_ID = 0xDC01;
public static final int PROPERTY_OBJECT_FORMAT = 0xDC02;
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index 6b0b761..b64299a 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -478,12 +478,26 @@
}
}
+ private int deleteRecursive(int handle) throws RemoteException {
+ int[] children = getObjectList(0 /* storageID */, 0 /* format */, handle);
+ Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
+ // delete parent first, to avoid potential infinite recursion
+ int count = mMediaProvider.delete(uri, null, null);
+ if (count == 1) {
+ if (children != null) {
+ for (int i = 0; i < children.length; i++) {
+ count += deleteRecursive(children[i]);
+ }
+ }
+ }
+ return count;
+ }
+
private int deleteFile(int handle) {
Log.d(TAG, "deleteFile: " + handle);
mDatabaseModified = true;
- Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
try {
- if (mMediaProvider.delete(uri, null, null) == 1) {
+ if (deleteRecursive(handle) > 0) {
return MtpConstants.RESPONSE_OK;
} else {
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 3d3bd62..6332b4e 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -21,6 +21,8 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
+#include <sys/stat.h>
+#include <dirent.h>
#include <cutils/properties.h>
@@ -686,21 +688,77 @@
return result;
}
+static void deleteRecursive(const char* path) {
+ char pathbuf[PATH_MAX];
+ int pathLength = strlen(path);
+ if (pathLength >= sizeof(pathbuf) - 1) {
+ LOGE("path too long: %s\n", path);
+ }
+ strcpy(pathbuf, path);
+ if (pathbuf[pathLength - 1] != '/') {
+ pathbuf[pathLength++] = '/';
+ }
+ char* fileSpot = pathbuf + pathLength;
+ int pathRemaining = sizeof(pathbuf) - pathLength - 1;
+
+ DIR* dir = opendir(path);
+ if (!dir) {
+ LOGE("opendir %s failed: %s", path, strerror(errno));
+ return;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dir))) {
+ const char* name = entry->d_name;
+
+ // ignore "." and ".."
+ if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
+ continue;
+ }
+
+ int nameLength = strlen(name);
+ if (nameLength > pathRemaining) {
+ LOGE("path %s/%s too long\n", path, name);
+ continue;
+ }
+ strcpy(fileSpot, name);
+
+ int type = entry->d_type;
+ if (entry->d_type == DT_DIR) {
+ deleteRecursive(pathbuf);
+ rmdir(pathbuf);
+ } else {
+ unlink(pathbuf);
+ }
+ }
+}
+
+static void deletePath(const char* path) {
+ struct stat statbuf;
+ if (stat(path, &statbuf) == 0) {
+ if (S_ISDIR(statbuf.st_mode)) {
+ deleteRecursive(path);
+ rmdir(path);
+ } else {
+ unlink(path);
+ }
+ } else {
+ LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
+ }
+}
+
MtpResponseCode MtpServer::doDeleteObject() {
MtpObjectHandle handle = mRequest.getParameter(1);
- MtpObjectFormat format = mRequest.getParameter(1);
+ MtpObjectFormat format = mRequest.getParameter(2);
// FIXME - support deleting all objects if handle is 0xFFFFFFFF
// FIXME - implement deleting objects by format
- // FIXME - handle non-empty directories
MtpString filePath;
int64_t fileLength;
int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
if (result == MTP_RESPONSE_OK) {
LOGV("deleting %s", (const char *)filePath);
- // one of these should work
- rmdir((const char *)filePath);
- unlink((const char *)filePath);
+ deletePath((const char *)filePath);
return mDatabase->deleteFile(handle);
} else {
return result;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index b4e0d3a..5dedcc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -718,7 +718,6 @@
iconId = sWifiSignalImages[mLastWifiInetConnectivityState]
[mLastWifiSignalLevel];
}
-
mService.setIcon("wifi", iconId, 0);
// Show the icon since wi-fi is connected
mService.setIconVisibility("wifi", true);
@@ -1101,7 +1100,7 @@
int iconId;
final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
int newSignalLevel = WifiManager.calculateSignalLevel(newRssi,
- sWifiSignalImages.length);
+ sWifiSignalImages[0].length);
if (newSignalLevel != mLastWifiSignalLevel) {
mLastWifiSignalLevel = newSignalLevel;
if (mIsWifiConnected) {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index e454c08..aa87f29 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -54,6 +54,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;
@@ -504,7 +505,7 @@
parseLeftoverJournals();
// Power management
- mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup");
+ mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
// Start the backup passes going
setBackupEnabled(areEnabled);
@@ -1363,6 +1364,7 @@
? IApplicationThread.BACKUP_MODE_FULL
: IApplicationThread.BACKUP_MODE_INCREMENTAL;
try {
+ mWakelock.setWorkSource(new WorkSource(request.appInfo.uid));
agent = bindToAgentSynchronous(request.appInfo, mode);
if (agent != null) {
int result = processOneBackup(request, agent, transport);
@@ -1378,6 +1380,8 @@
}
}
+ mWakelock.setWorkSource(null);
+
return BackupConstants.TRANSPORT_OK;
}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index a343c59..8452a9f 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -52,6 +52,7 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.WorkSource;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
@@ -157,6 +158,12 @@
private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
new HashMap<String,ArrayList<UpdateRecord>>();
+ /**
+ * Temporary filled in when computing min time for a provider. Access is
+ * protected by global lock mLock.
+ */
+ private final WorkSource mTmpWorkSource = new WorkSource();
+
// Proximity listeners
private Receiver mProximityReceiver = null;
private ILocationListener mProximityListener = null;
@@ -913,7 +920,7 @@
if (enabled) {
p.enable();
if (listeners > 0) {
- p.setMinTime(getMinTimeLocked(provider));
+ p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource);
p.enableLocationTracking(true);
}
} else {
@@ -925,9 +932,21 @@
private long getMinTimeLocked(String provider) {
long minTime = Long.MAX_VALUE;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
+ mTmpWorkSource.clear();
if (records != null) {
for (int i=records.size()-1; i>=0; i--) {
- minTime = Math.min(minTime, records.get(i).mMinTime);
+ UpdateRecord ur = records.get(i);
+ long curTime = ur.mMinTime;
+ if (curTime < minTime) {
+ minTime = curTime;
+ }
+ }
+ long inclTime = (minTime*3)/2;
+ for (int i=records.size()-1; i>=0; i--) {
+ UpdateRecord ur = records.get(i);
+ if (ur.mMinTime <= inclTime) {
+ mTmpWorkSource.add(ur.mUid);
+ }
}
}
return minTime;
@@ -1125,7 +1144,7 @@
boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
if (isProviderEnabled) {
long minTimeForProvider = getMinTimeLocked(provider);
- p.setMinTime(minTimeForProvider);
+ p.setMinTime(minTimeForProvider, mTmpWorkSource);
// try requesting single shot if singleShot is true, and fall back to
// regular location tracking if requestSingleShotFix() is not supported
if (!singleShot || !p.requestSingleShotFix()) {
@@ -1223,7 +1242,7 @@
LocationProviderInterface p = mProvidersByName.get(provider);
if (p != null) {
if (hasOtherListener) {
- p.setMinTime(getMinTimeLocked(provider));
+ p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource);
} else {
p.enableLocationTracking(false);
}
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 2e32e60..4d68b52 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -50,6 +50,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.provider.Settings.SettingNotFoundException;
import android.provider.Settings;
import android.util.EventLog;
@@ -310,7 +311,7 @@
long ident = Binder.clearCallingIdentity();
try {
PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken,
- MY_UID, MY_PID, mTag);
+ MY_UID, MY_PID, mTag, null);
mHeld = true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -607,6 +608,7 @@
final int uid;
final int pid;
final int monitorType;
+ WorkSource ws;
boolean activated = true;
int minState;
}
@@ -630,35 +632,74 @@
|| n == PowerManager.SCREEN_DIM_WAKE_LOCK;
}
- public void acquireWakeLock(int flags, IBinder lock, String tag) {
+ void enforceWakeSourcePermission(int uid, int pid) {
+ if (uid == Process.myUid()) {
+ return;
+ }
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
+ pid, uid, null);
+ }
+
+ public void acquireWakeLock(int flags, IBinder lock, String tag, WorkSource ws) {
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
if (uid != Process.myUid()) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
}
+ if (ws != null) {
+ enforceWakeSourcePermission(uid, pid);
+ }
long ident = Binder.clearCallingIdentity();
try {
synchronized (mLocks) {
- acquireWakeLockLocked(flags, lock, uid, pid, tag);
+ acquireWakeLockLocked(flags, lock, uid, pid, tag, ws);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag) {
- int acquireUid = -1;
- int acquirePid = -1;
- String acquireName = null;
- int acquireType = -1;
+ void noteStartWakeLocked(WakeLock wl, WorkSource ws) {
+ try {
+ if (ws != null) {
+ mBatteryStats.noteStartWakelockFromSource(ws, wl.pid, wl.tag,
+ wl.monitorType);
+ } else {
+ mBatteryStats.noteStartWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType);
+ }
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }
+ void noteStopWakeLocked(WakeLock wl, WorkSource ws) {
+ try {
+ if (ws != null) {
+ mBatteryStats.noteStopWakelockFromSource(ws, wl.pid, wl.tag,
+ wl.monitorType);
+ } else {
+ mBatteryStats.noteStopWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType);
+ }
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }
+
+ public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag,
+ WorkSource ws) {
if (mSpew) {
Slog.d(TAG, "acquireWakeLock flags=0x" + Integer.toHexString(flags) + " tag=" + tag);
}
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+
int index = mLocks.getIndex(lock);
WakeLock wl;
boolean newlock;
+ boolean diffsource;
+ WorkSource oldsource;
if (index < 0) {
wl = new WakeLock(flags, lock, tag, uid, pid);
switch (wl.flags & LOCK_MASK)
@@ -687,10 +728,31 @@
return;
}
mLocks.addLock(wl);
+ if (ws != null) {
+ wl.ws = new WorkSource(ws);
+ }
newlock = true;
+ diffsource = false;
+ oldsource = null;
} else {
wl = mLocks.get(index);
newlock = false;
+ oldsource = wl.ws;
+ if (oldsource != null) {
+ if (ws == null) {
+ wl.ws = null;
+ diffsource = true;
+ } else {
+ diffsource = oldsource.diff(ws);
+ }
+ } else if (ws != null) {
+ diffsource = true;
+ } else {
+ diffsource = false;
+ }
+ if (diffsource) {
+ wl.ws = new WorkSource(ws);
+ }
}
if (isScreenLock(flags)) {
// if this causes a wakeup, we reactivate all of the locks and
@@ -731,19 +793,41 @@
enableProximityLockLocked();
}
}
- if (newlock) {
- acquireUid = wl.uid;
- acquirePid = wl.pid;
- acquireName = wl.tag;
- acquireType = wl.monitorType;
- }
- if (acquireType >= 0) {
- try {
- mBatteryStats.noteStartWakelock(acquireUid, acquirePid, acquireName, acquireType);
- } catch (RemoteException e) {
- // Ignore
+ if (diffsource) {
+ // If the lock sources have changed, need to first release the
+ // old ones.
+ noteStopWakeLocked(wl, oldsource);
+ }
+ if (newlock || diffsource) {
+ noteStartWakeLocked(wl, ws);
+ }
+ }
+
+ public void updateWakeLockWorkSource(IBinder lock, WorkSource ws) {
+ int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+ if (ws != null) {
+ enforceWakeSourcePermission(uid, pid);
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLocks) {
+ int index = mLocks.getIndex(lock);
+ if (index < 0) {
+ throw new IllegalArgumentException("Wake lock not active");
+ }
+ WakeLock wl = mLocks.get(index);
+ WorkSource oldsource = wl.ws;
+ wl.ws = ws != null ? new WorkSource(ws) : null;
+ noteStopWakeLocked(wl, oldsource);
+ noteStartWakeLocked(wl, ws);
}
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -759,11 +843,6 @@
}
private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) {
- int releaseUid;
- int releasePid;
- String releaseName;
- int releaseType;
-
WakeLock wl = mLocks.removeLock(lock);
if (wl == null) {
return;
@@ -804,17 +883,11 @@
}
// Unlink the lock from the binder.
wl.binder.unlinkToDeath(wl, 0);
- releaseUid = wl.uid;
- releasePid = wl.pid;
- releaseName = wl.tag;
- releaseType = wl.monitorType;
- if (releaseType >= 0) {
+ if (wl.monitorType >= 0) {
long origId = Binder.clearCallingIdentity();
try {
- mBatteryStats.noteStopWakelock(releaseUid, releasePid, releaseName, releaseType);
- } catch (RemoteException e) {
- // Ignore
+ noteStopWakeLocked(wl, wl.ws);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index 2e7e3e1..f0b5955 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -29,6 +29,7 @@
import android.os.IBinder;
import android.os.Binder;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.util.Slog;
import java.util.LinkedList;
@@ -39,6 +40,7 @@
private final LinkedList<Vibration> mVibrations;
private Vibration mCurrentVibration;
+ private final WorkSource mTmpWorkSource = new WorkSource();
private class Vibration implements IBinder.DeathRecipient {
private final IBinder mToken;
@@ -46,22 +48,24 @@
private final long mStartTime;
private final long[] mPattern;
private final int mRepeat;
+ private final int mUid;
- Vibration(IBinder token, long millis) {
- this(token, millis, null, 0);
+ Vibration(IBinder token, long millis, int uid) {
+ this(token, millis, null, 0, uid);
}
- Vibration(IBinder token, long[] pattern, int repeat) {
- this(token, 0, pattern, repeat);
+ Vibration(IBinder token, long[] pattern, int repeat, int uid) {
+ this(token, 0, pattern, repeat, uid);
}
private Vibration(IBinder token, long millis, long[] pattern,
- int repeat) {
+ int repeat, int uid) {
mToken = token;
mTimeout = millis;
mStartTime = SystemClock.uptimeMillis();
mPattern = pattern;
mRepeat = repeat;
+ mUid = uid;
}
public void binderDied() {
@@ -98,7 +102,7 @@
mContext = context;
PowerManager pm = (PowerManager)context.getSystemService(
Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
mWakeLock.setReferenceCounted(true);
mVibrations = new LinkedList<Vibration>();
@@ -113,6 +117,7 @@
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
}
+ int uid = Binder.getCallingUid();
// We're running in the system server so we cannot crash. Check for a
// timeout of 0 or negative. This will ensure that a vibration has
// either a timeout of > 0 or a non-null pattern.
@@ -122,7 +127,7 @@
// longer than milliseconds.
return;
}
- Vibration vib = new Vibration(token, milliseconds);
+ Vibration vib = new Vibration(token, milliseconds, uid);
synchronized (mVibrations) {
removeVibrationLocked(token);
doCancelVibrateLocked();
@@ -146,6 +151,7 @@
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
}
+ int uid = Binder.getCallingUid();
// so wakelock calls will succeed
long identity = Binder.clearCallingIdentity();
try {
@@ -165,7 +171,7 @@
return;
}
- Vibration vib = new Vibration(token, pattern, repeat);
+ Vibration vib = new Vibration(token, pattern, repeat, uid);
try {
token.linkToDeath(vib, 0);
} catch (RemoteException e) {
@@ -280,6 +286,8 @@
VibrateThread(Vibration vib) {
mVibration = vib;
+ mTmpWorkSource.set(vib.mUid);
+ mWakeLock.setWorkSource(mTmpWorkSource);
mWakeLock.acquire();
}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 6ecc511..e8502fa 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -50,6 +50,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.WorkSource;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
@@ -1035,8 +1036,8 @@
}
private class WifiLock extends DeathRecipient {
- WifiLock(int lockMode, String tag, IBinder binder) {
- super(lockMode, tag, binder);
+ WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
+ super(lockMode, tag, binder, ws);
}
public void binderDied() {
@@ -1106,33 +1107,70 @@
}
}
- public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
+ void enforceWakeSourcePermission(int uid, int pid) {
+ if (uid == android.os.Process.myUid()) {
+ return;
+ }
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
+ pid, uid, null);
+ }
+
+ public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
return false;
}
- WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
+ if (ws != null) {
+ enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid());
+ }
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+ if (ws == null) {
+ ws = new WorkSource(Binder.getCallingUid());
+ }
+ WifiLock wifiLock = new WifiLock(lockMode, tag, binder, ws);
synchronized (mLocks) {
return acquireWifiLockLocked(wifiLock);
}
}
+ private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException {
+ switch(wifiLock.mMode) {
+ case WifiManager.WIFI_MODE_FULL:
+ mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
+ break;
+ case WifiManager.WIFI_MODE_SCAN_ONLY:
+ mBatteryStats.noteScanWifiLockAcquiredFromSource(wifiLock.mWorkSource);
+ break;
+ }
+ }
+
+ private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException {
+ switch(wifiLock.mMode) {
+ case WifiManager.WIFI_MODE_FULL:
+ mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
+ break;
+ case WifiManager.WIFI_MODE_SCAN_ONLY:
+ mBatteryStats.noteScanWifiLockReleasedFromSource(wifiLock.mWorkSource);
+ break;
+ }
+ }
+
private boolean acquireWifiLockLocked(WifiLock wifiLock) {
Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
mLocks.addLock(wifiLock);
- int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
+ noteAcquireWifiLock(wifiLock);
switch(wifiLock.mMode) {
case WifiManager.WIFI_MODE_FULL:
++mFullLocksAcquired;
- mBatteryStats.noteFullWifiLockAcquired(uid);
break;
case WifiManager.WIFI_MODE_SCAN_ONLY:
++mScanLocksAcquired;
- mBatteryStats.noteScanWifiLockAcquired(uid);
break;
}
} catch (RemoteException e) {
@@ -1144,6 +1182,33 @@
return true;
}
+ public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
+ int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+ if (ws != null) {
+ enforceWakeSourcePermission(uid, pid);
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLocks) {
+ int index = mLocks.findLockByBinder(lock);
+ if (index < 0) {
+ throw new IllegalArgumentException("Wifi lock not active");
+ }
+ WifiLock wl = mLocks.mList.get(index);
+ noteReleaseWifiLock(wl);
+ wl.mWorkSource = ws != null ? new WorkSource(ws) : new WorkSource(uid);
+ noteAcquireWifiLock(wl);
+ }
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
public boolean releaseWifiLock(IBinder lock) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
synchronized (mLocks) {
@@ -1161,17 +1226,15 @@
hadLock = (wifiLock != null);
if (hadLock) {
- int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
+ noteAcquireWifiLock(wifiLock);
switch(wifiLock.mMode) {
case WifiManager.WIFI_MODE_FULL:
++mFullLocksReleased;
- mBatteryStats.noteFullWifiLockReleased(uid);
break;
case WifiManager.WIFI_MODE_SCAN_ONLY:
++mScanLocksReleased;
- mBatteryStats.noteScanWifiLockReleased(uid);
break;
}
} catch (RemoteException e) {
@@ -1189,12 +1252,14 @@
String mTag;
int mMode;
IBinder mBinder;
+ WorkSource mWorkSource;
- DeathRecipient(int mode, String tag, IBinder binder) {
+ DeathRecipient(int mode, String tag, IBinder binder, WorkSource ws) {
super();
mTag = tag;
mMode = mode;
mBinder = binder;
+ mWorkSource = ws;
try {
mBinder.linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -1209,7 +1274,7 @@
private class Multicaster extends DeathRecipient {
Multicaster(String tag, IBinder binder) {
- super(Binder.getCallingUid(), tag, binder);
+ super(Binder.getCallingUid(), tag, binder, null);
}
public void binderDied() {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 73c1790..5432890 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6021,12 +6021,18 @@
finisher = new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
- boolean sticky)
- throws RemoteException {
- synchronized (ActivityManagerService.this) {
- mDidUpdate = true;
- }
- systemReady(goingCallback);
+ boolean sticky) {
+ // The raw IIntentReceiver interface is called
+ // with the AM lock held, so redispatch to
+ // execute our code without the lock.
+ mHandler.post(new Runnable() {
+ public void run() {
+ synchronized (ActivityManagerService.this) {
+ mDidUpdate = true;
+ }
+ systemReady(goingCallback);
+ }
+ });
}
};
}
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index a0c21dd..4fc8020 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1104,7 +1104,7 @@
// Okay we are now going to start a switch, to 'next'. We may first
// have to pause the current activity, but this is an important point
// where we have decided to go to 'next' so keep track of that.
- if (mLastStartedActivity != null) {
+ if (mLastStartedActivity != null && !mLastStartedActivity.finishing) {
long now = SystemClock.uptimeMillis();
final boolean inTime = mLastStartedActivity.startTime != 0
&& (mLastStartedActivity.startTime + START_WARN_TIME) >= now;
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 7314e04..bb40967 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -23,6 +23,7 @@
import android.os.Parcel;
import android.os.Process;
import android.os.ServiceManager;
+import android.os.WorkSource;
import android.telephony.SignalStrength;
import android.util.Slog;
@@ -107,6 +108,20 @@
}
}
+ public void noteStartWakelockFromSource(WorkSource ws, int pid, String name, int type) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteStartWakeFromSourceLocked(ws, pid, name, type);
+ }
+ }
+
+ public void noteStopWakelockFromSource(WorkSource ws, int pid, String name, int type) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteStopWakeFromSourceLocked(ws, pid, name, type);
+ }
+ }
+
public void noteStartSensor(int uid, int sensor) {
enforceCallingPermission();
synchronized (mStats) {
@@ -317,6 +332,48 @@
}
}
+ public void noteFullWifiLockAcquiredFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteFullWifiLockAcquiredFromSourceLocked(ws);
+ }
+ }
+
+ public void noteFullWifiLockReleasedFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteFullWifiLockReleasedFromSourceLocked(ws);
+ }
+ }
+
+ public void noteScanWifiLockAcquiredFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteScanWifiLockAcquiredFromSourceLocked(ws);
+ }
+ }
+
+ public void noteScanWifiLockReleasedFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteScanWifiLockReleasedFromSourceLocked(ws);
+ }
+ }
+
+ public void noteWifiMulticastEnabledFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteWifiMulticastEnabledFromSourceLocked(ws);
+ }
+ }
+
+ public void noteWifiMulticastDisabledFromSource(WorkSource ws) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteWifiMulticastDisabledFromSourceLocked(ws);
+ }
+ }
+
public boolean isOnBattery() {
return mStats.isOnBattery();
}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index c1165c7..3bf6ee4 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -44,6 +44,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.provider.Settings;
import android.util.Log;
import android.util.SparseIntArray;
@@ -736,7 +737,7 @@
startNavigating(true);
}
- public void setMinTime(long minTime) {
+ public void setMinTime(long minTime, WorkSource ws) {
if (DEBUG) Log.d(TAG, "setMinTime " + minTime);
if (minTime >= 0) {
@@ -779,7 +780,7 @@
public void addListener(int uid) {
synchronized (mWakeLock) {
mPendingListenerMessages++;
- mWakeLock.acquire();
+ mWakeLock.acquire();
Message m = Message.obtain(mHandler, ADD_LISTENER);
m.arg1 = uid;
mHandler.sendMessage(m);
diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
index 084ab81..858a582 100644
--- a/services/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -20,6 +20,7 @@
import android.location.Location;
import android.net.NetworkInfo;
import android.os.Bundle;
+import android.os.WorkSource;
/**
* Location Manager's interface for location providers.
@@ -47,7 +48,7 @@
/* returns false if single shot is not supported */
boolean requestSingleShotFix();
String getInternalState();
- void setMinTime(long minTime);
+ void setMinTime(long minTime, WorkSource ws);
void updateNetworkState(int state, NetworkInfo info);
void updateLocation(Location location);
boolean sendExtraCommand(String command, Bundle extras);
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index 24d7737..7dc9920 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -29,6 +29,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.util.Log;
import com.android.internal.location.DummyLocationProvider;
@@ -52,6 +53,7 @@
private boolean mLocationTracking = false;
private boolean mEnabled = false;
private long mMinTime = -1;
+ private WorkSource mMinTimeSource = new WorkSource();
private int mNetworkState;
private NetworkInfo mNetworkInfo;
@@ -122,7 +124,7 @@
provider.enableLocationTracking(true);
}
if (mMinTime >= 0) {
- provider.setMinTime(mMinTime);
+ provider.setMinTime(mMinTime, mMinTimeSource);
}
if (mNetworkInfo != null) {
provider.updateNetworkState(mNetworkState, mNetworkInfo);
@@ -318,6 +320,7 @@
mLocationTracking = enable;
if (!enable) {
mMinTime = -1;
+ mMinTimeSource.clear();
}
ILocationProvider provider;
synchronized (mServiceConnection) {
@@ -339,15 +342,16 @@
return mMinTime;
}
- public void setMinTime(long minTime) {
- mMinTime = minTime;
+ public void setMinTime(long minTime, WorkSource ws) {
+ mMinTime = minTime;
+ mMinTimeSource.set(ws);
ILocationProvider provider;
synchronized (mServiceConnection) {
provider = mProvider;
}
if (provider != null) {
try {
- provider.setMinTime(minTime);
+ provider.setMinTime(minTime, ws);
} catch (RemoteException e) {
}
}
diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
index 01b34b7..09d799f 100644
--- a/services/java/com/android/server/location/MockProvider.java
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -23,6 +23,7 @@
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.WorkSource;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -201,7 +202,7 @@
return false;
}
- public void setMinTime(long minTime) {
+ public void setMinTime(long minTime, WorkSource ws) {
}
public void updateNetworkState(int state, NetworkInfo info) {
diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
index 7fc93f8..ea0d1b0 100644
--- a/services/java/com/android/server/location/PassiveProvider.java
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -24,6 +24,7 @@
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.RemoteException;
+import android.os.WorkSource;
import android.util.Log;
/**
@@ -123,7 +124,7 @@
return false;
}
- public void setMinTime(long minTime) {
+ public void setMinTime(long minTime, WorkSource ws) {
}
public void updateNetworkState(int state, NetworkInfo info) {
diff --git a/tests/DumpRenderTree2/AndroidManifest.xml b/tests/DumpRenderTree2/AndroidManifest.xml
index b4dc190..5233677 100644
--- a/tests/DumpRenderTree2/AndroidManifest.xml
+++ b/tests/DumpRenderTree2/AndroidManifest.xml
@@ -40,6 +40,7 @@
</activity>
<activity android:name=".LayoutTestsExecutor"
+ android:theme="@style/WhiteBackground"
android:label="Layout tests' executor"
android:process=":executor">
</activity>
diff --git a/tests/DumpRenderTree2/assets/run_layout_tests.py b/tests/DumpRenderTree2/assets/run_layout_tests.py
index bd6e457..d5bf8b3 100755
--- a/tests/DumpRenderTree2/assets/run_layout_tests.py
+++ b/tests/DumpRenderTree2/assets/run_layout_tests.py
@@ -3,34 +3,39 @@
"""Run layout tests on the device.
It runs the specified tests on the device, downloads the summaries to the temporary directory
- and opens html details in the default browser.
+ and optionally shows the detailed results the host's default browser.
Usage:
- run_layout_tests.py PATH
+ run_layout_tests.py --show-results-in-browser test-relative-path
"""
-import sys
-import os
-import subprocess
import logging
-import webbrowser
+import optparse
+import os
+import sys
+import subprocess
import tempfile
+import webbrowser
#TODO: These should not be hardcoded
RESULTS_ABSOLUTE_PATH = "/sdcard/layout-test-results/"
DETAILS_HTML = "details.html"
SUMMARY_TXT = "summary.txt"
-def main():
- if len(sys.argv) > 1:
- path = sys.argv[1]
+def main(options, args):
+ if args:
+ path = " ".join(args);
else:
- path = ""
+ path = "";
logging.basicConfig(level=logging.INFO, format='%(message)s')
tmpdir = tempfile.gettempdir()
+ # Restart the server
+ cmd = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "run_apache2.py") + " restart"
+ os.system(cmd);
+
# Run the tests in path
cmd = "adb shell am instrument "
cmd += "-e class com.android.dumprendertree2.scriptsupport.Starter#startLayoutTests "
@@ -61,7 +66,12 @@
logging.info("")
# Open the browser with summary
- webbrowser.open(details_html_tmp_path)
+ if options.show_results_in_browser != "false":
+ webbrowser.open(details_html_tmp_path)
if __name__ == "__main__":
- main();
+ option_parser = optparse.OptionParser(usage="Usage: %prog [options] test-relative-path")
+ option_parser.add_option("", "--show-results-in-browser", default="true",
+ help="Show the results the host's default web browser, default=true")
+ options, args = option_parser.parse_args();
+ main(options, args);
diff --git a/tests/DumpRenderTree2/res/values/style.xml b/tests/DumpRenderTree2/res/values/style.xml
new file mode 100644
index 0000000..35f3419
--- /dev/null
+++ b/tests/DumpRenderTree2/res/values/style.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2010 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<resources>
+ <style name="WhiteBackground">
+ <item name="android:background">@android:color/white</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
index a82037e..ec8409a 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
@@ -35,12 +35,9 @@
mLayoutTestsExecutor = layoutTestsExecutor;
}
- public void waitUntilDone() {
- mLayoutTestsExecutor.waitUntilDone();
- }
-
- public void notifyDone() {
- mLayoutTestsExecutor.notifyDone();
+ public void clearAllDatabases() {
+ Log.i(LOG_TAG, "clearAllDatabases() called");
+ WebStorage.getInstance().deleteAllData();
}
public void dumpAsText() {
@@ -55,19 +52,27 @@
mLayoutTestsExecutor.dumpChildFramesAsText();
}
- public void clearAllDatabases() {
- Log.i(LOG_TAG, "clearAllDatabases() called");
- WebStorage.getInstance().deleteAllData();
+ public void dumpDatabaseCallbacks() {
+ mLayoutTestsExecutor.dumpDatabaseCallbacks();
+ }
+
+ public void notifyDone() {
+ mLayoutTestsExecutor.notifyDone();
+ }
+
+ public void overridePreference(String key, boolean value) {
+ mLayoutTestsExecutor.overridePreference(key, value);
+ }
+
+ public void setAppCacheMaximumSize(long size) {
+ Log.i(LOG_TAG, "setAppCacheMaximumSize() called with: " + size);
+ WebStorage.getInstance().setAppCacheMaximumSize(size);
}
public void setCanOpenWindows() {
mLayoutTestsExecutor.setCanOpenWindows();
}
- public void dumpDatabaseCallbacks() {
- mLayoutTestsExecutor.dumpDatabaseCallbacks();
- }
-
public void setDatabaseQuota(long quota) {
/** TODO: Reset this before every test! */
Log.i(LOG_TAG, "setDatabaseQuota() called with: " + quota);
@@ -79,21 +84,6 @@
mLayoutTestsExecutor.setGeolocationPermission(allow);
}
- public void overridePreference(String key, boolean value) {
- mLayoutTestsExecutor.overridePreference(key, value);
- }
-
- public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
- Log.i(LOG_TAG, "setMockGeolocationPosition(): " + "latitude=" + latitude +
- " longitude=" + longitude + " accuracy=" + accuracy);
- MockGeolocation.getInstance().setPosition(latitude, longitude, accuracy);
- }
-
- public void setMockGeolocationError(int code, String message) {
- Log.i(LOG_TAG, "setMockGeolocationError(): " + "code=" + code + " message=" + message);
- MockGeolocation.getInstance().setError(code, message);
- }
-
public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
// Configuration is in WebKit, so stay on WebCore thread, but go via LayoutTestsExecutor
@@ -104,4 +94,19 @@
mLayoutTestsExecutor.setMockDeviceOrientation(
canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
}
-}
\ No newline at end of file
+
+ public void setMockGeolocationError(int code, String message) {
+ Log.i(LOG_TAG, "setMockGeolocationError(): " + "code=" + code + " message=" + message);
+ MockGeolocation.getInstance().setError(code, message);
+ }
+
+ public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
+ Log.i(LOG_TAG, "setMockGeolocationPosition(): " + "latitude=" + latitude +
+ " longitude=" + longitude + " accuracy=" + accuracy);
+ MockGeolocation.getInstance().setPosition(latitude, longitude, accuracy);
+ }
+
+ public void waitUntilDone() {
+ mLayoutTestsExecutor.waitUntilDone();
+ }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index c997911..4c7124b 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -519,24 +519,12 @@
assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
switch (msg.what) {
- case MSG_WAIT_UNTIL_DONE:
- mCurrentState = CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST;
- break;
-
- case MSG_NOTIFY_DONE:
- if (mCurrentState == CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST) {
- onTestFinished();
- }
- break;
-
case MSG_DUMP_AS_TEXT:
if (mCurrentResult == null) {
mCurrentResult = new TextResult(mCurrentTestRelativePath);
}
-
assert mCurrentResult instanceof TextResult
: "mCurrentResult instanceof" + mCurrentResult.getClass().getName();
-
break;
case MSG_DUMP_CHILD_FRAMES_AS_TEXT:
@@ -551,27 +539,13 @@
((TextResult)mCurrentResult).setDumpChildFramesAsText(true);
break;
- case MSG_SET_CAN_OPEN_WINDOWS:
- mCanOpenWindows = true;
- break;
-
case MSG_DUMP_DATABASE_CALLBACKS:
mDumpDatabaseCallbacks = true;
break;
- case MSG_SET_GEOLOCATION_PERMISSION:
- mIsGeolocationPermissionSet = true;
- mGeolocationPermission = msg.arg1 == 1;
-
- if (mPendingGeolocationPermissionCallbacks != null) {
- Iterator<GeolocationPermissions.Callback> iter =
- mPendingGeolocationPermissionCallbacks.keySet().iterator();
- while (iter.hasNext()) {
- GeolocationPermissions.Callback callback = iter.next();
- String origin = mPendingGeolocationPermissionCallbacks.get(callback);
- callback.invoke(origin, mGeolocationPermission, false);
- }
- mPendingGeolocationPermissionCallbacks = null;
+ case MSG_NOTIFY_DONE:
+ if (mCurrentState == CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST) {
+ onTestFinished();
}
break;
@@ -591,6 +565,30 @@
}
break;
+ case MSG_SET_CAN_OPEN_WINDOWS:
+ mCanOpenWindows = true;
+ break;
+
+ case MSG_SET_GEOLOCATION_PERMISSION:
+ mIsGeolocationPermissionSet = true;
+ mGeolocationPermission = msg.arg1 == 1;
+
+ if (mPendingGeolocationPermissionCallbacks != null) {
+ Iterator<GeolocationPermissions.Callback> iter =
+ mPendingGeolocationPermissionCallbacks.keySet().iterator();
+ while (iter.hasNext()) {
+ GeolocationPermissions.Callback callback = iter.next();
+ String origin = mPendingGeolocationPermissionCallbacks.get(callback);
+ callback.invoke(origin, mGeolocationPermission, false);
+ }
+ mPendingGeolocationPermissionCallbacks = null;
+ }
+ break;
+
+ case MSG_WAIT_UNTIL_DONE:
+ mCurrentState = CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST;
+ break;
+
default:
assert false : "msg.what=" + msg.what;
break;
@@ -605,16 +603,6 @@
mPendingGeolocationPermissionCallbacks = null;
}
- public void waitUntilDone() {
- Log.i(LOG_TAG, mCurrentTestRelativePath + ": waitUntilDone() called");
- mLayoutTestControllerHandler.sendEmptyMessage(MSG_WAIT_UNTIL_DONE);
- }
-
- public void notifyDone() {
- Log.i(LOG_TAG, mCurrentTestRelativePath + ": notifyDone() called");
- mLayoutTestControllerHandler.sendEmptyMessage(MSG_NOTIFY_DONE);
- }
-
public void dumpAsText(boolean enablePixelTest) {
Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpAsText(" + enablePixelTest + ") called");
/** TODO: Implement */
@@ -629,22 +617,14 @@
mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_CHILD_FRAMES_AS_TEXT);
}
- public void setCanOpenWindows() {
- Log.i(LOG_TAG, mCurrentTestRelativePath + ": setCanOpenWindows() called");
- mLayoutTestControllerHandler.sendEmptyMessage(MSG_SET_CAN_OPEN_WINDOWS);
- }
-
public void dumpDatabaseCallbacks() {
Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpDatabaseCallbacks() called");
mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_DATABASE_CALLBACKS);
}
- public void setGeolocationPermission(boolean allow) {
- Log.i(LOG_TAG, mCurrentTestRelativePath + ": setGeolocationPermission(" + allow +
- ") called");
- Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_GEOLOCATION_PERMISSION);
- msg.arg1 = allow ? 1 : 0;
- msg.sendToTarget();
+ public void notifyDone() {
+ Log.i(LOG_TAG, mCurrentTestRelativePath + ": notifyDone() called");
+ mLayoutTestControllerHandler.sendEmptyMessage(MSG_NOTIFY_DONE);
}
public void overridePreference(String key, boolean value) {
@@ -656,6 +636,19 @@
msg.sendToTarget();
}
+ public void setCanOpenWindows() {
+ Log.i(LOG_TAG, mCurrentTestRelativePath + ": setCanOpenWindows() called");
+ mLayoutTestControllerHandler.sendEmptyMessage(MSG_SET_CAN_OPEN_WINDOWS);
+ }
+
+ public void setGeolocationPermission(boolean allow) {
+ Log.i(LOG_TAG, mCurrentTestRelativePath + ": setGeolocationPermission(" + allow +
+ ") called");
+ Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_GEOLOCATION_PERMISSION);
+ msg.arg1 = allow ? 1 : 0;
+ msg.sendToTarget();
+ }
+
public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
Log.i(LOG_TAG, mCurrentTestRelativePath + ": setMockDeviceOrientation(" + canProvideAlpha +
@@ -664,4 +657,9 @@
mCurrentWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
canProvideGamma, gamma);
}
+
+ public void waitUntilDone() {
+ Log.i(LOG_TAG, mCurrentTestRelativePath + ": waitUntilDone() called");
+ mLayoutTestControllerHandler.sendEmptyMessage(MSG_WAIT_UNTIL_DONE);
+ }
}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
index 3763bf2..2cde296 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
@@ -258,7 +258,7 @@
StringBuilder txt = new StringBuilder();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
- txt.append(mTestsRelativePath + "\n");
+ txt.append("Path: " + mTestsRelativePath + "\n");
txt.append("Date: " + dateFormat.format(mDate) + "\n");
txt.append("Build fingerprint: " + Build.FINGERPRINT + "\n");
txt.append("WebKit version: " + getWebKitVersionFromUserAgentString() + "\n");
@@ -338,7 +338,8 @@
private void createTopSummaryTable(String webKitRevision, StringBuilder html) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
- html.append("<h1>" + mTestsRelativePath + "</h1>");
+ html.append("<h1>" + "Layout tests' results for: " +
+ (mTestsRelativePath.equals("") ? "all tests" : mTestsRelativePath) + "</h1>");
html.append("<h3>" + "Date: " + dateFormat.format(new Date()) + "</h3>");
html.append("<h3>" + "Build fingerprint: " + Build.FINGERPRINT + "</h3>");
html.append("<h3>" + "WebKit version: " + getWebKitVersionFromUserAgentString() + "</h3>");
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index d30a723..5597415 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -118,6 +118,16 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <activity
+ android:name="ThinPatchesActivity"
+ android:label="_9patchThin"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
<activity
android:name="NinePatchesActivity"
diff --git a/tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png b/tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png
new file mode 100644
index 0000000..26ee1c2
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png b/tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png
new file mode 100644
index 0000000..53e95af
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
index 5b7753e..d9a2893 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
@@ -39,6 +39,7 @@
super(c);
mLayerPaint = new Paint();
+ mLayerPaint.setAlpha(127);
mRectPaint = new Paint();
mRectPaint.setAntiAlias(true);
mRectPaint.setTextSize(24.0f);
@@ -47,28 +48,21 @@
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
-
canvas.drawRGB(128, 255, 128);
+
+ canvas.save();
+
canvas.translate(140.0f, 100.0f);
-
- mLayerPaint.setAlpha(127);
- int count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint,
- Canvas.ALL_SAVE_FLAG);
-
- mRectPaint.setColor(0x7fff0000);
- canvas.drawRect(-20.0f, -20.0f, 220.0f, 120.0f, mRectPaint);
-
- mRectPaint.setColor(0xff000000);
- canvas.drawText("This is a very long string to overlap between layers and framebuffer",
- -100.0f, 50.0f, mRectPaint);
-
- canvas.restoreToCount(count);
+ drawStuff(canvas, Canvas.ALL_SAVE_FLAG);
canvas.translate(0.0f, 200.0f);
+ drawStuff(canvas, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
- mLayerPaint.setAlpha(127);
- count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint,
- Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
+ canvas.restore();
+ }
+
+ private void drawStuff(Canvas canvas, int saveFlags) {
+ int count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint, saveFlags);
mRectPaint.setColor(0x7fff0000);
canvas.drawRect(-20.0f, -20.0f, 220.0f, 120.0f, mRectPaint);
@@ -76,7 +70,7 @@
mRectPaint.setColor(0xff000000);
canvas.drawText("This is a very long string to overlap between layers and framebuffer",
-100.0f, 50.0f, mRectPaint);
-
+
canvas.restoreToCount(count);
}
}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
new file mode 100644
index 0000000..d374c32
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ThinPatchesActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FrameLayout layout = new FrameLayout(this);
+ PatchView b = new PatchView(this);
+ b.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT));
+ layout.addView(b);
+ layout.setBackgroundColor(0xffffffff);
+
+ setContentView(layout);
+ }
+
+ private class PatchView extends View {
+ private Drawable mPatch;
+
+ public PatchView(Activity activity) {
+ super(activity);
+
+ final Resources resources = activity.getResources();
+ mPatch = resources.getDrawable(R.drawable.btn_toggle_on);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ final int width = 100;
+ final int height = 60;
+
+ final int left = (getWidth() - width) / 2;
+ final int top = (getHeight() - height) / 2;
+
+ mPatch.setBounds(left, top, left + width, top + height);
+ mPatch.draw(canvas);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1913fa0..e73bca0 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -21,6 +21,8 @@
import android.net.wifi.ScanResult;
import android.net.DhcpInfo;
+import android.os.WorkSource;
+
/**
* Interface that allows controlling and querying Wi-Fi connectivity.
*
@@ -66,7 +68,9 @@
DhcpInfo getDhcpInfo();
- boolean acquireWifiLock(IBinder lock, int lockType, String tag);
+ boolean acquireWifiLock(IBinder lock, int lockType, String tag, in WorkSource ws);
+
+ void updateWifiLockWorkSource(IBinder lock, in WorkSource ws);
boolean releaseWifiLock(IBinder lock);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 8c3ec5f..26ed878 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -23,6 +23,7 @@
import android.os.IBinder;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.WorkSource;
import java.util.List;
@@ -1050,6 +1051,7 @@
int mLockType;
private boolean mRefCounted;
private boolean mHeld;
+ private WorkSource mWorkSource;
private WifiLock(int lockType, String tag) {
mTag = tag;
@@ -1075,7 +1077,7 @@
synchronized (mBinder) {
if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
try {
- mService.acquireWifiLock(mBinder, mLockType, mTag);
+ mService.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
synchronized (WifiManager.this) {
if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
mService.releaseWifiLock(mBinder);
@@ -1147,6 +1149,32 @@
}
}
+ public void setWorkSource(WorkSource ws) {
+ synchronized (mBinder) {
+ if (ws != null && ws.size() == 0) {
+ ws = null;
+ }
+ boolean changed = true;
+ if (ws == null) {
+ mWorkSource = null;
+ } else if (mWorkSource == null) {
+ changed = mWorkSource != null;
+ mWorkSource = new WorkSource(ws);
+ } else {
+ changed = mWorkSource.diff(ws);
+ if (changed) {
+ mWorkSource.set(ws);
+ }
+ }
+ if (changed && mHeld) {
+ try {
+ mService.updateWifiLockWorkSource(mBinder, mWorkSource);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
public String toString() {
String s1, s2, s3;
synchronized (mBinder) {