1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/history/android/sqlite_cursor.h" 6 7 #include "base/android/jni_android.h" 8 #include "base/android/jni_array.h" 9 #include "base/android/jni_string.h" 10 #include "base/bind.h" 11 #include "base/logging.h" 12 #include "chrome/browser/history/android/android_history_types.h" 13 #include "content/public/browser/browser_thread.h" 14 #include "jni/SQLiteCursor_jni.h" 15 #include "sql/statement.h" 16 17 using base::android::ConvertUTF8ToJavaString; 18 using base::android::ScopedJavaLocalRef; 19 using content::BrowserThread; 20 21 namespace { 22 23 SQLiteCursor::JavaColumnType ToJavaColumnType(sql::ColType type) { 24 switch (type) { 25 case sql::COLUMN_TYPE_INTEGER: 26 return SQLiteCursor::NUMERIC; 27 case sql::COLUMN_TYPE_FLOAT: 28 return SQLiteCursor::DOUBLE; 29 case sql::COLUMN_TYPE_TEXT: 30 return SQLiteCursor::LONG_VAR_CHAR; 31 case sql::COLUMN_TYPE_BLOB: 32 return SQLiteCursor::BLOB; 33 case sql::COLUMN_TYPE_NULL: 34 return SQLiteCursor::NULL_TYPE; 35 default: 36 NOTREACHED(); 37 } 38 return SQLiteCursor::NULL_TYPE; 39 } 40 41 } // namespace. 42 43 44 SQLiteCursor::TestObserver::TestObserver() { 45 } 46 47 SQLiteCursor::TestObserver::~TestObserver() { 48 } 49 50 ScopedJavaLocalRef<jobject> SQLiteCursor::NewJavaSqliteCursor( 51 JNIEnv* env, 52 const std::vector<std::string>& column_names, 53 history::AndroidStatement* statement, 54 AndroidHistoryProviderService* service, 55 FaviconService* favicon_service) { 56 SQLiteCursor* cursor = new SQLiteCursor(column_names, statement, service, 57 favicon_service); 58 return Java_SQLiteCursor_create(env, reinterpret_cast<intptr_t>(cursor)); 59 } 60 61 bool SQLiteCursor::RegisterSqliteCursor(JNIEnv* env) { 62 return RegisterNativesImpl(env); 63 } 64 65 jint SQLiteCursor::GetCount(JNIEnv* env, jobject obj) { 66 // Moves to maxium possible position so we will reach the last row, then finds 67 // out the total number of rows. 68 int current_position = position_; 69 int count = MoveTo(env, obj, std::numeric_limits<int>::max() - 1) + 1; 70 // Moves back to the previous position. 71 MoveTo(env, obj, current_position); 72 return count; 73 } 74 75 ScopedJavaLocalRef<jobjectArray> SQLiteCursor::GetColumnNames(JNIEnv* env, 76 jobject obj) { 77 return base::android::ToJavaArrayOfStrings(env, column_names_); 78 } 79 80 ScopedJavaLocalRef<jstring> SQLiteCursor::GetString(JNIEnv* env, 81 jobject obj, 82 jint column) { 83 base::string16 value = statement_->statement()->ColumnString16(column); 84 return ScopedJavaLocalRef<jstring>(env, 85 env->NewString(value.data(), value.size())); 86 } 87 88 jlong SQLiteCursor::GetLong(JNIEnv* env, jobject obj, jint column) { 89 return statement_->statement()->ColumnInt64(column); 90 } 91 92 jint SQLiteCursor::GetInt(JNIEnv* env, jobject obj, jint column) { 93 return statement_->statement()->ColumnInt(column); 94 } 95 96 jdouble SQLiteCursor::GetDouble(JNIEnv* env, jobject obj, jint column) { 97 return statement_->statement()->ColumnDouble(column); 98 } 99 100 ScopedJavaLocalRef<jbyteArray> SQLiteCursor::GetBlob(JNIEnv* env, 101 jobject obj, 102 jint column) { 103 std::vector<unsigned char> blob; 104 105 // Assume the client will only get favicon using GetBlob. 106 if (statement_->favicon_index() == column) { 107 if (!GetFavicon(statement_->statement()->ColumnInt(column), &blob)) 108 return ScopedJavaLocalRef<jbyteArray>(); 109 } else { 110 statement_->statement()->ColumnBlobAsVector(column, &blob); 111 } 112 return base::android::ToJavaByteArray(env, &blob[0], blob.size()); 113 } 114 115 jboolean SQLiteCursor::IsNull(JNIEnv* env, jobject obj, jint column) { 116 return NULL_TYPE == GetColumnTypeInternal(column) ? JNI_TRUE : JNI_FALSE; 117 } 118 119 jint SQLiteCursor::MoveTo(JNIEnv* env, jobject obj, jint pos) { 120 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 121 base::Bind(&SQLiteCursor::RunMoveStatementOnUIThread, 122 base::Unretained(this), pos)); 123 if (test_observer_) 124 test_observer_->OnPostMoveToTask(); 125 126 event_.Wait(); 127 return position_; 128 } 129 130 jint SQLiteCursor::GetColumnType(JNIEnv* env, jobject obj, jint column) { 131 return GetColumnTypeInternal(column); 132 } 133 134 void SQLiteCursor::Destroy(JNIEnv* env, jobject obj) { 135 // We do our best to cleanup when Destroy() is called from Java's finalize() 136 // where the UI message loop might stop running or in the process of shutting 137 // down, as the whole process will be destroyed soon, it's fine to leave some 138 // objects out there. 139 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { 140 DestroyOnUIThread(); 141 } else if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 142 base::Bind(&SQLiteCursor::DestroyOnUIThread, 143 base::Unretained(this)))) { 144 delete this; 145 } 146 } 147 148 SQLiteCursor::SQLiteCursor(const std::vector<std::string>& column_names, 149 history::AndroidStatement* statement, 150 AndroidHistoryProviderService* service, 151 FaviconService* favicon_service) 152 : position_(-1), 153 event_(false, false), 154 statement_(statement), 155 column_names_(column_names), 156 service_(service), 157 favicon_service_(favicon_service), 158 count_(-1), 159 test_observer_(NULL) { 160 } 161 162 SQLiteCursor::~SQLiteCursor() { 163 } 164 165 void SQLiteCursor::DestroyOnUIThread() { 166 // Consumer requests were set in the UI thread. They must be cancelled 167 // using the same thread. 168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 169 consumer_.reset(); 170 tracker_.reset(); 171 service_->CloseStatement(statement_); 172 delete this; 173 } 174 175 bool SQLiteCursor::GetFavicon(chrome::FaviconID id, 176 std::vector<unsigned char>* image_data) { 177 if (id) { 178 BrowserThread::PostTask( 179 BrowserThread::UI, 180 FROM_HERE, 181 base::Bind(&SQLiteCursor::GetFaviconForIDInUIThread, 182 base::Unretained(this), id, 183 base::Bind(&SQLiteCursor::OnFaviconData, 184 base::Unretained(this)))); 185 186 if (test_observer_) 187 test_observer_->OnPostGetFaviconTask(); 188 189 event_.Wait(); 190 if (!favicon_bitmap_result_.is_valid()) 191 return false; 192 193 scoped_refptr<base::RefCountedMemory> bitmap_data = 194 favicon_bitmap_result_.bitmap_data; 195 image_data->assign(bitmap_data->front(), 196 bitmap_data->front() + bitmap_data->size()); 197 return true; 198 } 199 200 return false; 201 } 202 203 void SQLiteCursor::GetFaviconForIDInUIThread( 204 chrome::FaviconID id, 205 const FaviconService::FaviconRawCallback& callback) { 206 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 207 if (!tracker_.get()) 208 tracker_.reset(new CancelableTaskTracker()); 209 favicon_service_->GetLargestRawFaviconForID(id, callback, tracker_.get()); 210 } 211 212 213 void SQLiteCursor::OnFaviconData( 214 const chrome::FaviconBitmapResult& bitmap_result) { 215 favicon_bitmap_result_ = bitmap_result; 216 event_.Signal(); 217 if (test_observer_) 218 test_observer_->OnGetFaviconResult(); 219 } 220 221 void SQLiteCursor::OnMoved(AndroidHistoryProviderService::Handle handle, 222 int pos) { 223 position_ = pos; 224 event_.Signal(); 225 if (test_observer_) 226 // Notified test_observer on UI thread instead of the one it will wait. 227 test_observer_->OnGetMoveToResult(); 228 } 229 230 SQLiteCursor::JavaColumnType SQLiteCursor::GetColumnTypeInternal(int column) { 231 if (column == statement_->favicon_index()) 232 return SQLiteCursor::BLOB; 233 234 return ToJavaColumnType(statement_->statement()->ColumnType(column)); 235 } 236 237 void SQLiteCursor::RunMoveStatementOnUIThread(int pos) { 238 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 239 if (!consumer_.get()) 240 consumer_.reset(new CancelableRequestConsumer()); 241 service_->MoveStatement( 242 statement_, position_, pos, consumer_.get(), 243 base::Bind(&SQLiteCursor::OnMoved, base::Unretained(this))); 244 } 245