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