Merge "Better support for rtsp (normal play-)time display. Better seek support, timeout if no packets arrive for too long." into gingerbread
diff --git a/api/current.xml b/api/current.xml
index 215a864..47a8472 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -78424,7 +78424,7 @@
type="float"
transient="false"
volatile="false"
- value="0.0010f"
+ value="0.001f"
static="true"
final="true"
deprecated="not deprecated"
@@ -224842,7 +224842,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
</parameter>
</method>
</interface>
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a3a8f09..773ff7c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1162,6 +1162,7 @@
*/
protected void onPause() {
mCalled = true;
+ QueuedWork.waitToFinish();
}
/**
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c07e3d3..084f637 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -152,7 +152,7 @@
= new ArrayList<Application>();
// set of instantiated backup agents, keyed by package name
final HashMap<String, BackupAgent> mBackupAgents = new HashMap<String, BackupAgent>();
- static final ThreadLocal sThreadLocal = new ThreadLocal();
+ static final ThreadLocal<ActivityThread> sThreadLocal = new ThreadLocal();
Instrumentation mInstrumentation;
String mInstrumentationAppDir = null;
String mInstrumentationAppPackage = null;
@@ -186,6 +186,8 @@
final GcIdler mGcIdler = new GcIdler();
boolean mGcIdlerScheduled = false;
+ static Handler sMainThreadHandler; // set once in main()
+
private static final class ActivityClientRecord {
IBinder token;
int ident;
@@ -1111,7 +1113,7 @@
}
public static final ActivityThread currentActivityThread() {
- return (ActivityThread)sThreadLocal.get();
+ return sThreadLocal.get();
}
public static final String currentPackageName() {
@@ -1780,6 +1782,8 @@
}
}
+ QueuedWork.waitToFinish();
+
try {
if (data.sync) {
if (DEBUG_BROADCAST) Slog.i(TAG,
@@ -2007,6 +2011,9 @@
data.args.setExtrasClassLoader(s.getClassLoader());
}
int res = s.onStartCommand(data.args, data.flags, data.startId);
+
+ QueuedWork.waitToFinish();
+
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 1, data.startId, res);
@@ -2035,6 +2042,9 @@
final String who = s.getClassName();
((ContextImpl) context).scheduleFinalCleanup(who, "Service");
}
+
+ QueuedWork.waitToFinish();
+
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
token, 0, 0, 0);
@@ -3598,6 +3608,9 @@
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
+ if (sMainThreadHandler == null) {
+ sMainThreadHandler = new Handler();
+ }
ActivityThread thread = new ActivityThread();
thread.attach(false);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7b35e7f..0cd1f3a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -119,9 +119,12 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.WeakHashMap;
-import java.util.Map.Entry;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicBoolean;
class ReceiverRestrictedContext extends ContextWrapper {
ReceiverRestrictedContext(Context base) {
@@ -170,8 +173,8 @@
private static ThrottleManager sThrottleManager;
private static WifiManager sWifiManager;
private static LocationManager sLocationManager;
- private static final HashMap<File, SharedPreferencesImpl> sSharedPrefs =
- new HashMap<File, SharedPreferencesImpl>();
+ private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs =
+ new HashMap<String, SharedPreferencesImpl>();
private AudioManager mAudioManager;
/*package*/ LoadedApk mPackageInfo;
@@ -210,7 +213,7 @@
private File mCacheDir;
private File mExternalFilesDir;
private File mExternalCacheDir;
-
+
private static long sInstanceCount = 0;
private static final String[] EMPTY_FILE_LIST = {};
@@ -335,15 +338,15 @@
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
SharedPreferencesImpl sp;
- File f = getSharedPrefsFile(name);
synchronized (sSharedPrefs) {
- sp = sSharedPrefs.get(f);
+ sp = sSharedPrefs.get(name);
if (sp != null && !sp.hasFileChanged()) {
//Log.i(TAG, "Returning existing prefs " + name + ": " + sp);
return sp;
}
}
-
+ File f = getSharedPrefsFile(name);
+
FileInputStream str = null;
File backup = makeBackupFile(f);
if (backup.exists()) {
@@ -355,7 +358,7 @@
if (f.exists() && !f.canRead()) {
Log.w(TAG, "Attempt to read preferences file " + f + " without permission");
}
-
+
Map map = null;
if (f.exists() && f.canRead()) {
try {
@@ -376,10 +379,10 @@
//Log.i(TAG, "Updating existing prefs " + name + " " + sp + ": " + map);
sp.replace(map);
} else {
- sp = sSharedPrefs.get(f);
+ sp = sSharedPrefs.get(name);
if (sp == null) {
sp = new SharedPreferencesImpl(f, mode, map);
- sSharedPrefs.put(f, sp);
+ sSharedPrefs.put(name, sp);
}
}
return sp;
@@ -2698,10 +2701,12 @@
private final File mFile;
private final File mBackupFile;
private final int mMode;
- private Map mMap;
- private final FileStatus mFileStatus = new FileStatus();
- private long mTimestamp;
+ private Map<String, Object> mMap; // guarded by 'this'
+ private long mTimestamp; // guarded by 'this'
+ private int mDiskWritesInFlight = 0; // guarded by 'this'
+
+ private final Object mWritingToDiskLock = new Object();
private static final Object mContent = new Object();
private WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners;
@@ -2710,22 +2715,24 @@
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
- mMap = initialContents != null ? initialContents : new HashMap();
- if (FileUtils.getFileStatus(file.getPath(), mFileStatus)) {
- mTimestamp = mFileStatus.mtime;
+ mMap = initialContents != null ? initialContents : new HashMap<String, Object>();
+ FileStatus stat = new FileStatus();
+ if (FileUtils.getFileStatus(file.getPath(), stat)) {
+ mTimestamp = stat.mtime;
}
mListeners = new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
}
public boolean hasFileChanged() {
+ FileStatus stat = new FileStatus();
+ if (!FileUtils.getFileStatus(mFile.getPath(), stat)) {
+ return true;
+ }
synchronized (this) {
- if (!FileUtils.getFileStatus(mFile.getPath(), mFileStatus)) {
- return true;
- }
- return mTimestamp != mFileStatus.mtime;
+ return mTimestamp != stat.mtime;
}
}
-
+
public void replace(Map newContents) {
if (newContents != null) {
synchronized (this) {
@@ -2733,7 +2740,7 @@
}
}
}
-
+
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
synchronized(this) {
mListeners.put(listener, mContent);
@@ -2749,7 +2756,7 @@
public Map<String, ?> getAll() {
synchronized(this) {
//noinspection unchecked
- return new HashMap(mMap);
+ return new HashMap<String, Object>(mMap);
}
}
@@ -2768,7 +2775,7 @@
}
public long getLong(String key, long defValue) {
synchronized (this) {
- Long v = (Long) mMap.get(key);
+ Long v = (Long)mMap.get(key);
return v != null ? v : defValue;
}
}
@@ -2791,10 +2798,31 @@
}
}
+ public Editor edit() {
+ return new EditorImpl();
+ }
+
+ // Return value from EditorImpl#commitToMemory()
+ private static class MemoryCommitResult {
+ public boolean changesMade; // any keys different?
+ public List<String> keysModified; // may be null
+ public Set<OnSharedPreferenceChangeListener> listeners; // may be null
+ public Map<?, ?> mapToWriteToDisk;
+ public final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);
+ public volatile boolean writeToDiskResult = false;
+
+ public void setDiskWriteResult(boolean result) {
+ writeToDiskResult = result;
+ writtenToDiskLatch.countDown();
+ }
+ }
+
public final class EditorImpl implements Editor {
private final Map<String, Object> mModified = Maps.newHashMap();
private boolean mClear = false;
+ private AtomicBoolean mCommitInFlight = new AtomicBoolean(false);
+
public Editor putString(String key, String value) {
synchronized (this) {
mModified.put(key, value);
@@ -2841,30 +2869,67 @@
}
public void startCommit() {
- // TODO: implement
- commit();
+ if (!mCommitInFlight.compareAndSet(false, true)) {
+ throw new IllegalStateException("can't call startCommit() twice");
+ }
+
+ final MemoryCommitResult mcr = commitToMemory();
+ final Runnable awaitCommit = new Runnable() {
+ public void run() {
+ try {
+ mcr.writtenToDiskLatch.await();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ };
+
+ QueuedWork.add(awaitCommit);
+
+ Runnable postWriteRunnable = new Runnable() {
+ public void run() {
+ awaitCommit.run();
+ mCommitInFlight.set(false);
+ QueuedWork.remove(awaitCommit);
+ }
+ };
+
+ SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
+
+ // Okay to notify the listeners before it's hit disk
+ // because the listeners should always get the same
+ // SharedPreferences instance back, which has the
+ // changes reflected in memory.
+ notifyListeners(mcr);
}
- public boolean commit() {
- boolean returnValue;
-
- boolean hasListeners;
- boolean changesMade = false;
- List<String> keysModified = null;
- Set<OnSharedPreferenceChangeListener> listeners = null;
-
+ // Returns true if any changes were made
+ private MemoryCommitResult commitToMemory() {
+ MemoryCommitResult mcr = new MemoryCommitResult();
synchronized (SharedPreferencesImpl.this) {
- hasListeners = mListeners.size() > 0;
+ // We optimistically don't make a deep copy until
+ // a memory commit comes in when we're already
+ // writing to disk.
+ if (mDiskWritesInFlight > 0) {
+ // We can't modify our mMap as a currently
+ // in-flight write owns it. Clone it before
+ // modifying it.
+ // noinspection unchecked
+ mMap = new HashMap<String, Object>(mMap);
+ }
+ mcr.mapToWriteToDisk = mMap;
+ mDiskWritesInFlight++;
+
+ boolean hasListeners = mListeners.size() > 0;
if (hasListeners) {
- keysModified = new ArrayList<String>();
- listeners =
- new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
+ mcr.keysModified = new ArrayList<String>();
+ mcr.listeners =
+ new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
}
synchronized (this) {
if (mClear) {
if (!mMap.isEmpty()) {
- changesMade = true;
+ mcr.changesMade = true;
mMap.clear();
}
mClear = false;
@@ -2874,53 +2939,122 @@
String k = e.getKey();
Object v = e.getValue();
if (v == this) { // magic value for a removal mutation
- if (mMap.containsKey(k)) {
- mMap.remove(k);
- changesMade = true;
+ if (!mMap.containsKey(k)) {
+ continue;
}
+ mMap.remove(k);
} else {
boolean isSame = false;
if (mMap.containsKey(k)) {
Object existingValue = mMap.get(k);
- isSame = existingValue != null && existingValue.equals(v);
+ if (existingValue != null && existingValue.equals(v)) {
+ continue;
+ }
}
- if (!isSame) {
- mMap.put(k, v);
- changesMade = true;
- }
+ mMap.put(k, v);
}
+ mcr.changesMade = true;
if (hasListeners) {
- keysModified.add(k);
+ mcr.keysModified.add(k);
}
}
mModified.clear();
}
-
- returnValue = writeFileLocked(changesMade);
}
+ return mcr;
+ }
- if (hasListeners) {
- for (int i = keysModified.size() - 1; i >= 0; i--) {
- final String key = keysModified.get(i);
- for (OnSharedPreferenceChangeListener listener : listeners) {
+ public boolean commit() {
+ MemoryCommitResult mcr = commitToMemory();
+ SharedPreferencesImpl.this.enqueueDiskWrite(
+ mcr, null /* sync write on this thread okay */);
+ try {
+ mcr.writtenToDiskLatch.await();
+ } catch (InterruptedException e) {
+ return false;
+ }
+ notifyListeners(mcr);
+ return mcr.writeToDiskResult;
+ }
+
+ private void notifyListeners(final MemoryCommitResult mcr) {
+ if (mcr.listeners == null || mcr.keysModified == null ||
+ mcr.keysModified.size() == 0) {
+ return;
+ }
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ for (int i = mcr.keysModified.size() - 1; i >= 0; i--) {
+ final String key = mcr.keysModified.get(i);
+ for (OnSharedPreferenceChangeListener listener : mcr.listeners) {
if (listener != null) {
listener.onSharedPreferenceChanged(SharedPreferencesImpl.this, key);
}
}
}
+ } else {
+ // Run this function on the main thread.
+ ActivityThread.sMainThreadHandler.post(new Runnable() {
+ public void run() {
+ notifyListeners(mcr);
+ }
+ });
}
-
- return returnValue;
}
}
- public Editor edit() {
- return new EditorImpl();
+ /**
+ * Enqueue an already-committed-to-memory result to be written
+ * to disk.
+ *
+ * They will be written to disk one-at-a-time in the order
+ * that they're enqueued.
+ *
+ * @param postWriteRunnable if non-null, we're being called
+ * from startCommit() and this is the runnable to run after
+ * the write proceeds. if null (from a regular commit()),
+ * then we're allowed to do this disk write on the main
+ * thread (which in addition to reducing allocations and
+ * creating a background thread, this has the advantage that
+ * we catch them in userdebug StrictMode reports to convert
+ * them where possible to startCommit...)
+ */
+ private void enqueueDiskWrite(final MemoryCommitResult mcr,
+ final Runnable postWriteRunnable) {
+ final Runnable writeToDiskRunnable = new Runnable() {
+ public void run() {
+ synchronized (mWritingToDiskLock) {
+ writeToFile(mcr);
+ }
+ synchronized (SharedPreferencesImpl.this) {
+ mDiskWritesInFlight--;
+ }
+ if (postWriteRunnable != null) {
+ postWriteRunnable.run();
+ }
+ }
+ };
+
+ final boolean isFromSyncCommit = (postWriteRunnable == null);
+
+ // Typical #commit() path with fewer allocations, doing a write on
+ // the current thread.
+ if (isFromSyncCommit) {
+ boolean wasEmpty = false;
+ synchronized (SharedPreferencesImpl.this) {
+ wasEmpty = mDiskWritesInFlight == 1;
+ }
+ if (wasEmpty) {
+ writeToDiskRunnable.run();
+ return;
+ }
+ }
+
+ QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
}
- private FileOutputStream createFileOutputStream(File file) {
+ private static FileOutputStream createFileOutputStream(File file) {
FileOutputStream str = null;
try {
str = new FileOutputStream(file);
@@ -2943,49 +3077,56 @@
return str;
}
- private boolean writeFileLocked(boolean changesMade) {
+ // Note: must hold mWritingToDiskLock
+ private void writeToFile(MemoryCommitResult mcr) {
// Rename the current file so it may be used as a backup during the next read
if (mFile.exists()) {
- if (!changesMade) {
+ if (!mcr.changesMade) {
// If the file already exists, but no changes were
// made to the underlying map, it's wasteful to
// re-write the file. Return as if we wrote it
// out.
- return true;
+ mcr.setDiskWriteResult(true);
+ return;
}
if (!mBackupFile.exists()) {
if (!mFile.renameTo(mBackupFile)) {
Log.e(TAG, "Couldn't rename file " + mFile
+ " to backup file " + mBackupFile);
- return false;
+ mcr.setDiskWriteResult(false);
+ return;
}
} else {
mFile.delete();
}
}
-
+
// Attempt to write the file, delete the backup and return true as atomically as
// possible. If any exception occurs, delete the new file; next time we will restore
// from the backup.
try {
FileOutputStream str = createFileOutputStream(mFile);
if (str == null) {
- return false;
+ mcr.setDiskWriteResult(false);
+ return;
}
- XmlUtils.writeMapXml(mMap, str);
+ XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);
str.close();
setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
- if (FileUtils.getFileStatus(mFile.getPath(), mFileStatus)) {
- mTimestamp = mFileStatus.mtime;
+ FileStatus stat = new FileStatus();
+ if (FileUtils.getFileStatus(mFile.getPath(), stat)) {
+ synchronized (this) {
+ mTimestamp = stat.mtime;
+ }
}
-
// Writing was successful, delete the backup file if there is one.
mBackupFile.delete();
- return true;
+ mcr.setDiskWriteResult(true);
+ return;
} catch (XmlPullParserException e) {
- Log.w(TAG, "writeFileLocked: Got exception:", e);
+ Log.w(TAG, "writeToFile: Got exception:", e);
} catch (IOException e) {
- Log.w(TAG, "writeFileLocked: Got exception:", e);
+ Log.w(TAG, "writeToFile: Got exception:", e);
}
// Clean up an unsuccessfully written file
if (mFile.exists()) {
@@ -2993,7 +3134,7 @@
Log.e(TAG, "Couldn't clean up partially-written file " + mFile);
}
}
- return false;
+ mcr.setDiskWriteResult(false);
}
}
}
diff --git a/core/java/android/app/QueuedWork.java b/core/java/android/app/QueuedWork.java
new file mode 100644
index 0000000..af6bb1b
--- /dev/null
+++ b/core/java/android/app/QueuedWork.java
@@ -0,0 +1,91 @@
+/*
+ * 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.app;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Internal utility class to keep track of process-global work that's
+ * outstanding and hasn't been finished yet.
+ *
+ * This was created for writing SharedPreference edits out
+ * asynchronously so we'd have a mechanism to wait for the writes in
+ * Activity.onPause and similar places, but we may use this mechanism
+ * for other things in the future.
+ *
+ * @hide
+ */
+public class QueuedWork {
+
+ // The set of Runnables that will finish or wait on any async
+ // activities started by the application.
+ private static final ConcurrentLinkedQueue<Runnable> sPendingWorkFinishers =
+ new ConcurrentLinkedQueue<Runnable>();
+
+ private static ExecutorService sSingleThreadExecutor = null; // lazy, guarded by class
+
+ /**
+ * Returns a single-thread Executor shared by the entire process,
+ * creating it if necessary.
+ */
+ public static ExecutorService singleThreadExecutor() {
+ synchronized (QueuedWork.class) {
+ if (sSingleThreadExecutor == null) {
+ // TODO: can we give this single thread a thread name?
+ sSingleThreadExecutor = Executors.newSingleThreadExecutor();
+ }
+ return sSingleThreadExecutor;
+ }
+ }
+
+ /**
+ * Add a runnable to finish (or wait for) a deferred operation
+ * started in this context earlier. Typically finished by e.g.
+ * an Activity#onPause. Used by SharedPreferences$Editor#startCommit().
+ *
+ * Note that this doesn't actually start it running. This is just
+ * a scratch set for callers doing async work to keep updated with
+ * what's in-flight. In the common case, caller code
+ * (e.g. SharedPreferences) will pretty quickly call remove()
+ * after an add(). The only time these Runnables are run is from
+ * waitToFinish(), below.
+ */
+ public static void add(Runnable finisher) {
+ sPendingWorkFinishers.add(finisher);
+ }
+
+ public static void remove(Runnable finisher) {
+ sPendingWorkFinishers.remove(finisher);
+ }
+
+ /**
+ * Finishes or waits for async operations to complete.
+ * (e.g. SharedPreferences$Editor#startCommit writes)
+ *
+ * Is called from the Activity base class's onPause(), after
+ * BroadcastReceiver's onReceive, after Service command handling,
+ * etc. (so async work is never lost)
+ */
+ public static void waitToFinish() {
+ Runnable toFinish;
+ while ((toFinish = sPendingWorkFinishers.poll()) != null) {
+ toFinish.run();
+ }
+ }
+}
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index f1b1490..b3db2ac 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -40,7 +40,9 @@
/**
* Called when a shared preference is changed, added, or removed. This
* may be called even if a preference is set to its existing value.
- *
+ *
+ * <p>This callback will be run on your main thread.
+ *
* @param sharedPreferences The {@link SharedPreferences} that received
* the change.
* @param key The key of the preference that was changed, added, or
@@ -187,9 +189,6 @@
* <p>If you call this from an {@link android.app.Activity},
* the base class will wait for any async commits to finish in
* its {@link android.app.Activity#onPause}.</p>
- *
- * @return Returns true if the new values were successfully written
- * to persistent storage.
*/
void startCommit();
}
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 2a32e54..3b2bf1e 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -563,13 +563,13 @@
return mMessenger;
}
}
-
+
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
Handler.this.sendMessage(msg);
}
}
-
+
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 197d976..1453329 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -1195,7 +1195,7 @@
private void tryCommit(SharedPreferences.Editor editor) {
if (mPreferenceManager.shouldCommit()) {
- editor.commit();
+ editor.startCommit();
}
}
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index f00389b..abd66ae 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -1737,7 +1737,7 @@
mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
mContext.MODE_PRIVATE).edit();
editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true);
- editor.commit();
+ editor.startCommit();
}
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 05c159b..052de97 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -5161,13 +5161,10 @@
}
} else {
if (mSelectingText) {
- // tapping on selection or controls does nothing
- if (!nativeHitSelection(contentX, contentY)) {
- if (mMapTrackballToArrowKeys) { // gmail
- copySelection();
- }
- selectionDone();
+ if (nativeHitSelection(contentX, contentY)) {
+ copySelection();
}
+ selectionDone();
break;
}
if (mTouchMode == TOUCH_INIT_MODE) {
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index c77416b..46f7db4 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -260,9 +260,15 @@
/**
* Sets a drawable as the content of this ImageView.
- *
+ *
+ * <p class="note">This does Bitmap reading and decoding on the UI
+ * thread, which can cause a latency hiccup. If that's a concern,
+ * consider using {@link #setImageDrawable} or
+ * {@link #setImageBitmap} and
+ * {@link android.graphics.BitmapFactory} instead.</p>
+ *
* @param resId the resource identifier of the the drawable
- *
+ *
* @attr ref android.R.styleable#ImageView_src
*/
@android.view.RemotableViewMethod
@@ -279,7 +285,13 @@
/**
* Sets the content of this ImageView to the specified Uri.
- *
+ *
+ * <p class="note">This does Bitmap reading and decoding on the UI
+ * thread, which can cause a latency hiccup. If that's a concern,
+ * consider using {@link #setImageDrawable} or
+ * {@link #setImageBitmap} and
+ * {@link android.graphics.BitmapFactory} instead.</p>
+ *
* @param uri The Uri of an image
*/
@android.view.RemotableViewMethod
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 248f6eb..d1a14d2 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4303,6 +4303,15 @@
if (shouldAdvanceFocusOnEnter()) {
return 0;
}
+ break;
+
+ // Has to be done on key down (and not on key up) to correctly be intercepted.
+ case KeyEvent.KEYCODE_BACK:
+ if (mIsInTextSelectionMode) {
+ stopTextSelectionMode();
+ return -1;
+ }
+ break;
}
if (mInput != null) {
@@ -4456,6 +4465,7 @@
return super.onKeyUp(keyCode, event);
}
+ break;
}
if (mInput != null)
@@ -6618,9 +6628,10 @@
end = mPrevEnd;
} else {
if ((mPrevStart != mPrevEnd) && (start == end)) {
- if ((start >= mPrevStart) && (start <= mPrevEnd)) {
+ if ((start >= mPrevStart) && (start < mPrevEnd)) {
// Tapping inside the selection does nothing
Selection.setSelection((Spannable) mText, mPrevStart, mPrevEnd);
+ showContextMenu();
return;
} else {
// Tapping outside stops selection mode, if any
@@ -7221,9 +7232,6 @@
setAlphabeticShortcut('v');
}
- menu.add(0, ID_STOP_SELECTING_TEXT, 0, com.android.internal.R.string.stopSelectingText).
- setOnMenuItemClickListener(handler);
-
added = true;
} else {
/*
@@ -7272,10 +7280,12 @@
}
}
- if (canPaste()) {
+ // Paste location is too imprecise. Only allow on empty text fields.
+ if (canPaste() && textIsOnlySpaces()) {
menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
setOnMenuItemClickListener(handler).
setAlphabeticShortcut('v');
+ added = true;
}
if (isInputMethodTarget()) {
@@ -7299,6 +7309,17 @@
}
}
+ private boolean textIsOnlySpaces() {
+ final int length = mTransformed.length();
+ for (int i=0; i<length; i++) {
+ final char c = mTransformed.charAt(i);
+ final int type = Character.getType(c);
+ if (type != Character.SPACE_SEPARATOR)
+ return false;
+ }
+ return true;
+ }
+
/**
* Returns whether this text view is a current input method target. The
* default implementation just checks with {@link InputMethodManager}.
@@ -7311,7 +7332,6 @@
// Context menu entries
private static final int ID_SELECT_ALL = android.R.id.selectAll;
private static final int ID_START_SELECTING_TEXT = android.R.id.startSelectingText;
- private static final int ID_STOP_SELECTING_TEXT = android.R.id.stopSelectingText;
private static final int ID_CUT = android.R.id.cut;
private static final int ID_COPY = android.R.id.copy;
private static final int ID_PASTE = android.R.id.paste;
@@ -7358,10 +7378,6 @@
startTextSelectionMode();
return true;
- case ID_STOP_SELECTING_TEXT:
- stopTextSelectionMode();
- return true;
-
case ID_CUT:
clip.setText(mTransformed.subSequence(min, max));
((Editable) mText).delete(min, max);
@@ -7737,6 +7753,8 @@
private boolean mStartIsDragged = false;
// Starting time of the fade timer
private long mFadeOutTimerStart;
+ // Used to detect a tap (vs drag) on the controller
+ private long mOnDownTimerStart;
// The cursor controller images
private final Handle mStartHandle, mEndHandle;
// Offset between finger hot point on active cursor controller and actual cursor
@@ -7884,12 +7902,22 @@
mOffsetX = (bounds.left + bounds.right) / 2.0f - x;
mOffsetY = draggedHandle.mHotSpotVerticalPosition - y;
+ mOnDownTimerStart = event.getEventTime();
((ArrowKeyMovementMethod)mMovement).setCursorController(this);
}
}
}
break;
+ case MotionEvent.ACTION_UP:
+ int time = (int) (event.getEventTime() - mOnDownTimerStart);
+
+ if (time <= ViewConfiguration.getTapTimeout()) {
+ // A tap on the controller (not a drag) opens the contextual Copy menu
+ showContextMenu();
+ }
+ break;
+
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_POINTER_UP:
// Handle multi-point gestures. Keep min and max offset positions.
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index b7d0c67..f3b9357 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -66,16 +66,6 @@
// ----------------------------------------------------------------------------
-static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
-{
- jclass npeClazz;
-
- npeClazz = env->FindClass(exc);
- LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc);
-
- env->ThrowNew(npeClazz, msg);
-}
-
enum {
STYLE_NUM_ENTRIES = 6,
STYLE_TYPE = 0,
@@ -131,14 +121,14 @@
LOGV("openAsset in %p (Java object %p)\n", am, clazz);
- if (fileName == NULL || am == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ if (fileName == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", "fileName");
return -1;
}
if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
&& mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
- doThrow(env, "java/lang/IllegalArgumentException");
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
return -1;
}
@@ -146,7 +136,7 @@
Asset* a = am->open(fileName8, (Asset::AccessMode)mode);
if (a == NULL) {
- doThrow(env, "java/io/FileNotFoundException", fileName8);
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8);
env->ReleaseStringUTFChars(fileName, fileName8);
return -1;
}
@@ -164,7 +154,7 @@
delete a;
if (fd < 0) {
- doThrow(env, "java/io/FileNotFoundException",
+ jniThrowException(env, "java/io/FileNotFoundException",
"This file can not be opened as a file descriptor; it is probably compressed");
return NULL;
}
@@ -199,8 +189,8 @@
LOGV("openAssetFd in %p (Java object %p)\n", am, clazz);
- if (fileName == NULL || am == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ if (fileName == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", "fileName");
return NULL;
}
@@ -208,7 +198,7 @@
Asset* a = am->open(fileName8, Asset::ACCESS_RANDOM);
if (a == NULL) {
- doThrow(env, "java/io/FileNotFoundException", fileName8);
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8);
env->ReleaseStringUTFChars(fileName, fileName8);
return NULL;
}
@@ -231,14 +221,14 @@
LOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz);
- if (fileName == NULL || am == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ if (fileName == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", "fileName");
return -1;
}
if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
&& mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
- doThrow(env, "java/lang/IllegalArgumentException");
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
return -1;
}
@@ -248,7 +238,7 @@
: am->openNonAsset(fileName8, (Asset::AccessMode)mode);
if (a == NULL) {
- doThrow(env, "java/io/FileNotFoundException", fileName8);
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8);
env->ReleaseStringUTFChars(fileName, fileName8);
return -1;
}
@@ -271,8 +261,8 @@
LOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz);
- if (fileName == NULL || am == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ if (fileName == NULL ) {
+ jniThrowException(env, "java/lang/NullPointerException", "fileName");
return NULL;
}
@@ -282,7 +272,7 @@
: am->openNonAsset(fileName8, Asset::ACCESS_RANDOM);
if (a == NULL) {
- doThrow(env, "java/io/FileNotFoundException", fileName8);
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8);
env->ReleaseStringUTFChars(fileName, fileName8);
return NULL;
}
@@ -301,8 +291,8 @@
return NULL;
}
- if (fileName == NULL || am == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ if (fileName == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", "fileName");
return NULL;
}
@@ -313,7 +303,7 @@
env->ReleaseStringUTFChars(fileName, fileName8);
if (dir == NULL) {
- doThrow(env, "java/io/FileNotFoundException", fileName8);
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8);
return NULL;
}
@@ -329,7 +319,6 @@
jobjectArray array = env->NewObjectArray(dir->getFileCount(),
cls, NULL);
if (array == NULL) {
- doThrow(env, "java/lang/OutOfMemoryError");
delete dir;
return NULL;
}
@@ -338,11 +327,11 @@
const String8& name = dir->getFileName(i);
jstring str = env->NewStringUTF(name.string());
if (str == NULL) {
- doThrow(env, "java/lang/OutOfMemoryError");
delete dir;
return NULL;
}
env->SetObjectArrayElement(array, i, str);
+ env->DeleteLocalRef(str);
}
delete dir;
@@ -358,7 +347,7 @@
//printf("Destroying Asset Stream: %p\n", a);
if (a == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowException(env, "java/lang/NullPointerException", "asset");
return;
}
@@ -371,7 +360,7 @@
Asset* a = (Asset*)asset;
if (a == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowException(env, "java/lang/NullPointerException", "asset");
return -1;
}
@@ -387,7 +376,7 @@
Asset* a = (Asset*)asset;
if (a == NULL || bArray == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowException(env, "java/lang/NullPointerException", "asset");
return -1;
}
@@ -397,7 +386,7 @@
jsize bLen = env->GetArrayLength(bArray);
if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) {
- doThrow(env, "java/lang/IndexOutOfBoundsException");
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
return -1;
}
@@ -408,7 +397,7 @@
if (res > 0) return res;
if (res < 0) {
- doThrow(env, "java/io/IOException");
+ jniThrowException(env, "java/io/IOException", "");
}
return -1;
}
@@ -420,7 +409,7 @@
Asset* a = (Asset*)asset;
if (a == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowException(env, "java/lang/NullPointerException", "asset");
return -1;
}
@@ -434,7 +423,7 @@
Asset* a = (Asset*)asset;
if (a == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowException(env, "java/lang/NullPointerException", "asset");
return -1;
}
@@ -447,7 +436,7 @@
Asset* a = (Asset*)asset;
if (a == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowException(env, "java/lang/NullPointerException", "asset");
return -1;
}
@@ -458,7 +447,7 @@
jstring path)
{
if (path == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowException(env, "java/lang/NullPointerException", "path");
return JNI_FALSE;
}
@@ -490,7 +479,7 @@
jstring locale)
{
if (locale == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowException(env, "java/lang/NullPointerException", "locale");
return;
}
@@ -525,7 +514,12 @@
}
for (int i=0; i<N; i++) {
- env->SetObjectArrayElement(result, i, env->NewStringUTF(locales[i].string()));
+ jstring str = env->NewStringUTF(locales[i].string());
+ if (str == NULL) {
+ return NULL;
+ }
+ env->SetObjectArrayElement(result, i, str);
+ env->DeleteLocalRef(str);
}
return result;
@@ -576,7 +570,7 @@
jstring defPackage)
{
if (name == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowException(env, "java/lang/NullPointerException", "name");
return 0;
}
@@ -814,14 +808,10 @@
}
String8 name(am->getAssetPath((void*)cookie));
if (name.length() == 0) {
- doThrow(env, "java/lang/IndexOutOfBoundsException");
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name");
return NULL;
}
jstring str = env->NewStringUTF(name.string());
- if (str == NULL) {
- doThrow(env, "java/lang/OutOfMemoryError");
- return NULL;
- }
return str;
}
@@ -889,7 +879,7 @@
const ResTable& res(theme->getResTable());
if (tag == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowException(env, "java/lang/NullPointerException", "tag");
return;
}
@@ -917,8 +907,16 @@
jintArray outValues,
jintArray outIndices)
{
- if (themeToken == 0 || attrs == NULL || outValues == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ if (themeToken == 0) {
+ jniThrowException(env, "java/lang/NullPointerException", "theme token");
+ return JNI_FALSE;
+ }
+ if (attrs == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", "attrs");
+ return JNI_FALSE;
+ }
+ if (outValues == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", "out values");
return JNI_FALSE;
}
@@ -934,13 +932,13 @@
const jsize NI = env->GetArrayLength(attrs);
const jsize NV = env->GetArrayLength(outValues);
if (NV < (NI*STYLE_NUM_ENTRIES)) {
- doThrow(env, "java/lang/IndexOutOfBoundsException");
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
return JNI_FALSE;
}
jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
if (src == NULL) {
- doThrow(env, "java/lang/OutOfMemoryError");
+ jniThrowException(env, "java/lang/OutOfMemoryError", "");
return JNI_FALSE;
}
@@ -948,7 +946,7 @@
jint* dest = baseDest;
if (dest == NULL) {
env->ReleasePrimitiveArrayCritical(attrs, src, 0);
- doThrow(env, "java/lang/OutOfMemoryError");
+ jniThrowException(env, "java/lang/OutOfMemoryError", "");
return JNI_FALSE;
}
@@ -1152,8 +1150,16 @@
jintArray outValues,
jintArray outIndices)
{
- if (xmlParserToken == 0 || attrs == NULL || outValues == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ if (xmlParserToken == 0) {
+ jniThrowException(env, "java/lang/NullPointerException", "xmlParserToken");
+ return JNI_FALSE;
+ }
+ if (attrs == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", "attrs");
+ return JNI_FALSE;
+ }
+ if (outValues == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", "out values");
return JNI_FALSE;
}
@@ -1169,13 +1175,13 @@
const jsize NI = env->GetArrayLength(attrs);
const jsize NV = env->GetArrayLength(outValues);
if (NV < (NI*STYLE_NUM_ENTRIES)) {
- doThrow(env, "java/lang/IndexOutOfBoundsException");
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
return JNI_FALSE;
}
jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
if (src == NULL) {
- doThrow(env, "java/lang/OutOfMemoryError");
+ jniThrowException(env, "java/lang/OutOfMemoryError", "");
return JNI_FALSE;
}
@@ -1183,7 +1189,7 @@
jint* dest = baseDest;
if (dest == NULL) {
env->ReleasePrimitiveArrayCritical(attrs, src, 0);
- doThrow(env, "java/lang/OutOfMemoryError");
+ jniThrowException(env, "java/lang/OutOfMemoryError", "");
return JNI_FALSE;
}
@@ -1306,7 +1312,7 @@
jintArray outValues)
{
if (outValues == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ jniThrowException(env, "java/lang/NullPointerException", "out values");
return JNI_FALSE;
}
@@ -1324,7 +1330,7 @@
jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
jint* dest = baseDest;
if (dest == NULL) {
- doThrow(env, "java/lang/OutOfMemoryError");
+ jniThrowException(env, "java/lang/OutOfMemoryError", "");
return JNI_FALSE;
}
@@ -1399,8 +1405,8 @@
LOGV("openXmlAsset in %p (Java object %p)\n", am, clazz);
- if (fileName == NULL || am == NULL) {
- doThrow(env, "java/lang/NullPointerException");
+ if (fileName == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", "fileName");
return 0;
}
@@ -1410,7 +1416,7 @@
: am->openNonAsset(fileName8, Asset::ACCESS_BUFFER);
if (a == NULL) {
- doThrow(env, "java/io/FileNotFoundException", fileName8);
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8);
env->ReleaseStringUTFChars(fileName, fileName8);
return 0;
}
@@ -1422,7 +1428,7 @@
delete a;
if (err != NO_ERROR) {
- doThrow(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
+ jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
return 0;
}
@@ -1446,7 +1452,7 @@
jintArray array = env->NewIntArray(N * 2);
if (array == NULL) {
- doThrow(env, "java/lang/OutOfMemoryError");
+ jniThrowException(env, "java/lang/OutOfMemoryError", "");
res.unlockBag(startOfBag);
return NULL;
}
@@ -1540,13 +1546,12 @@
res.unlockBag(startOfBag);
return NULL;
}
- }
- env->SetObjectArrayElement(array, i, str);
+ env->SetObjectArrayElement(array, i, str);
- // If we have a large amount of strings in our array, we might
- // overflow the local reference table of the VM.
- if (str != NULL) {
+ // str is not NULL at that point, otherwise ExceptionCheck would have been true.
+ // If we have a large amount of strings in our array, we might
+ // overflow the local reference table of the VM.
env->DeleteLocalRef(str);
}
}
@@ -1571,7 +1576,7 @@
jintArray array = env->NewIntArray(N);
if (array == NULL) {
- doThrow(env, "java/lang/OutOfMemoryError");
+ jniThrowException(env, "java/lang/OutOfMemoryError", "");
res.unlockBag(startOfBag);
return NULL;
}
@@ -1603,7 +1608,7 @@
{
AssetManager* am = new AssetManager();
if (am == NULL) {
- doThrow(env, "java/lang/OutOfMemoryError");
+ jniThrowException(env, "java/lang/OutOfMemoryError", "");
return;
}
@@ -1637,11 +1642,6 @@
}
jstring str = env->NewStringUTF(alloc.string());
- if (str == NULL) {
- doThrow(env, "java/lang/OutOfMemoryError");
- return NULL;
- }
-
return str;
}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 52abe45..03b721c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1853,12 +1853,9 @@
<!-- Item on EditText context menu. This action is used to select all text in the edit field. -->
<string name="selectAll">Select all</string>
- <!-- Item on EditText context menu. This action is used to start selecting text in the edit field. -->
+ <!-- Item on EditText context menu. This action is used to start selecting text in the edit field. [CHAR LIMIT=20] -->
<string name="selectText">Select word</string>
- <!-- Item on EditText context menu. This action is used to stop selecting text in the edit field. -->
- <string name="stopSelectingText">Stop selecting text</string>
-
<!-- Item on EditText context menu. This action is used to cut selected the text into the clipboard. -->
<string name="cut">Cut</string>
diff --git a/docs/html/guide/topics/providers/content-providers.jd b/docs/html/guide/topics/providers/content-providers.jd
index 30f8d8c..da4e7a1 100644
--- a/docs/html/guide/topics/providers/content-providers.jd
+++ b/docs/html/guide/topics/providers/content-providers.jd
@@ -779,7 +779,7 @@
requested. Here is the general format for each type:</p></li>
<ul>
-<li><p>For a single record: {@code vnd.android.cursor.item/vnd.<em>yourcompanyname.contenttype</em}</p>
+<li><p>For a single record: {@code vnd.android.cursor.item/vnd.<em>yourcompanyname.contenttype</em>}</p>
<p>For example, a request for train record 122, like this URI,</p>
<p style="margin-left: 2em">{@code content://com.example.transportationprovider/trains/122}</p>
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index d016dfa..d689667 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -151,7 +151,6 @@
~SharedBufferBase();
status_t getStatus() const;
int32_t getIdentity() const;
- size_t getFrontBuffer() const;
String8 dump(char const* prefix) const;
protected:
@@ -226,6 +225,11 @@
inline ssize_t operator()();
};
+ struct DequeueUpdate : public UpdateBase {
+ inline DequeueUpdate(SharedBufferBase* sbb);
+ inline ssize_t operator()();
+ };
+
struct UndoDequeueUpdate : public UpdateBase {
inline UndoDequeueUpdate(SharedBufferBase* sbb);
inline ssize_t operator()();
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 4ad9f86..38b2fae 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -191,12 +191,6 @@
return stack.identity;
}
-size_t SharedBufferBase::getFrontBuffer() const
-{
- SharedBufferStack& stack( *mSharedStack );
- return size_t( stack.head );
-}
-
String8 SharedBufferBase::dump(char const* prefix) const
{
const size_t SIZE = 1024;
@@ -281,6 +275,16 @@
return NO_ERROR;
}
+SharedBufferClient::DequeueUpdate::DequeueUpdate(SharedBufferBase* sbb)
+ : UpdateBase(sbb) {
+}
+ssize_t SharedBufferClient::DequeueUpdate::operator()() {
+ if (android_atomic_dec(&stack.available) == 0) {
+ LOGW("dequeue probably called from multiple threads!");
+ }
+ return NO_ERROR;
+}
+
SharedBufferClient::UndoDequeueUpdate::UndoDequeueUpdate(SharedBufferBase* sbb)
: UpdateBase(sbb) {
}
@@ -388,12 +392,8 @@
if (err != NO_ERROR)
return ssize_t(err);
- // NOTE: 'stack.available' is part of the conditions, however
- // decrementing it, never changes any conditions, so we don't need
- // to do this as part of an update.
- if (android_atomic_dec(&stack.available) == 0) {
- LOGW("dequeue probably called from multiple threads!");
- }
+ DequeueUpdate update(this);
+ updateCondition( update );
undoDequeueTail = tail;
int dequeued = stack.index[tail];
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index 604f558..a0e01c6 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -508,8 +508,8 @@
}
if (get4LE(lfhBuf) != kLFHSignature) {
- LOGW("didn't find signature at start of lfh, offset=%ld\n",
- localHdrOffset);
+ LOGW("didn't find signature at start of lfh, offset=%ld (got 0x%08lx, expected 0x%08x)\n",
+ localHdrOffset, get4LE(lfhBuf), kLFHSignature);
return false;
}
diff --git a/services/java/com/android/server/BootReceiver.java b/services/java/com/android/server/BootReceiver.java
index f409751..d15a058 100644
--- a/services/java/com/android/server/BootReceiver.java
+++ b/services/java/com/android/server/BootReceiver.java
@@ -165,7 +165,9 @@
if (prefs != null) {
long lastTime = prefs.getLong(filename, 0);
if (lastTime == fileTime) return; // Already logged this particular file
- prefs.edit().putLong(filename, fileTime).commit();
+ // TODO: move all these SharedPreferences Editor commits
+ // outside this function to the end of logBootEvents
+ prefs.edit().putLong(filename, fileTime).startCommit();
}
Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");