diff options
26 files changed, 614 insertions, 277 deletions
diff --git a/api/current.xml b/api/current.xml index 3b90f38106fd..89af2acddc34 100644 --- a/api/current.xml +++ b/api/current.xml @@ -189783,7 +189783,7 @@ synchronized="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </method> @@ -189835,7 +189835,7 @@ synchronized="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > </method> diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 393bbba17e1d..211a2aeda5c6 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -819,7 +819,7 @@ public class SyncManager implements OnAccountsUpdateListener { } scheduleSyncOperation(new SyncOperation(operation.account, operation.syncSource, operation.authority, operation.extras, - DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS)); + DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000)); } else if (syncResult.hasSoftError()) { if (isLoggable) { Log.d(TAG, "retrying sync operation because it encountered a soft error: " diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java index 4599165b1370..3b3f9c159b5b 100644 --- a/core/java/android/content/SyncOperation.java +++ b/core/java/android/content/SyncOperation.java @@ -19,7 +19,7 @@ public class SyncOperation implements Comparable { public SyncStorageEngine.PendingOperation pendingOperation; public SyncOperation(Account account, int source, String authority, Bundle extras, - long delay) { + long delayInMs) { this.account = account; this.syncSource = source; this.authority = authority; @@ -33,12 +33,12 @@ public class SyncOperation implements Comparable { removeFalseExtra(ContentResolver.SYNC_EXTRAS_EXPEDITED); removeFalseExtra(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS); final long now = SystemClock.elapsedRealtime(); - if (delay < 0) { + if (delayInMs < 0) { this.expedited = true; this.earliestRunTime = now; } else { this.expedited = false; - this.earliestRunTime = now + delay; + this.earliestRunTime = now + delayInMs; } this.key = toKey(); } diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index d96596295be6..0de1868ebec1 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -101,6 +101,12 @@ public final class Telephony { public static final String READ = "read"; /** + * Indicates whether this message has been seen by the user. The "seen" flag will be + * used to figure out whether we need to throw up a statusbar notification or not. + */ + public static final String SEEN = "seen"; + + /** * The TP-Status value for the message, or -1 if no status has * been received */ @@ -646,6 +652,12 @@ public final class Telephony { public static final String READ = "read"; /** + * Indicates whether this message has been seen by the user. The "seen" flag will be + * used to figure out whether we need to throw up a statusbar notification or not. + */ + public static final String SEEN = "seen"; + + /** * The Message-ID of the message. * <P>Type: TEXT</P> */ @@ -1097,6 +1109,7 @@ public final class Telephony { * <P>Type: INTEGER</P> */ public static final String READ = "read"; + /** * The snippet of the latest message in the thread. * <P>Type: TEXT</P> diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index afb22acef433..9589bf369e6e 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -1557,13 +1557,17 @@ public class TextUtils { * @param reqModes The modes to be checked: may be any combination of * {@link #CAP_MODE_CHARACTERS}, {@link #CAP_MODE_WORDS}, and * {@link #CAP_MODE_SENTENCES}. - * + * * @return Returns the actual capitalization modes that can be in effect * at the current position, which is any combination of * {@link #CAP_MODE_CHARACTERS}, {@link #CAP_MODE_WORDS}, and * {@link #CAP_MODE_SENTENCES}. */ public static int getCapsMode(CharSequence cs, int off, int reqModes) { + if (off < 0) { + return 0; + } + int i; char c; int mode = 0; diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index d7f25392900d..0722699de6e6 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -320,8 +320,14 @@ public class SurfaceView extends View { * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}. */ public void setZOrderOnTop(boolean onTop) { - mWindowType = onTop ? WindowManager.LayoutParams.TYPE_APPLICATION_PANEL - : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; + if (onTop) { + mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; + // ensures the surface is placed below the IME + mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + } else { + mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; + mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + } } /** diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 3f1672af29bf..1c0d55f3392f 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -407,7 +407,8 @@ class BrowserFrame extends Handler { } } } - CacheManager.trimCacheIfNeeded(); + WebViewWorker.getHandler().sendEmptyMessage( + WebViewWorker.MSG_TRIM_CACHE); break; } diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java index 647556b5ad55..1c59c10685d8 100644 --- a/core/java/android/webkit/CacheManager.java +++ b/core/java/android/webkit/CacheManager.java @@ -25,10 +25,11 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; +import java.util.List; import java.util.Map; @@ -200,9 +201,9 @@ public final class CacheManager { // the cache database. The directory could be recreated // because the system flushed all the data/cache directories // to free up disk space. - WebViewCore.endCacheTransaction(); - mDataBase.clearCache(); - WebViewCore.startCacheTransaction(); + // delete rows in the cache database + WebViewWorker.getHandler().sendEmptyMessage( + WebViewWorker.MSG_CLEAR_CACHE); return true; } return false; @@ -223,7 +224,6 @@ public final class CacheManager { * * @param disabled true to disable the cache */ - // only called from WebCore thread static void setCacheDisabled(boolean disabled) { if (disabled == mDisabled) { return; @@ -243,7 +243,7 @@ public final class CacheManager { return mDisabled; } - // only called from WebCore thread + // only called from WebViewWorkerThread // make sure to call enableTransaction/disableTransaction in pair static boolean enableTransaction() { if (++mRefCount == 1) { @@ -253,12 +253,9 @@ public final class CacheManager { return false; } - // only called from WebCore thread + // only called from WebViewWorkerThread // make sure to call enableTransaction/disableTransaction in pair static boolean disableTransaction() { - if (mRefCount == 0) { - Log.e(LOGTAG, "disableTransaction is out of sync"); - } if (--mRefCount == 0) { mDataBase.endCacheTransaction(); return true; @@ -266,15 +263,15 @@ public final class CacheManager { return false; } - // only called from WebCore thread - // make sure to call startCacheTransaction/endCacheTransaction in pair - public static boolean startCacheTransaction() { + // only called from WebViewWorkerThread + // make sure to call startTransaction/endTransaction in pair + static boolean startTransaction() { return mDataBase.startCacheTransaction(); } - // only called from WebCore thread - // make sure to call startCacheTransaction/endCacheTransaction in pair - public static boolean endCacheTransaction() { + // only called from WebViewWorkerThread + // make sure to call startTransaction/endTransaction in pair + static boolean endTransaction() { boolean ret = mDataBase.endCacheTransaction(); if (++mTrimCacheCount >= TRIM_CACHE_INTERVAL) { mTrimCacheCount = 0; @@ -283,6 +280,26 @@ public final class CacheManager { return ret; } + // only called from WebCore Thread + // make sure to call startCacheTransaction/endCacheTransaction in pair + /** + * @deprecated + */ + @Deprecated + public static boolean startCacheTransaction() { + return false; + } + + // only called from WebCore Thread + // make sure to call startCacheTransaction/endCacheTransaction in pair + /** + * @deprecated + */ + @Deprecated + public static boolean endCacheTransaction() { + return false; + } + /** * Given a url, returns the CacheResult if exists. Otherwise returns null. * If headers are provided and a cache needs validation, @@ -291,13 +308,11 @@ public final class CacheManager { * * @return the CacheResult for a given url */ - // only called from WebCore thread public static CacheResult getCacheFile(String url, Map<String, String> headers) { return getCacheFile(url, 0, headers); } - // only called from WebCore thread static CacheResult getCacheFile(String url, long postIdentifier, Map<String, String> headers) { if (mDisabled) { @@ -368,14 +383,12 @@ public final class CacheManager { * @hide - hide createCacheFile since it has a parameter of type headers, which is * in a hidden package. */ - // only called from WebCore thread public static CacheResult createCacheFile(String url, int statusCode, Headers headers, String mimeType, boolean forceCache) { return createCacheFile(url, statusCode, headers, mimeType, 0, forceCache); } - // only called from WebCore thread static CacheResult createCacheFile(String url, int statusCode, Headers headers, String mimeType, long postIdentifier, boolean forceCache) { @@ -435,12 +448,10 @@ public final class CacheManager { * Save the info of a cache file for a given url to the CacheMap so that it * can be reused later */ - // only called from WebCore thread public static void saveCacheFile(String url, CacheResult cacheRet) { saveCacheFile(url, 0, cacheRet); } - // only called from WebCore thread static void saveCacheFile(String url, long postIdentifier, CacheResult cacheRet) { try { @@ -489,7 +500,6 @@ public final class CacheManager { * * @return true if it succeeds */ - // only called from WebCore thread static boolean removeAllCacheFiles() { // Note, this is called before init() when the database is // created or upgraded. @@ -499,7 +509,10 @@ public final class CacheManager { mClearCacheOnInit = true; return true; } - // delete cache in a separate thread to not block UI. + // delete rows in the cache database + WebViewWorker.getHandler().sendEmptyMessage( + WebViewWorker.MSG_CLEAR_CACHE); + // delete cache files in a separate thread to not block UI. final Runnable clearCache = new Runnable() { public void run() { // delete all cache files @@ -517,8 +530,6 @@ public final class CacheManager { } catch (SecurityException e) { // Ignore SecurityExceptions. } - // delete database - mDataBase.clearCache(); } }; new Thread(clearCache).start(); @@ -528,15 +539,13 @@ public final class CacheManager { /** * Return true if the cache is empty. */ - // only called from WebCore thread static boolean cacheEmpty() { return mDataBase.hasCache(); } - // only called from WebCore thread static void trimCacheIfNeeded() { if (mDataBase.getCacheTotalSize() > CACHE_THRESHOLD) { - ArrayList<String> pathList = mDataBase.trimCache(CACHE_TRIM_AMOUNT); + List<String> pathList = mDataBase.trimCache(CACHE_TRIM_AMOUNT); int size = pathList.size(); for (int i = 0; i < size; i++) { File f = new File(mBaseDir, pathList.get(i)); @@ -544,9 +553,34 @@ public final class CacheManager { Log.e(LOGTAG, f.getPath() + " delete failed."); } } + // remove the unreferenced files in the cache directory + final List<String> fileList = mDataBase.getAllCacheFileNames(); + if (fileList == null) return; + String[] toDelete = mBaseDir.list(new FilenameFilter() { + public boolean accept(File dir, String filename) { + if (fileList.contains(filename)) { + return false; + } else { + return true; + } + } + }); + if (toDelete == null) return; + size = toDelete.length; + for (int i = 0; i < size; i++) { + File f = new File(mBaseDir, toDelete[i]); + if (!f.delete()) { + Log.e(LOGTAG, f.getPath() + " delete failed."); + } + } } } + static void clearCache() { + // delete database + mDataBase.clearCache(); + } + private static boolean checkCacheRedirect(int statusCode) { if (statusCode == 301 || statusCode == 302 || statusCode == 307) { // as 303 can't be cached, we do not return true diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java index b13c4055bc8a..790363227bb9 100644 --- a/core/java/android/webkit/FrameLoader.java +++ b/core/java/android/webkit/FrameLoader.java @@ -110,7 +110,9 @@ class FrameLoader { return false; } mNetwork = Network.getInstance(mListener.getContext()); - return handleHTTPLoad(); + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_ADD_HTTPLOADER, this).sendToTarget(); + return true; } else if (handleLocalFile(url, mListener, mSettings)) { return true; } @@ -142,24 +144,33 @@ class FrameLoader { } if (URLUtil.isAssetUrl(url)) { // load asset in a separate thread as it involves IO - new FileLoader(url, loadListener, FileLoader.TYPE_ASSET, true) - .enqueue(); + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_ADD_STREAMLOADER, + new FileLoader(url, loadListener, FileLoader.TYPE_ASSET, + true)).sendToTarget(); return true; } else if (URLUtil.isResourceUrl(url)) { // load resource in a separate thread as it involves IO - new FileLoader(url, loadListener, FileLoader.TYPE_RES, true) - .enqueue(); + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_ADD_STREAMLOADER, + new FileLoader(url, loadListener, FileLoader.TYPE_RES, + true)).sendToTarget(); return true; } else if (URLUtil.isFileUrl(url)) { // load file in a separate thread as it involves IO - new FileLoader(url, loadListener, FileLoader.TYPE_FILE, settings - .getAllowFileAccess()).enqueue(); + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_ADD_STREAMLOADER, + new FileLoader(url, loadListener, FileLoader.TYPE_FILE, + settings.getAllowFileAccess())).sendToTarget(); return true; } else if (URLUtil.isContentUrl(url)) { // Send the raw url to the ContentLoader because it will do a // permission check and the url has to match. // load content in a separate thread as it involves IO - new ContentLoader(loadListener.url(), loadListener).enqueue(); + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_ADD_STREAMLOADER, + new ContentLoader(loadListener.url(), loadListener)) + .sendToTarget(); return true; } else if (URLUtil.isDataUrl(url)) { // load data in the current thread to reduce the latency @@ -172,8 +183,8 @@ class FrameLoader { } return false; } - - private boolean handleHTTPLoad() { + + boolean handleHTTPLoad() { if (mHeaders == null) { mHeaders = new HashMap<String, String>(); } @@ -229,7 +240,9 @@ class FrameLoader { CacheLoader cacheLoader = new CacheLoader(mListener, result); mListener.setCacheLoader(cacheLoader); - cacheLoader.load(); + // Load the cached file in a separate thread + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_ADD_STREAMLOADER, cacheLoader).sendToTarget(); } /* diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java index cdc6608ea1f6..8bacee4be8f4 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -101,7 +101,6 @@ class LoadListener extends Handler implements EventHandler { private boolean mCancelled; // The request has been cancelled. private boolean mAuthFailed; // indicates that the prev. auth failed private CacheLoader mCacheLoader; - private CacheManager.CacheResult mCacheResult; private boolean mFromCache = false; private HttpAuthHeader mAuthHeader; private int mErrorID = OK; @@ -301,6 +300,12 @@ class LoadListener extends Handler implements EventHandler { */ public void headers(Headers headers) { if (DebugFlags.LOAD_LISTENER) Log.v(LOGTAG, "LoadListener.headers"); + // call db (setCookie) in the non-WebCore thread + if (mCancelled) return; + ArrayList<String> cookies = headers.getSetCookie(); + for (int i = 0; i < cookies.size(); ++i) { + CookieManager.getInstance().setCookie(mUri, cookies.get(i)); + } sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS, headers)); } @@ -316,11 +321,6 @@ class LoadListener extends Handler implements EventHandler { if (mCancelled) return; mHeaders = headers; - ArrayList<String> cookies = headers.getSetCookie(); - for (int i = 0; i < cookies.size(); ++i) { - CookieManager.getInstance().setCookie(mUri, cookies.get(i)); - } - long contentLength = headers.getContentLength(); if (contentLength != Headers.NO_CONTENT_LENGTH) { mContentLength = contentLength; @@ -454,12 +454,19 @@ class LoadListener extends Handler implements EventHandler { if (!mFromCache && mRequestHandle != null && (!mRequestHandle.getMethod().equals("POST") || mPostIdentifier != 0)) { - mCacheResult = CacheManager.createCacheFile(mUrl, mStatusCode, - headers, mMimeType, mPostIdentifier, false); - } - if (mCacheResult != null) { - mCacheResult.encoding = mEncoding; + WebViewWorker.CacheCreateData data = new WebViewWorker.CacheCreateData(); + data.mListener = this; + data.mUrl = mUrl; + data.mMimeType = mMimeType; + data.mStatusCode = mStatusCode; + data.mPostId = mPostIdentifier; + data.mHeaders = headers; + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_CREATE_CACHE, data).sendToTarget(); } + WebViewWorker.CacheEncoding ce = new WebViewWorker.CacheEncoding(); + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_UPDATE_CACHE_ENCODING, ce).sendToTarget(); } commitHeadersCheckRedirect(); } @@ -649,7 +656,10 @@ class LoadListener extends Handler implements EventHandler { // ask for it, so make sure we have a valid CacheLoader // before calling it. if (mCacheLoader != null) { - mCacheLoader.load(); + // Load the cached file in a separate thread + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader) + .sendToTarget(); mFromCache = true; if (DebugFlags.LOAD_LISTENER) { Log.v(LOGTAG, "LoadListener cache load url=" + url()); @@ -708,8 +718,10 @@ class LoadListener extends Handler implements EventHandler { Log.v(LOGTAG, "FrameLoader: HTTP URL in cache " + "and usable: " + url()); } - // Load the cached file - mCacheLoader.load(); + // Load the cached file in a separate thread + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader) + .sendToTarget(); mFromCache = true; return true; } @@ -934,12 +946,9 @@ class LoadListener extends Handler implements EventHandler { * WebCore. */ void downloadFile() { - // Setting the Cache Result to null ensures that this - // content is not added to the cache - if (mCacheResult != null) { - CacheManager.cleanupCacheFile(mCacheResult); - mCacheResult = null; - } + // remove the cache + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget(); // Inform the client that they should download a file mBrowserFrame.getCallbackProxy().onDownloadStart(url(), @@ -1098,24 +1107,15 @@ class LoadListener extends Handler implements EventHandler { if (c == null) break; if (c.mLength != 0) { - if (mCacheResult != null) { - mCacheResult.contentLength += c.mLength; - if (mCacheResult.contentLength > CacheManager.CACHE_MAX_SIZE) { - CacheManager.cleanupCacheFile(mCacheResult); - mCacheResult = null; - } else { - try { - mCacheResult.outStream - .write(c.mArray, 0, c.mLength); - } catch (IOException e) { - CacheManager.cleanupCacheFile(mCacheResult); - mCacheResult = null; - } - } - } nativeAddData(c.mArray, c.mLength); + WebViewWorker.CacheData data = new WebViewWorker.CacheData(); + data.mListener = this; + data.mChunk = c; + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_APPEND_CACHE, data).sendToTarget(); + } else { + c.release(); } - c.release(); checker.responseAlert("res nativeAddData"); } } @@ -1125,18 +1125,16 @@ class LoadListener extends Handler implements EventHandler { * cancellation or errors during the load. */ void tearDown() { - if (mCacheResult != null) { - if (getErrorID() == OK) { - CacheManager.saveCacheFile(mUrl, mPostIdentifier, mCacheResult); - } else { - CacheManager.cleanupCacheFile(mCacheResult); - } - - // we need to reset mCacheResult to be null - // resource loader's tearDown will call into WebCore's - // nativeFinish, which in turn calls loader.cancel(). - // If we don't reset mCacheFile, the file will be deleted. - mCacheResult = null; + if (getErrorID() == OK) { + WebViewWorker.CacheSaveData data = new WebViewWorker.CacheSaveData(); + data.mListener = this; + data.mUrl = mUrl; + data.mPostId = mPostIdentifier; + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_SAVE_CACHE, data).sendToTarget(); + } else { + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget(); } if (mNativeLoader != 0) { PerfChecker checker = new PerfChecker(); @@ -1194,10 +1192,8 @@ class LoadListener extends Handler implements EventHandler { mRequestHandle = null; } - if (mCacheResult != null) { - CacheManager.cleanupCacheFile(mCacheResult); - mCacheResult = null; - } + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget(); mCancelled = true; clearNativeLoader(); @@ -1258,14 +1254,16 @@ class LoadListener extends Handler implements EventHandler { } // Cache the redirect response - if (mCacheResult != null) { - if (getErrorID() == OK) { - CacheManager.saveCacheFile(mUrl, mPostIdentifier, - mCacheResult); - } else { - CacheManager.cleanupCacheFile(mCacheResult); - } - mCacheResult = null; + if (getErrorID() == OK) { + WebViewWorker.CacheSaveData data = new WebViewWorker.CacheSaveData(); + data.mListener = this; + data.mUrl = mUrl; + data.mPostId = mPostIdentifier; + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_SAVE_CACHE, data).sendToTarget(); + } else { + WebViewWorker.getHandler().obtainMessage( + WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget(); } // This will strip the anchor diff --git a/core/java/android/webkit/StreamLoader.java b/core/java/android/webkit/StreamLoader.java index 4c3299773dd9..7bcd50dd4677 100644 --- a/core/java/android/webkit/StreamLoader.java +++ b/core/java/android/webkit/StreamLoader.java @@ -20,8 +20,6 @@ import android.content.Context; import android.net.http.EventHandler; import android.net.http.Headers; import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; import android.os.Message; import java.io.IOException; @@ -61,11 +59,6 @@ abstract class StreamLoader implements Handler.Callback { // Handler which will be initialized in the thread where load() is called. private Handler mHandler; - // Handler which will be used to load StreamLoader in a separate thread - private static StreamQueueHandler sStreamQueueHandler; - - private static final Object sStreamQueueLock = new Object(); - /** * Constructor. Although this class calls the LoadListener, it only calls * the EventHandler Interface methods. LoadListener concrete class is used @@ -97,26 +90,6 @@ abstract class StreamLoader implements Handler.Callback { abstract protected void buildHeaders(Headers headers); /** - * Calling this method to load this StreamLoader in a separate - * "StreamLoadingThread". - */ - final void enqueue() { - synchronized (sStreamQueueLock) { - if (sStreamQueueHandler == null) { - HandlerThread thread = new HandlerThread( - StreamQueueHandler.THREAD_NAME, - android.os.Process.THREAD_PRIORITY_DEFAULT + - android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE); - thread.start(); - sStreamQueueHandler = new StreamQueueHandler(thread.getLooper()); - } - } - - sStreamQueueHandler.obtainMessage(StreamQueueHandler.MSG_ADD_LOADER, - this).sendToTarget(); - } - - /** * Calling this method starts the load of the content for this StreamLoader. * This method simply creates a Handler in the current thread and posts a * message to send the status and returns immediately. @@ -228,22 +201,4 @@ abstract class StreamLoader implements Handler.Callback { } mLoadListener.endData(); } - - private static class StreamQueueHandler extends Handler { - private static final String THREAD_NAME = "StreamLoadingThread"; - - private static final int MSG_ADD_LOADER = 101; - - StreamQueueHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - if (msg.what == MSG_ADD_LOADER) { - StreamLoader loader = (StreamLoader) msg.obj; - loader.load(); - } - } - } } diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index d1ad61fa9bdc..db19bcacc19c 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -304,15 +304,16 @@ import java.util.ArrayList; public void onEditorAction(int actionCode) { switch (actionCode) { case EditorInfo.IME_ACTION_NEXT: - // Since the cursor will no longer be in the same place as the - // focus, set the focus controller back to inactive - mWebView.setFocusControllerInactive(); - mWebView.nativeMoveCursorToNextTextInput(); - // Preemptively rebuild the WebTextView, so that the action will - // be set properly. - mWebView.rebuildWebTextView(); - setDefaultSelection(); - mWebView.invalidate(); + if (mWebView.nativeMoveCursorToNextTextInput()) { + // Since the cursor will no longer be in the same place as the + // focus, set the focus controller back to inactive + mWebView.setFocusControllerInactive(); + // Preemptively rebuild the WebTextView, so that the action will + // be set properly. + mWebView.rebuildWebTextView(); + setDefaultSelection(); + mWebView.invalidate(); + } break; case EditorInfo.IME_ACTION_DONE: super.onEditorAction(actionCode); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index d29d6f378274..897bd75fe1aa 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -6807,7 +6807,7 @@ public class WebView extends AbsoluteLayout private native void nativeHideCursor(); private native String nativeImageURI(int x, int y); private native void nativeInstrumentReport(); - /* package */ native void nativeMoveCursorToNextTextInput(); + /* package */ native boolean nativeMoveCursorToNextTextInput(); // return true if the page has been scrolled private native boolean nativeMotionUp(int x, int y, int slop); // returns false if it handled the key diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 4606bc6ffa1b..71f69feeb19d 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -587,13 +587,6 @@ final class WebViewCore { private static final int INITIALIZE = 0; private static final int REDUCE_PRIORITY = 1; private static final int RESUME_PRIORITY = 2; - private static final int CACHE_TICKER = 3; - private static final int BLOCK_CACHE_TICKER = 4; - private static final int RESUME_CACHE_TICKER = 5; - - private static final int CACHE_TICKER_INTERVAL = 60 * 1000; // 1 minute - - private static boolean mCacheTickersBlocked = true; public void run() { Looper.prepare(); @@ -619,28 +612,6 @@ final class WebViewCore { Process.setThreadPriority( Process.THREAD_PRIORITY_DEFAULT); break; - - case CACHE_TICKER: - if (!mCacheTickersBlocked) { - CacheManager.endCacheTransaction(); - CacheManager.startCacheTransaction(); - sendMessageDelayed( - obtainMessage(CACHE_TICKER), - CACHE_TICKER_INTERVAL); - } - break; - - case BLOCK_CACHE_TICKER: - if (CacheManager.endCacheTransaction()) { - mCacheTickersBlocked = true; - } - break; - - case RESUME_CACHE_TICKER: - if (CacheManager.startCacheTransaction()) { - mCacheTickersBlocked = false; - } - break; } } }; @@ -1092,23 +1063,15 @@ final class WebViewCore { Process.setThreadPriority(mTid, Process.THREAD_PRIORITY_BACKGROUND); pauseTimers(); - if (CacheManager.disableTransaction()) { - WebCoreThread.mCacheTickersBlocked = true; - sWebCoreHandler.removeMessages( - WebCoreThread.CACHE_TICKER); - } + WebViewWorker.getHandler().sendEmptyMessage( + WebViewWorker.MSG_PAUSE_CACHE_TRANSACTION); break; case RESUME_TIMERS: Process.setThreadPriority(mTid, mSavedPriority); resumeTimers(); - if (CacheManager.enableTransaction()) { - WebCoreThread.mCacheTickersBlocked = false; - sWebCoreHandler.sendMessageDelayed( - sWebCoreHandler.obtainMessage( - WebCoreThread.CACHE_TICKER), - WebCoreThread.CACHE_TICKER_INTERVAL); - } + WebViewWorker.getHandler().sendEmptyMessage( + WebViewWorker.MSG_RESUME_CACHE_TRANSACTION); break; case ON_PAUSE: @@ -1851,16 +1814,6 @@ final class WebViewCore { .obtainMessage(WebCoreThread.RESUME_PRIORITY)); } - static void startCacheTransaction() { - sWebCoreHandler.sendMessage(sWebCoreHandler - .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER)); - } - - static void endCacheTransaction() { - sWebCoreHandler.sendMessage(sWebCoreHandler - .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER)); - } - static void pauseUpdatePicture(WebViewCore core) { // Note: there is one possible failure mode. If pauseUpdatePicture() is // called from UI thread while WEBKIT_DRAW is just pulled out of the @@ -1992,9 +1945,10 @@ final class WebViewCore { sendUpdateTextEntry(); // as CacheManager can behave based on database transaction, we need to // call tick() to trigger endTransaction - sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER); - sWebCoreHandler.sendMessage(sWebCoreHandler - .obtainMessage(WebCoreThread.CACHE_TICKER)); + WebViewWorker.getHandler().removeMessages( + WebViewWorker.MSG_CACHE_TRANSACTION_TICKER); + WebViewWorker.getHandler().sendEmptyMessage( + WebViewWorker.MSG_CACHE_TRANSACTION_TICKER); contentDraw(); } diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java index 110e4f817c8f..a8709312b3b4 100644 --- a/core/java/android/webkit/WebViewDatabase.java +++ b/core/java/android/webkit/WebViewDatabase.java @@ -19,6 +19,7 @@ package android.webkit; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Set; import java.util.Map.Entry; @@ -234,6 +235,13 @@ public class WebViewDatabase { } if (mCacheDatabase != null) { + // use read_uncommitted to speed up READ + mCacheDatabase.execSQL("PRAGMA read_uncommitted = true;"); + // as only READ can be called in the non-WebViewWorkerThread, + // and read_uncommitted is used, we can turn off database lock + // to use transaction. + mCacheDatabase.setLockingEnabled(false); + // use InsertHelper for faster insertion mCacheInserter = new DatabaseUtils.InsertHelper(mCacheDatabase, "cache"); @@ -548,19 +556,33 @@ public class WebViewDatabase { } // - // cache functions, can only be called from WebCoreThread + // cache functions // + // only called from WebViewWorkerThread boolean startCacheTransaction() { if (++mCacheTransactionRefcount == 1) { + if (!Thread.currentThread().equals( + WebViewWorker.getHandler().getLooper().getThread())) { + Log.w(LOGTAG, "startCacheTransaction should be called from " + + "WebViewWorkerThread instead of from " + + Thread.currentThread().getName()); + } mCacheDatabase.beginTransaction(); return true; } return false; } + // only called from WebViewWorkerThread boolean endCacheTransaction() { if (--mCacheTransactionRefcount == 0) { + if (!Thread.currentThread().equals( + WebViewWorker.getHandler().getLooper().getThread())) { + Log.w(LOGTAG, "endCacheTransaction should be called from " + + "WebViewWorkerThread instead of from " + + Thread.currentThread().getName()); + } try { mCacheDatabase.setTransactionSuccessful(); } finally { @@ -684,7 +706,7 @@ public class WebViewDatabase { return size; } - ArrayList<String> trimCache(long amount) { + List<String> trimCache(long amount) { ArrayList<String> pathList = new ArrayList<String>(100); Cursor cursor = mCacheDatabase.rawQuery( "SELECT contentlength, filepath FROM cache ORDER BY expires ASC", @@ -727,6 +749,20 @@ public class WebViewDatabase { return pathList; } + List<String> getAllCacheFileNames() { + ArrayList<String> pathList = null; + Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath FROM cache", + null); + if (cursor != null && cursor.moveToFirst()) { + pathList = new ArrayList<String>(cursor.getCount()); + do { + pathList.add(cursor.getString(0)); + } while (cursor.moveToNext()); + } + cursor.close(); + return pathList; + } + // // password functions // diff --git a/core/java/android/webkit/WebViewWorker.java b/core/java/android/webkit/WebViewWorker.java new file mode 100644 index 000000000000..c488150f182e --- /dev/null +++ b/core/java/android/webkit/WebViewWorker.java @@ -0,0 +1,223 @@ +/* + * 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.webkit; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import android.net.http.Headers; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; + +/** + * WebViewWorker executes in a separate thread other than UI and WebViewCore. To + * avoid blocking UI or WebKit's execution, the caller can send a message to + * WebViewWorker.getHandler() and it will be handled in the WebViewWorkerThread. + */ +final class WebViewWorker extends Handler { + + private static final String THREAD_NAME = "WebViewWorkerThread"; + + private static WebViewWorker sWorkerHandler; + + private static Map<LoadListener, CacheManager.CacheResult> mCacheResultMap + = new HashMap<LoadListener, CacheManager.CacheResult>(); + + /** + * Package level class to be used while creating a cache entry. + */ + static class CacheCreateData { + LoadListener mListener; + String mUrl; + String mMimeType; + int mStatusCode; + long mPostId; + Headers mHeaders; + } + + /** + * Package level class to be used while saving a cache entry. + */ + static class CacheSaveData { + LoadListener mListener; + String mUrl; + long mPostId; + } + + /** + * Package level class to be used while updating a cache entry's encoding. + */ + static class CacheEncoding { + LoadListener mListener; + String mEncoding; + } + + /** + * Package level class to be used while appending data to a cache entry. + */ + static class CacheData { + LoadListener mListener; + ByteArrayBuilder.Chunk mChunk; + } + + static synchronized WebViewWorker getHandler() { + if (sWorkerHandler == null) { + HandlerThread thread = new HandlerThread(THREAD_NAME, + android.os.Process.THREAD_PRIORITY_DEFAULT + + android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE); + thread.start(); + sWorkerHandler = new WebViewWorker(thread.getLooper()); + } + return sWorkerHandler; + } + + private WebViewWorker(Looper looper) { + super(looper); + } + + // trigger transaction once a minute + private static final int CACHE_TRANSACTION_TICKER_INTERVAL = 60 * 1000; + + private static boolean mCacheTickersBlocked = true; + + // message ids + static final int MSG_ADD_STREAMLOADER = 101; + static final int MSG_ADD_HTTPLOADER = 102; + static final int MSG_CREATE_CACHE = 103; + static final int MSG_UPDATE_CACHE_ENCODING = 104; + static final int MSG_APPEND_CACHE = 105; + static final int MSG_SAVE_CACHE = 106; + static final int MSG_REMOVE_CACHE = 107; + static final int MSG_TRIM_CACHE = 108; + static final int MSG_CLEAR_CACHE = 109; + static final int MSG_CACHE_TRANSACTION_TICKER = 110; + static final int MSG_PAUSE_CACHE_TRANSACTION = 111; + static final int MSG_RESUME_CACHE_TRANSACTION = 112; + + @Override + public void handleMessage(Message msg) { + switch(msg.what) { + case MSG_ADD_STREAMLOADER: { + StreamLoader loader = (StreamLoader) msg.obj; + loader.load(); + break; + } + case MSG_ADD_HTTPLOADER: { + FrameLoader loader = (FrameLoader) msg.obj; + loader.handleHTTPLoad(); + break; + } + case MSG_CREATE_CACHE: { + CacheCreateData data = (CacheCreateData) msg.obj; + CacheManager.CacheResult cache = CacheManager.createCacheFile( + data.mUrl, data.mStatusCode, data.mHeaders, + data.mMimeType, data.mPostId, false); + if (cache != null) { + mCacheResultMap.put(data.mListener, cache); + } else { + mCacheResultMap.remove(data.mListener); + } + break; + } + case MSG_UPDATE_CACHE_ENCODING: { + CacheEncoding data = (CacheEncoding) msg.obj; + CacheManager.CacheResult cache = mCacheResultMap + .get(data.mListener); + if (cache != null) { + cache.encoding = data.mEncoding; + } + break; + } + case MSG_APPEND_CACHE: { + CacheData data = (CacheData) msg.obj; + CacheManager.CacheResult cache = mCacheResultMap + .get(data.mListener); + if (cache != null) { + cache.contentLength += data.mChunk.mLength; + if (cache.contentLength > CacheManager.CACHE_MAX_SIZE) { + CacheManager.cleanupCacheFile(cache); + mCacheResultMap.remove(data.mListener); + } else { + try { + cache.outStream.write(data.mChunk.mArray, 0, + data.mChunk.mLength); + } catch (IOException e) { + CacheManager.cleanupCacheFile(cache); + mCacheResultMap.remove(data.mListener); + } + } + } + data.mChunk.release(); + break; + } + case MSG_SAVE_CACHE: { + CacheSaveData data = (CacheSaveData) msg.obj; + CacheManager.CacheResult cache = mCacheResultMap + .get(data.mListener); + if (cache != null) { + CacheManager.saveCacheFile(data.mUrl, data.mPostId, cache); + mCacheResultMap.remove(data.mListener); + } + break; + } + case MSG_REMOVE_CACHE: { + LoadListener listener = (LoadListener) msg.obj; + CacheManager.CacheResult cache = mCacheResultMap.get(listener); + if (cache != null) { + CacheManager.cleanupCacheFile(cache); + mCacheResultMap.remove(listener); + } + break; + } + case MSG_TRIM_CACHE: { + CacheManager.trimCacheIfNeeded(); + break; + } + case MSG_CLEAR_CACHE: { + CacheManager.clearCache(); + break; + } + case MSG_CACHE_TRANSACTION_TICKER: { + if (!mCacheTickersBlocked) { + CacheManager.endTransaction(); + CacheManager.startTransaction(); + sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER, + CACHE_TRANSACTION_TICKER_INTERVAL); + } + break; + } + case MSG_PAUSE_CACHE_TRANSACTION: { + if (CacheManager.disableTransaction()) { + mCacheTickersBlocked = true; + removeMessages(MSG_CACHE_TRANSACTION_TICKER); + } + break; + } + case MSG_RESUME_CACHE_TRANSACTION: { + if (CacheManager.enableTransaction()) { + mCacheTickersBlocked = false; + sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER, + CACHE_TRANSACTION_TICKER_INTERVAL); + } + break; + } + } + } +} diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java index 79abd4bebace..9cc8bd50584a 100644 --- a/core/java/android/widget/ExpandableListView.java +++ b/core/java/android/widget/ExpandableListView.java @@ -547,9 +547,10 @@ public class ExpandableListView extends ListView { final int groupPos = posMetadata.position.groupPos; final int groupFlatPos = posMetadata.position.flatListPos; - - smoothScrollToPosition(groupFlatPos + mAdapter.getChildrenCount(groupPos), - groupFlatPos); + + final int shiftedGroupPosition = groupFlatPos + getHeaderViewsCount(); + smoothScrollToPosition(shiftedGroupPosition + mAdapter.getChildrenCount(groupPos), + shiftedGroupPosition); } returnValue = true; diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 394ce0a31985..35ea0ccd861b 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2276,6 +2276,6 @@ <!-- Strings for car mode notification --> <!-- Shown when car mode is enabled --> <string name="car_mode_disable_notification_title">Car mode enabled</string> - <string name="car_mode_disable_notification_message">Select to disable car mode.</string> + <string name="car_mode_disable_notification_message">Select to exit car mode.</string> </resources> diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java index 46f28c7f0385..d92f0e1d7a96 100644 --- a/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java +++ b/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java @@ -1145,6 +1145,10 @@ public class PduPersister { } } + // mark "read" and "seen" + values.put(Mms.READ, 0); + values.put(Mms.SEEN, 0); + Uri res = SqliteWrapper.insert(mContext, mContentResolver, uri, values); if (res == null) { throw new MmsException("persist() failed: return null."); diff --git a/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java b/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java index 87e47583e1db..cfc9231f0fae 100644 --- a/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java +++ b/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java @@ -51,37 +51,6 @@ public final class TelephonyProvider { private static final boolean DEBUG = true; private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; -// public static final Pattern EMAIL_ADDRESS -// = Pattern.compile( -// "[a-zA-Z0-9\\+\\.\\_\\%\\-]{1,256}" + -// "\\@" + -// "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" + -// "(" + -// "\\." + -// "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" + -// ")+" -// ); -// -// /** -// * This pattern is intended for searching for things that look like they -// * might be phone numbers in arbitrary text, not for validating whether -// * something is in fact a phone number. It will miss many things that -// * are legitimate phone numbers. -// * -// * <p> The pattern matches the following: -// * <ul> -// * <li>Optionally, a + sign followed immediately by one or more digits. Spaces, dots, or dashes -// * may follow. -// * <li>Optionally, sets of digits in parentheses, separated by spaces, dots, or dashes. -// * <li>A string starting and ending with a digit, containing digits, spaces, dots, and/or dashes. -// * </ul> -// */ -// public static final Pattern PHONE -// = Pattern.compile( // sdd = space, dot, or dash -// "(\\+[0-9]+[\\- \\.]*)?" // +<digits><sdd>* -// + "(\\([0-9]+\\)[\\- \\.]*)?" // (<digits>)<sdd>* -// + "([0-9][0-9\\- \\.][0-9\\- \\.]+[0-9])"); // <digit><digit|sdd>+<digit> - // Constructor public TelephonyProvider() { } @@ -136,6 +105,12 @@ public final class TelephonyProvider { public static final String READ = "read"; /** + * Indicates whether this message has been seen by the user. The "seen" flag will be + * used to figure out whether we need to throw up a statusbar notification or not. + */ + public static final String SEEN = "seen"; + + /** * The TP-Status value for the message, or -1 if no status has * been received */ @@ -706,6 +681,12 @@ public final class TelephonyProvider { public static final String READ = "read"; /** + * Indicates whether this message has been seen by the user. The "seen" flag will be + * used to figure out whether we need to throw up a statusbar notification or not. + */ + public static final String SEEN = "seen"; + + /** * The Message-ID of the message. * <P>Type: TEXT</P> */ @@ -1157,6 +1138,7 @@ public final class TelephonyProvider { * <P>Type: INTEGER</P> */ public static final String READ = "read"; + /** * The snippet of the latest message in the thread. * <P>Type: TEXT</P> @@ -1697,6 +1679,13 @@ public final class TelephonyProvider { */ public static final String LAST_TRY = "last_try"; } + + public static final class WordsTable { + public static final String ID = "_id"; + public static final String SOURCE_ROW_ID = "source_id"; + public static final String TABLE_ID = "table_to_use"; + public static final String INDEXED_TEXT = "index_text"; + } } public static final class Carriers implements BaseColumns { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index a404ec5c7f54..1840ecce17bb 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1860,11 +1860,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " app=" + app + " knownToBeDead=" + knownToBeDead + " thread=" + (app != null ? app.thread : null) + " pid=" + (app != null ? app.pid : -1)); - if (app != null && - (!knownToBeDead || app.thread == null) && app.pid > 0) { - return app; + if (app != null && app.pid > 0) { + if (!knownToBeDead || app.thread == null) { + return app; + } else { + // An application record is attached to a previous process, + // clean it up now. + handleAppDiedLocked(app, true); + } } - + String hostingNameStr = hostingName != null ? hostingName.flattenToShortString() : null; @@ -4588,7 +4593,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mProcDeaths[0]++; - if (app.thread != null && app.thread.asBinder() == thread.asBinder()) { + // Clean up already done if the process has been re-started. + if (app.pid == pid && app.thread != null && + app.thread.asBinder() == thread.asBinder()) { Log.i(TAG, "Process " + app.processName + " (pid " + pid + ") has died."); EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.pid, app.processName); @@ -4636,6 +4643,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen scheduleAppGcsLocked(); } } + } else if (app.pid != pid) { + // A new process has already been started. + Log.i(TAG, "Process " + app.processName + " (pid " + pid + + ") has died and restarted (pid " + app.pid + ")."); + EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.pid, app.processName); } else if (DEBUG_PROCESSES) { Log.d(TAG, "Received spurious death notification for thread " + thread.asBinder()); @@ -5479,6 +5491,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen finishReceiverLocked(br.receiver, br.resultCode, br.resultData, br.resultExtras, br.resultAbort, true); scheduleBroadcastsLocked(); + // We need to reset the state if we fails to start the receiver. + br.state = BroadcastRecord.IDLE; } } diff --git a/test-runner/src/android/test/IsolatedContext.java b/test-runner/src/android/test/IsolatedContext.java index 485e45c957c9..f0935989b545 100644 --- a/test-runner/src/android/test/IsolatedContext.java +++ b/test-runner/src/android/test/IsolatedContext.java @@ -3,7 +3,11 @@ package android.test; import com.google.android.collect.Lists; import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.accounts.AuthenticatorException; import android.accounts.OnAccountsUpdateListener; +import android.accounts.OperationCanceledException; import android.accounts.Account; import android.content.ContextWrapper; import android.content.ContentResolver; @@ -16,8 +20,13 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Handler; -import java.util.List; import java.io.File; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.util.List; + /** * A mock context which prevents its users from talking to the rest of the device while @@ -105,7 +114,58 @@ public class IsolatedContext extends ContextWrapper { public Account[] getAccounts() { return new Account[]{}; } + + public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures( + final String type, final String[] features, + AccountManagerCallback<Account[]> callback, Handler handler) { + return new MockAccountManagerFuture<Account[]>(new Account[0]); + } + + public String blockingGetAuthToken(Account account, String authTokenType, + boolean notifyAuthFailure) + throws OperationCanceledException, IOException, AuthenticatorException { + return null; + } + + + /** + * A very simple AccountManagerFuture class + * that returns what ever was passed in + */ + private class MockAccountManagerFuture<T> + implements AccountManagerFuture<T> { + + T mResult; + + public MockAccountManagerFuture(T result) { + mResult = result; + } + + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + public boolean isCancelled() { + return false; + } + + public boolean isDone() { + return true; + } + + public T getResult() + throws OperationCanceledException, IOException, AuthenticatorException { + return mResult; + } + + public T getResult(long timeout, TimeUnit unit) + throws OperationCanceledException, IOException, AuthenticatorException { + return getResult(); + } + } + } + @Override public File getFilesDir() { return new File("/dev/null"); diff --git a/tests/DumpRenderTree/assets/run_layout_tests.py b/tests/DumpRenderTree/assets/run_layout_tests.py index c3627bbf8fae..7f3ef2d07c88 100755 --- a/tests/DumpRenderTree/assets/run_layout_tests.py +++ b/tests/DumpRenderTree/assets/run_layout_tests.py @@ -26,6 +26,7 @@ --time-out-ms (default is 8000 millis) for each test --adb-options="-e" passes option string to adb --results-directory=..., (default is ./layout-test-results) directory name under which results are stored. + --js-engine the JavaScript engine currently in use, determines which set of Android-specific expected results we should use, should be 'jsc' or 'v8' """ import logging @@ -186,6 +187,16 @@ def main(options, args): run_layout_test_cmd_postfix = " -e path \"" + path + "\" -e timeout " + timeout_ms if options.rebaseline: run_layout_test_cmd_postfix += " -e rebaseline true" + + # If the JS engine is not specified on the command line, try reading the + # JS_ENGINE environment variable, which is used by the build system in + # external/webkit/Android.mk. + js_engine = options.js_engine + if not js_engine: + js_engine = os.environ['JS_ENGINE'] + if js_engine: + run_layout_test_cmd_postfix += " -e jsengine " + js_engine + run_layout_test_cmd_postfix += " -w com.android.dumprendertree/.LayoutTestsAutoRunner" # Call LayoutTestsAutoTest::startLayoutTests. @@ -297,6 +308,9 @@ if '__main__' == __name__: default=None, dest="ref_directory", help="directory where reference results are stored.") + option_parser.add_option("", "--js-engine", + default=None, + help="The JavaScript engine currently in use, which determines which set of Android-specific expected results we should use. Should be 'jsc' or 'v8'."); options, args = option_parser.parse_args(); main(options, args) diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java index 6ceb0f927070..191b5e94ae73 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java @@ -84,7 +84,6 @@ public class FileFilter { // This first block of tests are for HTML5 features, for which Android // should pass all tests. They are skipped only temporarily. // TODO: Fix these failing tests and remove them from this list. - ignoreResultList.add("fast/dom/Geolocation/callback-exception.html"); // exception output incorrect with V8 ignoreResultList.add("http/tests/appcache/auth.html"); // file not found ignoreResultList.add("http/tests/appcache/deferred-events.html"); // file not found ignoreResultList.add("http/tests/appcache/deferred-events-delete-while-raising.html"); // file not found diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java index 539d55105b23..e058f3221080 100755 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java @@ -79,14 +79,17 @@ public class LayoutTestsAutoRunner extends InstrumentationTestRunner { mSaveImagePath = (String) icicle.get("saveimage"); + mJsEngine = (String) icicle.get("jsengine"); + super.onCreate(icicle); } - public String mTestPath = null; - public String mSaveImagePath = null; - public int mTimeoutInMillis = 0; - public int mDelay = 0; - public boolean mRebaseline = false; - public boolean mLogtime = false; - public boolean mGetDrawTime = false; + public String mTestPath; + public String mSaveImagePath; + public int mTimeoutInMillis; + public int mDelay; + public boolean mRebaseline; + public boolean mLogtime; + public boolean mGetDrawTime; + public String mJsEngine; } diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java index 634d6831af3d..d9ec3fad3314 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java @@ -147,6 +147,9 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh private MyTestRecorder mResultRecorder; private Vector<String> mTestList; private boolean mRebaselineResults; + // The JavaScript engine currently in use. This determines which set of Android-specific + // expected test results we use. + private String mJsEngine; private String mTestPathPrefix; private boolean mFinished; @@ -214,14 +217,24 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh return shortName.replaceFirst(LAYOUT_TESTS_ROOT, LAYOUT_TESTS_RESULT_DIR) + "-result.txt"; } + // Gets the file which contains WebKit's expected results for this test. private String getExpectedResultFile(String test) { + // The generic result is at <path>/<name>-expected.txt + // First try the Android-specific result at + // platform/android-<js-engine>/<path>/<name>-expected.txt int pos = test.lastIndexOf('.'); - if(pos == -1) + if (pos == -1) return null; - String shortName = test.substring(0, pos); - return shortName + "-expected.txt"; + String genericExpectedResult = test.substring(0, pos) + "-expected.txt"; + String androidExpectedResultsDir = "platform/android-" + mJsEngine + "/"; + String androidExpectedResult = + genericExpectedResult.replaceFirst(LAYOUT_TESTS_ROOT, LAYOUT_TESTS_ROOT + androidExpectedResultsDir); + File f = new File(androidExpectedResult); + return f.exists() ? androidExpectedResult : genericExpectedResult; } + // Gets the file which contains the actual results of running the test on + // Android, generated by a previous run which set a new baseline. private String getAndroidExpectedResultFile(String expectedResultFile) { return expectedResultFile.replaceFirst(LAYOUT_TESTS_ROOT, ANDROID_EXPECTED_RESULT_DIR); } @@ -282,8 +295,8 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh }); String resultFile = getResultFile(test); - if(resultFile == null) { - //simply ignore this test + if (resultFile == null) { + // Simply ignore this test. return; } if (mRebaselineResults) { @@ -339,8 +352,10 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh this.mTestList = new Vector<String>(); // Read settings - this.mTestPathPrefix = (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getAbsolutePath(); - this.mRebaselineResults = runner.mRebaseline; + mTestPathPrefix = (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getAbsolutePath(); + mRebaselineResults = runner.mRebaseline; + // JSC is the default JavaScript engine. + mJsEngine = runner.mJsEngine == null ? "jsc" : runner.mJsEngine; int timeout = runner.mTimeoutInMillis; if (timeout <= 0) { |