1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.webkit; 18 19 import java.io.IOException; 20 import java.util.HashMap; 21 import java.util.Map; 22 23 import android.net.http.Headers; 24 import android.os.Handler; 25 import android.os.HandlerThread; 26 import android.os.Looper; 27 import android.os.Message; 28 29 /** 30 * WebViewWorker executes in a separate thread other than UI and WebViewCore. To 31 * avoid blocking UI or WebKit's execution, the caller can send a message to 32 * WebViewWorker.getHandler() and it will be handled in the WebViewWorkerThread. 33 */ 34 final class WebViewWorker extends Handler { 35 36 private static final String THREAD_NAME = "WebViewWorkerThread"; 37 38 private static WebViewWorker sWorkerHandler; 39 40 private static Map<LoadListener, CacheManager.CacheResult> mCacheResultMap 41 = new HashMap<LoadListener, CacheManager.CacheResult>(); 42 43 /** 44 * Package level class to be used while creating a cache entry. 45 */ 46 static class CacheCreateData { 47 LoadListener mListener; 48 String mUrl; 49 String mMimeType; 50 int mStatusCode; 51 long mPostId; 52 Headers mHeaders; 53 } 54 55 /** 56 * Package level class to be used while saving a cache entry. 57 */ 58 static class CacheSaveData { 59 LoadListener mListener; 60 String mUrl; 61 long mPostId; 62 } 63 64 /** 65 * Package level class to be used while updating a cache entry's encoding. 66 */ 67 static class CacheEncoding { 68 LoadListener mListener; 69 String mEncoding; 70 } 71 72 /** 73 * Package level class to be used while appending data to a cache entry. 74 */ 75 static class CacheData { 76 LoadListener mListener; 77 ByteArrayBuilder.Chunk mChunk; 78 } 79 80 static synchronized WebViewWorker getHandler() { 81 if (sWorkerHandler == null) { 82 HandlerThread thread = new HandlerThread(THREAD_NAME, 83 android.os.Process.THREAD_PRIORITY_DEFAULT 84 + android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE); 85 thread.start(); 86 sWorkerHandler = new WebViewWorker(thread.getLooper()); 87 } 88 return sWorkerHandler; 89 } 90 91 private WebViewWorker(Looper looper) { 92 super(looper); 93 } 94 95 // trigger transaction once a minute 96 private static final int CACHE_TRANSACTION_TICKER_INTERVAL = 60 * 1000; 97 98 private static boolean mCacheTickersBlocked = true; 99 100 // message ids 101 static final int MSG_ADD_STREAMLOADER = 101; 102 static final int MSG_ADD_HTTPLOADER = 102; 103 static final int MSG_CREATE_CACHE = 103; 104 static final int MSG_UPDATE_CACHE_ENCODING = 104; 105 static final int MSG_APPEND_CACHE = 105; 106 static final int MSG_SAVE_CACHE = 106; 107 static final int MSG_REMOVE_CACHE = 107; 108 static final int MSG_TRIM_CACHE = 108; 109 static final int MSG_CLEAR_CACHE = 109; 110 static final int MSG_CACHE_TRANSACTION_TICKER = 110; 111 static final int MSG_PAUSE_CACHE_TRANSACTION = 111; 112 static final int MSG_RESUME_CACHE_TRANSACTION = 112; 113 114 @Override 115 public void handleMessage(Message msg) { 116 switch(msg.what) { 117 case MSG_ADD_STREAMLOADER: { 118 StreamLoader loader = (StreamLoader) msg.obj; 119 loader.load(); 120 break; 121 } 122 case MSG_ADD_HTTPLOADER: { 123 FrameLoader loader = (FrameLoader) msg.obj; 124 loader.handleHTTPLoad(); 125 break; 126 } 127 case MSG_CREATE_CACHE: { 128 assert !JniUtil.useChromiumHttpStack(); 129 CacheCreateData data = (CacheCreateData) msg.obj; 130 CacheManager.CacheResult cache = CacheManager.createCacheFile( 131 data.mUrl, data.mStatusCode, data.mHeaders, 132 data.mMimeType, data.mPostId, false); 133 if (cache != null) { 134 mCacheResultMap.put(data.mListener, cache); 135 } else { 136 mCacheResultMap.remove(data.mListener); 137 } 138 break; 139 } 140 case MSG_UPDATE_CACHE_ENCODING: { 141 assert !JniUtil.useChromiumHttpStack(); 142 CacheEncoding data = (CacheEncoding) msg.obj; 143 CacheManager.CacheResult cache = mCacheResultMap 144 .get(data.mListener); 145 if (cache != null) { 146 cache.encoding = data.mEncoding; 147 } 148 break; 149 } 150 case MSG_APPEND_CACHE: { 151 assert !JniUtil.useChromiumHttpStack(); 152 CacheData data = (CacheData) msg.obj; 153 CacheManager.CacheResult cache = mCacheResultMap 154 .get(data.mListener); 155 if (cache != null) { 156 cache.contentLength += data.mChunk.mLength; 157 if (cache.contentLength > CacheManager.CACHE_MAX_SIZE) { 158 CacheManager.cleanupCacheFile(cache); 159 mCacheResultMap.remove(data.mListener); 160 } else { 161 try { 162 cache.outStream.write(data.mChunk.mArray, 0, 163 data.mChunk.mLength); 164 } catch (IOException e) { 165 CacheManager.cleanupCacheFile(cache); 166 mCacheResultMap.remove(data.mListener); 167 } 168 } 169 } 170 data.mChunk.release(); 171 break; 172 } 173 case MSG_SAVE_CACHE: { 174 assert !JniUtil.useChromiumHttpStack(); 175 CacheSaveData data = (CacheSaveData) msg.obj; 176 CacheManager.CacheResult cache = mCacheResultMap 177 .get(data.mListener); 178 if (cache != null) { 179 CacheManager.saveCacheFile(data.mUrl, data.mPostId, cache); 180 mCacheResultMap.remove(data.mListener); 181 } 182 break; 183 } 184 case MSG_REMOVE_CACHE: { 185 assert !JniUtil.useChromiumHttpStack(); 186 LoadListener listener = (LoadListener) msg.obj; 187 CacheManager.CacheResult cache = mCacheResultMap.get(listener); 188 if (cache != null) { 189 CacheManager.cleanupCacheFile(cache); 190 mCacheResultMap.remove(listener); 191 } 192 break; 193 } 194 case MSG_TRIM_CACHE: { 195 assert !JniUtil.useChromiumHttpStack(); 196 CacheManager.trimCacheIfNeeded(); 197 break; 198 } 199 case MSG_CLEAR_CACHE: { 200 assert !JniUtil.useChromiumHttpStack(); 201 CacheManager.clearCache(); 202 break; 203 } 204 case MSG_CACHE_TRANSACTION_TICKER: { 205 assert !JniUtil.useChromiumHttpStack(); 206 if (!mCacheTickersBlocked) { 207 CacheManager.endTransaction(); 208 CacheManager.startTransaction(); 209 sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER, 210 CACHE_TRANSACTION_TICKER_INTERVAL); 211 } 212 break; 213 } 214 case MSG_PAUSE_CACHE_TRANSACTION: { 215 assert !JniUtil.useChromiumHttpStack(); 216 if (CacheManager.disableTransaction()) { 217 mCacheTickersBlocked = true; 218 removeMessages(MSG_CACHE_TRANSACTION_TICKER); 219 } 220 break; 221 } 222 case MSG_RESUME_CACHE_TRANSACTION: { 223 assert !JniUtil.useChromiumHttpStack(); 224 if (CacheManager.enableTransaction()) { 225 mCacheTickersBlocked = false; 226 sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER, 227 CACHE_TRANSACTION_TICKER_INTERVAL); 228 } 229 break; 230 } 231 } 232 } 233 } 234